Title: [198683] trunk
Revision
198683
Author
[email protected]
Date
2016-03-25 12:25:05 -0700 (Fri, 25 Mar 2016)

Log Message

Implement the allow-end value of the hanging-punctuation CSS property.
https://bugs.webkit.org/show_bug.cgi?id=104996

Reviewed by Simon Fraser.

Source/WebCore:

Added new tests in fast/text.

* rendering/RenderBlockLineLayout.cpp:
(WebCore::RenderBlockFlow::constructLine):
Fix a bug where empty RenderInlines were incorrectly excluding their end borders if
they occurred at the end of a line. Needed to adequately test allow-end and empty
inline borders.

* rendering/RenderText.cpp:
(WebCore::RenderText::isHangableStopOrComma):
Helper function that identifies the hangable stops and commas.

* rendering/RenderText.h:
Add new isHangableStopOrComma function to RenderText.

* rendering/line/BreakingContext.h:
(WebCore::BreakingContext::lineBreak):
(WebCore::BreakingContext::lineWidth):
(WebCore::BreakingContext::atEnd):
(WebCore::BreakingContext::fitsOnLineOrHangsAtEnd):
(WebCore::BreakingContext::clearLineBreakIfFitsOnLine):
(WebCore::BreakingContext::commitLineBreakAtCurrentWidth):
(WebCore::BreakingContext::handleBR):
(WebCore::BreakingContext::handleEmptyInline):
(WebCore::BreakingContext::handleReplaced):
(WebCore::tryHyphenating):
(WebCore::BreakingContext::computeAdditionalBetweenWordsWidth):
(WebCore::BreakingContext::handleText):
(WebCore::BreakingContext::commitAndUpdateLineBreakIfNeeded):
Modified breaking rules to handle allow-end. The basic idea is to see if you can
fit without the comma and only hang if you do, and if nothing else gets added to the
line after the comma. This involves tracking a new state, m_hangsAtEnd, that can
be set/cleared while iterating over the objects that will end up on the line.

LayoutTests:

* fast/text/hanging-punctuation-allow-end-basic-expected.html: Added.
* fast/text/hanging-punctuation-allow-end-basic.html: Added.
* fast/text/hanging-punctuation-allow-end-expected.html: Added.
* fast/text/hanging-punctuation-allow-end-inlines-expected.html: Added.
* fast/text/hanging-punctuation-allow-end-inlines.html: Added.
* fast/text/hanging-punctuation-allow-end.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (198682 => 198683)


--- trunk/LayoutTests/ChangeLog	2016-03-25 18:44:38 UTC (rev 198682)
+++ trunk/LayoutTests/ChangeLog	2016-03-25 19:25:05 UTC (rev 198683)
@@ -1,3 +1,17 @@
+2016-03-23  Dave Hyatt  <[email protected]>
+
+        Implement the allow-end value of the hanging-punctuation CSS property.
+        https://bugs.webkit.org/show_bug.cgi?id=104996
+
+        Reviewed by Simon Fraser.
+
+        * fast/text/hanging-punctuation-allow-end-basic-expected.html: Added.
+        * fast/text/hanging-punctuation-allow-end-basic.html: Added.
+        * fast/text/hanging-punctuation-allow-end-expected.html: Added.
+        * fast/text/hanging-punctuation-allow-end-inlines-expected.html: Added.
+        * fast/text/hanging-punctuation-allow-end-inlines.html: Added.
+        * fast/text/hanging-punctuation-allow-end.html: Added.
+
 2016-03-25  Caitlin Potter  <[email protected]>
 
         [JSC] implement String.prototype.padStart() and String.prototype.padEnd() proposal

Added: trunk/LayoutTests/fast/text/hanging-punctuation-allow-end-basic-expected.html (0 => 198683)


--- trunk/LayoutTests/fast/text/hanging-punctuation-allow-end-basic-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/text/hanging-punctuation-allow-end-basic-expected.html	2016-03-25 19:25:05 UTC (rev 198683)
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="UTF-8">
+            <style>
+                .hang { white-space: pre; }
+                    .not-hang { white-space: pre; }
+                    .frame { width: 65px; font-family: ahem; border: solid 3px cyan; }
+            </style>
+        </head>
+    <body>
+        <!--
+          Test for checking overflowing conditions of hanging-punctuation: allow-end.
+          http://www.w3.org/TR/2012/WD-css3-text-20121113/#hanging-punctuation0
+          -->
+        <!-- Overflow occurred at U+3001. This should hang. -->
+        <div class="hang frame">ab c&#x3001;</div>
+        <!-- Overflow occurred at "d". This should NOT hang. -->
+        <div class="not-hang frame">ab
+cd&#x3001;</div>
+        <!-- Overflow occurred at first U+3001. This should NOT hang. -->
+        <div class="not-hang frame">ab
+c&#x3001;&#x3001;</div>
+        <!-- Overflow occurred at second U+3001. This should hang. -->
+        <div class="hang frame">a b&#x3001;&#x3001;</div>
+        <!-- Hanging with soft hyphen. This should hang. -->
+        <div class="hang frame">a b&shy;c&#x3001;</div>
+        <!-- Overflow occurred at U+3001 and there is a <br> after it. This should hang. -->
+        <div class="hang frame">ab c&#x3001;<br></div>
+    </body>
+</html>
\ No newline at end of file

Added: trunk/LayoutTests/fast/text/hanging-punctuation-allow-end-basic.html (0 => 198683)


--- trunk/LayoutTests/fast/text/hanging-punctuation-allow-end-basic.html	                        (rev 0)
+++ trunk/LayoutTests/fast/text/hanging-punctuation-allow-end-basic.html	2016-03-25 19:25:05 UTC (rev 198683)
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="UTF-8">
+            <style>
+                    .hang { hanging-punctuation: allow-end; }
+                    .not-hang { hanging-punctuation: allow-end; }
+                    .frame { width: 65px; font-family: ahem; border: solid 3px cyan; }
+            </style>
+        </head>
+    <body>
+        <!--
+          Test for checking overflowing conditions of hanging-punctuation: allow-end.
+          http://www.w3.org/TR/2012/WD-css3-text-20121113/#hanging-punctuation0
+          -->
+        <!-- Overflow occurred at U+3001. This should hang. -->
+        <div class="hang frame">
+            ab c&#x3001;
+        </div>
+        <!-- Overflow occurred at "d". This should NOT hang. -->
+        <div class="not-hang frame">
+            ab cd&#x3001;
+            </div>
+        <!-- Overflow occurred at first U+3001. This should NOT hang. -->
+        <div class="not-hang frame">
+            ab c&#x3001;&#x3001;
+            </div>
+        <!-- Overflow occurred at second U+3001. This should hang. -->
+        <div class="hang frame">
+            a b&#x3001;&#x3001;
+            </div>
+        <!-- Hanging with soft hyphen. This should hang. -->
+        <div class="hang frame">
+            a b&shy;c&#x3001;
+            </div>
+        <!-- Overflow occurred at U+3001 and there is a <br> after it. This should hang. -->
+        <div class="hang frame">
+            ab c&#x3001;<br>
+        </div>
+    </body>
+</html>
\ No newline at end of file

Added: trunk/LayoutTests/fast/text/hanging-punctuation-allow-end-expected.html (0 => 198683)


--- trunk/LayoutTests/fast/text/hanging-punctuation-allow-end-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/text/hanging-punctuation-allow-end-expected.html	2016-03-25 19:25:05 UTC (rev 198683)
@@ -0,0 +1,19 @@
+<head>
+<style>
+    body { font-family: 'Ahem'; color:green }
+    .hang { white-space:pre; margin:1em; width:5em; border:2px solid black }
+</style>
+</head>
+<body>
+<div style="float:left" class="hang">12 34,</div>
+<div style="clear:both">
+<div class="hang">12 34,
+1234,</div>
+<div class="hang" style="text-align:justify;">12 34,</div>
+<div class="hang">12 34,<br>12345</div>
+<div class="hang">12
+34,56
+1234<br>12345</div>
+<div class="hang">12
+34,56
+1234<br>12345</div>
\ No newline at end of file

Added: trunk/LayoutTests/fast/text/hanging-punctuation-allow-end-inlines-expected.html (0 => 198683)


--- trunk/LayoutTests/fast/text/hanging-punctuation-allow-end-inlines-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/text/hanging-punctuation-allow-end-inlines-expected.html	2016-03-25 19:25:05 UTC (rev 198683)
@@ -0,0 +1,23 @@
+<head>
+<style>
+    body { font-family: 'Ahem'; color:green }
+    .hang { white-space:pre; margin:1em; width:5em; border:2px solid black }
+</style>
+</head>
+<body>
+<div style="float:left; width:auto" class="hang">12 34,</div>
+<div style="clear:both">
+<div class="hang">12 34,<span></span>
+1234,</div>
+<div class="hang">12
+34,<span style="border-left:1em solid black"></span>
+1234,</div>
+<div class="hang">12
+34,<span style="border-right:1em solid black"></span>
+1234,</div>
+<div class="hang"><span style="border-right:1em solid black">12
+34,</span>
+2345</div>
+<div class="hang">12
+34<span style="border-left:1em solid black">,</span>
+2345</div>

Added: trunk/LayoutTests/fast/text/hanging-punctuation-allow-end-inlines.html (0 => 198683)


--- trunk/LayoutTests/fast/text/hanging-punctuation-allow-end-inlines.html	                        (rev 0)
+++ trunk/LayoutTests/fast/text/hanging-punctuation-allow-end-inlines.html	2016-03-25 19:25:05 UTC (rev 198683)
@@ -0,0 +1,14 @@
+<head>
+<style>
+    body { font-family: 'Ahem'; color:green }
+    .hang { hanging-punctuation: allow-end; margin:1em; width:5em; border:2px solid black }
+</style>
+</head>
+<body>
+<div style="float:left; width:auto" class="hang">12 34,</div>
+<div style="clear:both">
+<div class="hang">12 34,<span></span> 1234,</div>
+<div class="hang">12 34,<span style="border-left:1em solid black"></span> 1234,</div>
+<div class="hang">12 34,<span style="border-right:1em solid black"></span> 1234,</div>
+<div class="hang"><span style="border-right:1em solid black">12 34,</span> 2345</div>
+<div class="hang">12 34<span style="border-left:1em solid black">,</span> 2345</div>

Added: trunk/LayoutTests/fast/text/hanging-punctuation-allow-end.html (0 => 198683)


--- trunk/LayoutTests/fast/text/hanging-punctuation-allow-end.html	                        (rev 0)
+++ trunk/LayoutTests/fast/text/hanging-punctuation-allow-end.html	2016-03-25 19:25:05 UTC (rev 198683)
@@ -0,0 +1,16 @@
+<head>
+<style>
+    body { font-family: 'Ahem'; color:green }
+    .hang { hanging-punctuation: allow-end; margin:1em; width:5em; border:2px solid black }
+</style>
+</head>
+<body>
+<div style="float:left" class="hang">12 34,</div>
+<div style="clear:both">
+<div class="hang">12 34, 1234,</div>
+<div class="hang" style="text-align:justify;">12 34,</div>
+<div class="hang">12 34,<br>12345</div>
+<div class="hang">12 34,56 1234<br>12345</div>
+<div class="hang">12 <span style="white-space:nowrap">34,<div style="display:inline-block">56</div></span> 1234<br>12345</div>
+
+

Modified: trunk/Source/WebCore/ChangeLog (198682 => 198683)


--- trunk/Source/WebCore/ChangeLog	2016-03-25 18:44:38 UTC (rev 198682)
+++ trunk/Source/WebCore/ChangeLog	2016-03-25 19:25:05 UTC (rev 198683)
@@ -1,3 +1,44 @@
+2016-03-23  Dave Hyatt  <[email protected]>
+
+        Implement the allow-end value of the hanging-punctuation CSS property.
+        https://bugs.webkit.org/show_bug.cgi?id=104996
+
+        Reviewed by Simon Fraser.
+
+        Added new tests in fast/text.
+
+        * rendering/RenderBlockLineLayout.cpp:
+        (WebCore::RenderBlockFlow::constructLine):
+        Fix a bug where empty RenderInlines were incorrectly excluding their end borders if
+        they occurred at the end of a line. Needed to adequately test allow-end and empty
+        inline borders.
+
+        * rendering/RenderText.cpp:
+        (WebCore::RenderText::isHangableStopOrComma):
+        Helper function that identifies the hangable stops and commas.
+
+        * rendering/RenderText.h:
+        Add new isHangableStopOrComma function to RenderText.
+
+        * rendering/line/BreakingContext.h:
+        (WebCore::BreakingContext::lineBreak):
+        (WebCore::BreakingContext::lineWidth):
+        (WebCore::BreakingContext::atEnd):
+        (WebCore::BreakingContext::fitsOnLineOrHangsAtEnd):
+        (WebCore::BreakingContext::clearLineBreakIfFitsOnLine):
+        (WebCore::BreakingContext::commitLineBreakAtCurrentWidth):
+        (WebCore::BreakingContext::handleBR):
+        (WebCore::BreakingContext::handleEmptyInline):
+        (WebCore::BreakingContext::handleReplaced):
+        (WebCore::tryHyphenating):
+        (WebCore::BreakingContext::computeAdditionalBetweenWordsWidth):
+        (WebCore::BreakingContext::handleText):
+        (WebCore::BreakingContext::commitAndUpdateLineBreakIfNeeded):
+        Modified breaking rules to handle allow-end. The basic idea is to see if you can
+        fit without the comma and only hang if you do, and if nothing else gets added to the
+        line after the comma. This involves tracking a new state, m_hangsAtEnd, that can
+        be set/cleared while iterating over the objects that will end up on the line.
+
 2016-03-25  Alex Christensen  <[email protected]>
 
         Revert most of r198673.

Modified: trunk/Source/WebCore/rendering/RenderBlockLineLayout.cpp (198682 => 198683)


--- trunk/Source/WebCore/rendering/RenderBlockLineLayout.cpp	2016-03-25 18:44:38 UTC (rev 198682)
+++ trunk/Source/WebCore/rendering/RenderBlockLineLayout.cpp	2016-03-25 19:25:05 UTC (rev 198683)
@@ -337,7 +337,7 @@
     // paint borders/margins/padding.  This knowledge will ultimately be used when
     // we determine the horizontal positions and widths of all the inline boxes on
     // the line.
-    bool isLogicallyLastRunWrapped = bidiRuns.logicallyLastRun()->renderer().isText() ? !reachedEndOfTextRenderer(bidiRuns) : true;
+    bool isLogicallyLastRunWrapped = bidiRuns.logicallyLastRun()->renderer().isText() ? !reachedEndOfTextRenderer(bidiRuns) : !is<RenderInline>(bidiRuns.logicallyLastRun()->renderer());
     lastRootBox()->determineSpacingForFlowBoxes(lineInfo.isLastLine(), isLogicallyLastRunWrapped, &bidiRuns.logicallyLastRun()->renderer());
 
     // Now mark the line boxes as being constructed.

Modified: trunk/Source/WebCore/rendering/RenderText.cpp (198682 => 198683)


--- trunk/Source/WebCore/rendering/RenderText.cpp	2016-03-25 18:44:38 UTC (rev 198682)
+++ trunk/Source/WebCore/rendering/RenderText.cpp	2016-03-25 19:25:05 UTC (rev 198683)
@@ -546,6 +546,13 @@
     return widthFromCache(font, 0, 1, 0, 0, 0, style);
 }
 
+bool RenderText::isHangableStopOrComma(UChar c) const
+{
+    return c == 0x002C || c == 0x002E || c == 0x060C || c == 0x06D4 || c == 0x3001
+        || c == 0x3002 || c == 0xFF0C || c == 0xFF0E || c == 0xFE50 || c == 0xFE51
+        || c == 0xFE52 || c == 0xFF61 || c == 0xFF64;
+}
+
 unsigned RenderText::firstCharacterIndexStrippingSpaces() const
 {
     if (!style().collapseWhiteSpace())

Modified: trunk/Source/WebCore/rendering/RenderText.h (198682 => 198683)


--- trunk/Source/WebCore/rendering/RenderText.h	2016-03-25 18:44:38 UTC (rev 198682)
+++ trunk/Source/WebCore/rendering/RenderText.h	2016-03-25 19:25:05 UTC (rev 198683)
@@ -106,7 +106,8 @@
     float hangablePunctuationEndWidth(unsigned index) const;
     unsigned firstCharacterIndexStrippingSpaces() const;
     unsigned lastCharacterIndexStrippingSpaces() const;
-
+    bool isHangableStopOrComma(UChar) const;
+    
     WEBCORE_EXPORT virtual IntRect linesBoundingBox() const;
     LayoutRect linesVisualOverflowBoundingBox() const;
 

Modified: trunk/Source/WebCore/rendering/line/BreakingContext.h (198682 => 198683)


--- trunk/Source/WebCore/rendering/line/BreakingContext.h	2016-03-25 18:44:38 UTC (rev 198682)
+++ trunk/Source/WebCore/rendering/line/BreakingContext.h	2016-03-25 19:25:05 UTC (rev 198683)
@@ -138,6 +138,8 @@
     InlineIterator lineBreak() { return m_lineBreakHistory.current(); }
     LineWidth& lineWidth() { return m_width; }
     bool atEnd() { return m_atEnd; }
+    
+    bool fitsOnLineOrHangsAtEnd() const { return m_width.fitsOnLine() || m_hangsAtEnd; }
 
     void initializeForCurrentObject();
 
@@ -155,17 +157,21 @@
 #if ENABLE(CSS_TRAILING_WORD)
     InlineIterator optimalLineBreakLocationForTrailingWord();
 #endif
+    
+    float computeAdditionalBetweenWordsWidth(RenderText&, TextLayout*, UChar, WordTrailingSpace&, HashSet<const Font*>& fallbackFonts, WordMeasurements&, const FontCascade&, bool isFixedPitch, unsigned lastSpace, float lastSpaceWordSpacing, float wordSpacingForWordMeasurement, unsigned offset);
 
     void clearLineBreakIfFitsOnLine(bool ignoringTrailingSpace = false)
     {
-        if (m_width.fitsOnLine(ignoringTrailingSpace) || m_lastWS == NOWRAP)
+        if (m_width.fitsOnLine(ignoringTrailingSpace) || m_lastWS == NOWRAP || m_hangsAtEnd)
             m_lineBreakHistory.clear();
+        m_hangsAtEnd = false;
     }
 
     void commitLineBreakAtCurrentWidth(RenderObject& object, unsigned offset = 0, int nextBreak = -1)
     {
         m_width.commit();
         m_lineBreakHistory.moveTo(&object, offset, nextBreak);
+        m_hangsAtEnd = false;
     }
 
 private:
@@ -283,6 +289,8 @@
     bool m_allowImagesToBreak;
     bool m_atEnd;
     bool m_hadUncommittedWidthBeforeCurrent;
+    
+    bool m_hangsAtEnd { false };
 
     LineMidpointState& m_lineMidpointState;
 
@@ -325,7 +333,7 @@
 
 inline void BreakingContext::handleBR(EClear& clear)
 {
-    if (m_width.fitsOnLine()) {
+    if (fitsOnLineOrHangsAtEnd()) {
         RenderObject* br = m_current.renderer();
         m_lineBreakHistory.push([&](InlineIterator& modifyMe) {
             modifyMe.moveToStartOf(br);
@@ -509,8 +517,11 @@
         } else
             m_trailingObjects.appendBoxIfNeeded(&flowBox);
     }
-
-    m_width.addUncommittedWidth(inlineLogicalWidth(m_current.renderer()) + borderPaddingMarginStart(flowBox) + borderPaddingMarginEnd(flowBox));
+    
+    float inlineWidth = inlineLogicalWidth(m_current.renderer()) + borderPaddingMarginStart(flowBox) + borderPaddingMarginEnd(flowBox);
+    m_width.addUncommittedWidth(inlineWidth);
+    if (m_hangsAtEnd && inlineWidth)
+        m_hangsAtEnd = false;
 }
 
 inline void BreakingContext::handleReplaced()
@@ -530,7 +541,8 @@
             m_atEnd = true;
             return;
         }
-    }
+    } else
+        m_hangsAtEnd = false;
     
     if (replacedBox.isAnonymousInlineBlock())
         m_block.layoutBlockChild(replacedBox, m_lineLayoutState.marginInfo(),
@@ -706,6 +718,33 @@
     hyphenated = true;
 }
 
+inline float BreakingContext::computeAdditionalBetweenWordsWidth(RenderText& renderText, TextLayout* textLayout, UChar currentCharacter, WordTrailingSpace& wordTrailingSpace, HashSet<const Font*>& fallbackFonts, WordMeasurements& wordMeasurements, const FontCascade& font, bool isFixedPitch, unsigned lastSpace, float lastSpaceWordSpacing, float wordSpacingForWordMeasurement, unsigned offset)
+{
+    wordMeasurements.grow(wordMeasurements.size() + 1);
+    WordMeasurement& wordMeasurement = wordMeasurements.last();
+    
+    wordMeasurement.renderer = &renderText;
+    wordMeasurement.endOffset = offset;
+    wordMeasurement.startOffset = lastSpace;
+    
+    float additionalTempWidth = 0;
+    WTF::Optional<float> wordTrailingSpaceWidth;
+    if (currentCharacter == ' ')
+        wordTrailingSpaceWidth = wordTrailingSpace.width(fallbackFonts);
+    if (wordTrailingSpaceWidth)
+        additionalTempWidth = textWidth(renderText, lastSpace, offset + 1 - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, wordMeasurement.fallbackFonts, textLayout) - wordTrailingSpaceWidth.value();
+    else
+        additionalTempWidth = textWidth(renderText, lastSpace, offset - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, wordMeasurement.fallbackFonts, textLayout);
+    
+    if (wordMeasurement.fallbackFonts.isEmpty() && !fallbackFonts.isEmpty())
+        wordMeasurement.fallbackFonts.swap(fallbackFonts);
+    fallbackFonts.clear();
+    
+    wordMeasurement.width = additionalTempWidth + wordSpacingForWordMeasurement;
+    additionalTempWidth += lastSpaceWordSpacing;
+    return additionalTempWidth;
+}
+
 inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool& hyphenated,  unsigned& consecutiveHyphenatedLines)
 {
     if (!m_current.offset())
@@ -737,6 +776,7 @@
     bool canHyphenate = style.hyphens() == HyphensAuto && WebCore::canHyphenate(style.locale());
     bool canHangPunctuationAtStart = style.hangingPunctuation() & FirstHangingPunctuation;
     bool canHangPunctuationAtEnd = style.hangingPunctuation() & LastHangingPunctuation;
+    bool canHangStopOrCommaAtLineEnd = style.hangingPunctuation() & AllowEndHangingPunctuation;
     int endPunctuationIndex = canHangPunctuationAtEnd && m_collapseWhiteSpace ? renderText.lastCharacterIndexStrippingSpaces() : renderText.textLength() - 1;
     unsigned lastSpace = m_current.offset();
     float wordSpacing = m_currentStyle->fontCascade().wordSpacing();
@@ -794,7 +834,7 @@
         
         if (canHangPunctuationAtEnd && !m_nextObject && (int)m_current.offset() == endPunctuationIndex && !inlineLogicalWidth(m_current.renderer(), false, true)) {
             m_width.addUncommittedWidth(-renderText.hangablePunctuationEndWidth(endPunctuationIndex));
-            canHangPunctuationAtStart = false;
+            canHangPunctuationAtEnd = false;
         }
 
         if (!m_collapseWhiteSpace || !m_currentCharacterIsSpace)
@@ -820,14 +860,30 @@
         bool betweenWords = c == '\n' || (m_currWS != PRE && !m_atStart && isBreakable(m_renderTextInfo.lineBreakIterator, m_current.offset(), nextBreakablePosition, breakNBSP, isLooseCJKMode, keepAllWords)
             && (style.hyphens() != HyphensNone || (m_current.previousInSameNode() != softHyphen)));
         m_current.setNextBreakablePosition(nextBreakablePosition);
-
+        
+        if (canHangStopOrCommaAtLineEnd && renderText.isHangableStopOrComma(c) && m_width.fitsOnLine()) {
+            // We need to see if a measurement that excludes the stop would fit. If so, then we should hang
+            // the stop/comma at the end. First measure including the comma.
+            m_hangsAtEnd = false;
+            float inlineStartWidth = !m_appliedStartWidth ? inlineLogicalWidth(m_current.renderer(), true, false) : LayoutUnit();
+            float widthIncludingComma = computeAdditionalBetweenWordsWidth(renderText, textLayout, c, wordTrailingSpace, fallbackFonts, wordMeasurements, font, isFixedPitch, lastSpace, lastSpaceWordSpacing, wordSpacingForWordMeasurement, m_current.offset() + 1) + inlineStartWidth;
+            m_width.addUncommittedWidth(widthIncludingComma);
+            if (!m_width.fitsOnLine()) {
+                // See if we fit without the comma involved. If we do, then this is a potential hang point.
+                float widthWithoutStopOrComma = computeAdditionalBetweenWordsWidth(renderText, textLayout, lastCharacter, wordTrailingSpace, fallbackFonts, wordMeasurements, font, isFixedPitch, lastSpace, lastSpaceWordSpacing, wordSpacingForWordMeasurement, m_current.offset()) + inlineStartWidth;
+                m_width.addUncommittedWidth(widthWithoutStopOrComma - widthIncludingComma);
+                if (m_width.fitsOnLine())
+                    m_hangsAtEnd = true;
+            } else
+                m_width.addUncommittedWidth(-widthIncludingComma);
+        }
+        
         if (betweenWords || midWordBreak) {
             bool stoppedIgnoringSpaces = false;
             if (m_ignoringSpaces) {
                 lastSpaceWordSpacing = 0;
                 if (!m_currentCharacterIsSpace) {
-                    // Stop ignoring spaces and begin at this
-                    // new point.
+                    // Stop ignoring spaces and begin at this new point.
                     m_ignoringSpaces = false;
                     wordSpacingForWordMeasurement = 0;
                     lastSpace = m_current.offset(); // e.g., "Foo    goo", don't add in any of the ignored spaces.
@@ -839,51 +895,33 @@
                     continue;
                 }
             }
-
-            wordMeasurements.grow(wordMeasurements.size() + 1);
+            
+            float additionalTempWidth = computeAdditionalBetweenWordsWidth(renderText, textLayout, c, wordTrailingSpace, fallbackFonts, wordMeasurements, font, isFixedPitch, lastSpace, lastSpaceWordSpacing, wordSpacingForWordMeasurement, m_current.offset());
+            m_width.addUncommittedWidth(additionalTempWidth);
+            
             WordMeasurement& wordMeasurement = wordMeasurements.last();
 
-            wordMeasurement.renderer = &renderText;
-            wordMeasurement.endOffset = m_current.offset();
-            wordMeasurement.startOffset = lastSpace;
-
-            float additionalTempWidth;
-            WTF::Optional<float> wordTrailingSpaceWidth;
-            if (c == ' ')
-                wordTrailingSpaceWidth = wordTrailingSpace.width(fallbackFonts);
-            if (wordTrailingSpaceWidth) {
-                additionalTempWidth = textWidth(renderText, lastSpace, m_current.offset() + 1 - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace,
-                    wordMeasurement.fallbackFonts, textLayout) - wordTrailingSpaceWidth.value();
-            }
-            else
-                additionalTempWidth = textWidth(renderText, lastSpace, m_current.offset() - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, wordMeasurement.fallbackFonts, textLayout);
-
-            if (wordMeasurement.fallbackFonts.isEmpty() && !fallbackFonts.isEmpty())
-                wordMeasurement.fallbackFonts.swap(fallbackFonts);
-            fallbackFonts.clear();
-
-            wordMeasurement.width = additionalTempWidth + wordSpacingForWordMeasurement;
-            additionalTempWidth += lastSpaceWordSpacing;
-            m_width.addUncommittedWidth(additionalTempWidth);
-
             if (m_collapseWhiteSpace && previousCharacterIsSpace && m_currentCharacterIsSpace && additionalTempWidth)
                 m_width.setTrailingWhitespaceWidth(additionalTempWidth);
 
             if (!m_appliedStartWidth) {
-                m_width.addUncommittedWidth(inlineLogicalWidth(m_current.renderer(), true, false));
+                float inlineStartWidth = inlineLogicalWidth(m_current.renderer(), true, false);
+                m_width.addUncommittedWidth(inlineStartWidth);
                 m_appliedStartWidth = true;
+                if (m_hangsAtEnd && inlineStartWidth)
+                    m_hangsAtEnd = false;
             }
 
             applyWordSpacing = wordSpacing && m_currentCharacterIsSpace;
 
-            if (!m_width.hasCommitted() && m_autoWrap && !m_width.fitsOnLine())
+            if (!m_width.hasCommitted() && m_autoWrap && !fitsOnLineOrHangsAtEnd())
                 m_width.fitBelowFloats(m_lineInfo.isFirstLine());
 
             if (m_autoWrap || breakWords) {
                 // If we break only after white-space, consider the current character
                 // as candidate width for this line.
                 bool lineWasTooWide = false;
-                if (m_width.fitsOnLine() && m_currentCharacterIsWS && m_currentStyle->breakOnlyAfterWhiteSpace() && !midWordBreak) {
+                if (fitsOnLineOrHangsAtEnd() && m_currentCharacterIsWS && m_currentStyle->breakOnlyAfterWhiteSpace() && !midWordBreak) {
                     float charWidth = textWidth(renderText, m_current.offset(), 1, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, wordMeasurement.fallbackFonts, textLayout) + (applyWordSpacing ? wordSpacing : 0);
                     // Check if line is too big even without the extra space
                     // at the end of the line. If it is not, do nothing.
@@ -898,7 +936,7 @@
                         });
                     }
                 }
-                if (lineWasTooWide || !m_width.fitsOnLine()) {
+                if ((lineWasTooWide || !m_width.fitsOnLine()) && !m_hangsAtEnd) {
                     if (canHyphenate && !m_width.fitsOnLine()) {
                         m_lineBreakHistory.push([&](InlineIterator& modifyMe) {
                             tryHyphenating(renderText, font, style.locale(), consecutiveHyphenatedLines, m_blockStyle.hyphenationLimitLines(), style.hyphenationLimitBefore(), style.hyphenationLimitAfter(), lastSpace, m_current.offset(), m_width.currentWidth() - additionalTempWidth, m_width.availableWidth(), isFixedPitch, m_collapseWhiteSpace, lastSpaceWordSpacing, modifyMe, m_current.nextBreakablePosition(), m_lineBreaker.m_hyphenated);
@@ -1007,14 +1045,17 @@
                     m_trailingObjects.updateMidpointsForTrailingBoxes(m_lineMidpointState, InlineIterator(), TrailingObjects::DoNotCollapseFirstSpace);
                 }
             }
-        } else if (m_ignoringSpaces) {
-            // Stop ignoring spaces and begin at this
-            // new point.
-            m_ignoringSpaces = false;
-            lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
-            wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurements.last().width) ? wordSpacing : 0;
-            lastSpace = m_current.offset(); // e.g., "Foo    goo", don't add in any of the ignored spaces.
-            m_lineMidpointState.stopIgnoringSpaces(InlineIterator(nullptr, m_current.renderer(), m_current.offset()));
+        } else {
+            if (m_ignoringSpaces) {
+                // Stop ignoring spaces and begin at this new point.
+                m_ignoringSpaces = false;
+                lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
+                wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurements.last().width) ? wordSpacing : 0;
+                lastSpace = m_current.offset(); // e.g., "Foo    goo", don't add in any of the ignored spaces.
+                m_lineMidpointState.stopIgnoringSpaces(InlineIterator(nullptr, m_current.renderer(), m_current.offset()));
+            }
+            if (m_hangsAtEnd && !renderText.isHangableStopOrComma(c))
+                m_hangsAtEnd = false;
         }
 
         if (isSVGText && m_current.offset()) {
@@ -1066,6 +1107,8 @@
 
     float inlineLogicalTempWidth = inlineLogicalWidth(m_current.renderer(), !m_appliedStartWidth, m_includeEndWidth);
     m_width.addUncommittedWidth(additionalTempWidth + inlineLogicalTempWidth);
+    if (m_hangsAtEnd && inlineLogicalTempWidth)
+        m_hangsAtEnd = false;
 
     if (wordMeasurement.fallbackFonts.isEmpty() && !fallbackFonts.isEmpty())
         wordMeasurement.fallbackFonts.swap(fallbackFonts);
@@ -1076,7 +1119,7 @@
 
     m_includeEndWidth = false;
 
-    if (!m_width.fitsOnLine()) {
+    if (!fitsOnLineOrHangsAtEnd()) {
         if (canHyphenate) {
             m_lineBreakHistory.push([&](InlineIterator& modifyMe) {
                 tryHyphenating(renderText, font, style.locale(), consecutiveHyphenatedLines, m_blockStyle.hyphenationLimitLines(), style.hyphenationLimitBefore(), style.hyphenationLimitAfter(), lastSpace, m_current.offset(), m_width.currentWidth() - additionalTempWidth, m_width.availableWidth(), isFixedPitch, m_collapseWhiteSpace, lastSpaceWordSpacing, modifyMe, m_current.nextBreakablePosition(), m_lineBreaker.m_hyphenated);
@@ -1144,7 +1187,7 @@
 {
     bool checkForBreak = canBreakAtThisPosition();
 
-    if (checkForBreak && !m_width.fitsOnLine(m_ignoringSpaces)) {
+    if (checkForBreak && !m_width.fitsOnLine(m_ignoringSpaces) && !m_hangsAtEnd) {
         // if we have floats, try to get below them.
         if (m_currentCharacterIsSpace && !m_ignoringSpaces && m_currentStyle->collapseWhiteSpace())
             m_trailingObjects.clear();
@@ -1154,7 +1197,8 @@
             return;
         }
 
-        m_width.fitBelowFloats(m_lineInfo.isFirstLine());
+        if (!m_hangsAtEnd)
+            m_width.fitBelowFloats(m_lineInfo.isFirstLine());
 
         // |width| may have been adjusted because we got shoved down past a float (thus
         // giving us more room), so we need to retest, and only jump to
@@ -1163,7 +1207,7 @@
             m_atEnd = true;
             return;
         }
-    } else if (m_blockStyle.autoWrap() && !m_width.fitsOnLine() && !m_width.hasCommitted()) {
+    } else if (m_blockStyle.autoWrap() && !m_width.fitsOnLine() && !m_width.hasCommitted() && !m_hangsAtEnd) {
         // If the container autowraps but the current child does not then we still need to ensure that it
         // wraps and moves below any floats.
         m_width.fitBelowFloats(m_lineInfo.isFirstLine());
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to