Chromium Code Reviews| Index: third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h |
| diff --git a/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h b/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h |
| index 7b5d2a0a98770914df9efc22daa3aa66bbd6a2a1..7e1eeb5ca803c765f862772fecab5f315f1a2d7e 100644 |
| --- a/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h |
| +++ b/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h |
| @@ -40,6 +40,7 @@ |
| #include "core/layout/line/TrailingObjects.h" |
| #include "core/layout/line/WordMeasurement.h" |
| #include "core/paint/PaintLayer.h" |
| +#include "platform/text/Hyphenation.h" |
| #include "platform/text/TextBreakIterator.h" |
| #include "wtf/Allocator.h" |
| #include "wtf/Vector.h" |
| @@ -99,7 +100,7 @@ public: |
| void handleReplaced(); |
| bool handleText(WordMeasurements&, bool& hyphenated); |
| void prepareForNextCharacter(const LineLayoutText&, bool& prohibitBreakInside, bool previousCharacterIsSpace); |
| - bool canBreakAtWhitespace(bool breakWords, WordMeasurement&, bool stoppedIgnoringSpaces, bool& hyphenated, float charWidth, float& hyphenWidth, bool betweenWords, bool midWordBreak, bool canBreakMidWord, bool previousCharacterIsSpace, float lastWidthMeasurement, const LineLayoutText&, const Font&, bool applyWordSpacing, float wordSpacing); |
| + bool canBreakAtWhitespace(bool breakWords, WordMeasurement&, bool stoppedIgnoringSpaces, float charWidth, bool& hyphenated, bool disableSoftHyphen, float& hyphenWidth, bool betweenWords, bool midWordBreak, bool canBreakMidWord, bool previousCharacterIsSpace, float lastWidthMeasurement, const LineLayoutText&, const Font&, bool applyWordSpacing, float wordSpacing); |
| bool trailingSpaceExceedsAvailableWidth(bool canBreakMidWord, const LineLayoutText&, WordMeasurement&, bool applyWordSpacing, bool wordSpacing, const Font&); |
| WordMeasurement& calculateWordWidth(WordMeasurements&, LineLayoutText&, unsigned lastSpace, float& lastWidthMeasurement, float wordSpacingForWordMeasurement, const Font&, float wordTrailingSpaceWidth, UChar); |
| void stopIgnoringSpaces(unsigned& lastSpace); |
| @@ -117,6 +118,7 @@ private: |
| bool rewindToMidWordBreak(WordMeasurement&, int end, float width); |
| bool rewindToFirstMidWordBreak(LineLayoutText, const ComputedStyle&, const Font&, bool breakAll, WordMeasurement&); |
| bool rewindToMidWordBreak(LineLayoutText, const ComputedStyle&, const Font&, bool breakAll, WordMeasurement&); |
| + bool hyphenate(LineLayoutText, const ComputedStyle&, const Font&, const Hyphenation&, float lastSpaceWordSpacing, WordMeasurement&); |
| InlineBidiResolver& m_resolver; |
| @@ -560,8 +562,8 @@ ALWAYS_INLINE bool BreakingContext::rewindToMidWordBreak( |
| wordMeasurement.endOffset = end; |
| wordMeasurement.width = width; |
| - m_current.moveTo(m_current.getLineLayoutItem(), end); |
| - m_lineBreak.moveTo(m_current.getLineLayoutItem(), m_current.offset()); |
| + m_current.moveTo(m_current.getLineLayoutItem(), end, m_current.nextBreakablePosition()); |
| + m_lineBreak.moveTo(m_current.getLineLayoutItem(), end, m_current.nextBreakablePosition()); |
| return true; |
| } |
| @@ -622,6 +624,46 @@ ALWAYS_INLINE bool BreakingContext::rewindToMidWordBreak(LineLayoutText text, |
| return rewindToMidWordBreak(wordMeasurement, end, rect.width()); |
| } |
| +ALWAYS_INLINE bool BreakingContext::hyphenate(LineLayoutText text, |
| + const ComputedStyle& style, const Font& font, |
| + const Hyphenation& hyphenation, float lastSpaceWordSpacing, |
| + WordMeasurement& wordMeasurement) |
| +{ |
| + const unsigned minimumPrefixLength = 2; |
| + const unsigned minimumSuffixLength = 2; |
| + unsigned start = wordMeasurement.startOffset; |
| + unsigned len = wordMeasurement.endOffset - start; |
| + if (len <= minimumSuffixLength) |
| + return false; |
| + |
| + float hyphenWidth = text.hyphenWidth(font, textDirectionFromUnicode(m_resolver.position().direction())); |
| + float maxPrefixWidth = m_width.availableWidth() + LayoutUnit::epsilon() |
|
eae
2016/05/16 16:43:26
Do we need epsilon here? If we can ceil a floor to
kojii
2016/05/16 17:29:34
When tests use ch/em that should exactly fit, this
eae
2016/05/16 17:32:38
Thanks. We're trying to remove epsilon from LineWi
|
| + - m_width.currentWidth() - hyphenWidth - lastSpaceWordSpacing; |
| + |
| + // If the maximum width available for the prefix before the hyphen is small, then it is very unlikely |
| + // that an hyphenation opportunity exists, so do not bother to look for it. |
| + const int minPrefixWidthNumerator = 5; |
|
eae
2016/05/16 16:43:25
Where did you get these numbers from?
kojii
2016/05/16 17:29:34
From our removal patch (crrev.com/20526006). ToT W
eae
2016/05/16 17:32:38
Ok, could you add a quick comment explaining the n
|
| + const int minPrefixWidthDenominator = 4; |
| + if (maxPrefixWidth <= font.getFontDescription().computedSize() * minPrefixWidthNumerator / minPrefixWidthDenominator) |
| + return false; |
| + |
| + TextRun run = constructTextRun(font, text, start, len, style); |
| + run.setTabSize(!m_collapseWhiteSpace, style.getTabSize()); |
| + run.setXPos(m_width.currentWidth()); |
| + unsigned maxPrefixLength = font.offsetForPosition(run, maxPrefixWidth, false); |
| + if (maxPrefixLength < minimumPrefixLength) |
| + return false; |
| + |
| + unsigned prefixLength = hyphenation.lastHyphenLocation( |
| + text.text().createView(start, len), |
| + std::min(maxPrefixLength, len - minimumSuffixLength) + 1); |
| + if (!prefixLength || prefixLength < minimumPrefixLength) |
| + return false; |
| + |
| + FloatRect rect = font.selectionRectForText(run, FloatPoint(), 0, 0, prefixLength); |
|
eae
2016/05/16 16:43:25
Calling shaper.getCharacterRange instead of select
kojii
2016/05/16 17:29:34
Done.
eae
2016/05/16 17:32:38
Thanks!`
|
| + return rewindToMidWordBreak(wordMeasurement, start + prefixLength, rect.width()); |
| +} |
| + |
| inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool& hyphenated) |
| { |
| if (!m_current.offset()) |
| @@ -660,6 +702,9 @@ inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool |
| // See: fast/css3-text/css3-word-break/word-break-all-wrap-with-floats.html |
| float widthMeasurementAtLastBreakOpportunity = 0; |
| + Hyphenation* hyphenation = style.getHyphens() == HyphensAuto |
| + ? Hyphenation::get(font.getFontDescription().locale()) : nullptr; |
| + bool disableSoftHyphen = style.getHyphens() == HyphensNone; |
| float hyphenWidth = 0; |
| if (layoutText.isSVGInlineText()) { |
| @@ -705,7 +750,7 @@ inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool |
| m_width.setTrailingWhitespaceWidth(0); |
| } |
| - if (c == softHyphenCharacter && m_autoWrap && !hyphenWidth) { |
| + if (c == softHyphenCharacter && m_autoWrap && !hyphenWidth && !disableSoftHyphen) { |
| hyphenWidth = layoutText.hyphenWidth(font, textDirectionFromUnicode(m_resolver.position().direction())); |
| m_width.addUncommittedWidth(hyphenWidth); |
| } |
| @@ -726,7 +771,8 @@ inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool |
| // Determine if we are in the whitespace between words. |
| int nextBreakablePosition = m_current.nextBreakablePosition(); |
| - bool betweenWords = c == newlineCharacter || (m_currWS != PRE && !m_atStart && m_layoutTextInfo.m_lineBreakIterator.isBreakable(m_current.offset(), nextBreakablePosition, lineBreakType)); |
| + bool betweenWords = c == newlineCharacter || (m_currWS != PRE && !m_atStart && m_layoutTextInfo.m_lineBreakIterator.isBreakable(m_current.offset(), nextBreakablePosition, lineBreakType) |
| + && (!disableSoftHyphen || m_current.previousInSameNode() != softHyphenCharacter)); |
| m_current.setNextBreakablePosition(nextBreakablePosition); |
| // If we're in the middle of a word or at the start of a new one and can't break there, then continue to the next character. |
| @@ -788,18 +834,31 @@ inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool |
| m_width.fitBelowFloats(m_lineInfo.isFirstLine()); |
| midWordBreak = false; |
| - if (canBreakMidWord && !m_width.fitsOnLine()) { |
| - m_width.addUncommittedWidth(-wordMeasurement.width); |
| - if (rewindToMidWordBreak(layoutText, style, font, breakAll, wordMeasurement)) { |
| - lastWidthMeasurement = wordMeasurement.width + lastSpaceWordSpacing; |
| - midWordBreak = true; |
| + if (!m_width.fitsOnLine()) { |
| + if (canBreakMidWord) { |
| + m_width.addUncommittedWidth(-wordMeasurement.width); |
| + if (rewindToMidWordBreak(layoutText, style, font, breakAll, wordMeasurement)) { |
| + lastWidthMeasurement = wordMeasurement.width + lastSpaceWordSpacing; |
| + midWordBreak = true; |
| + } |
| + m_width.addUncommittedWidth(wordMeasurement.width); |
| + } else if (hyphenation) { |
| + m_width.addUncommittedWidth(-wordMeasurement.width); |
| + DCHECK(lastSpace == static_cast<unsigned>(wordMeasurement.startOffset)); |
| + DCHECK(m_current.offset() == static_cast<unsigned>(wordMeasurement.endOffset)); |
| + if (hyphenate(layoutText, style, font, *hyphenation, lastSpaceWordSpacing, wordMeasurement)) { |
| + m_width.addUncommittedWidth(wordMeasurement.width); |
| + hyphenated = true; |
| + m_atEnd = true; |
| + return false; |
| + } |
| + m_width.addUncommittedWidth(wordMeasurement.width); |
| } |
| - m_width.addUncommittedWidth(wordMeasurement.width); |
| } |
| // If there is a soft-break available at this whitespace position then take it. |
| applyWordSpacing = wordSpacing && m_currentCharacterIsSpace; |
| - if (canBreakAtWhitespace(breakWords, wordMeasurement, stoppedIgnoringSpaces, hyphenated, charWidth, hyphenWidth, betweenWords, midWordBreak, canBreakMidWord, previousCharacterIsSpace, lastWidthMeasurement, layoutText, font, applyWordSpacing, wordSpacing)) |
| + if (canBreakAtWhitespace(breakWords, wordMeasurement, stoppedIgnoringSpaces, charWidth, hyphenated, disableSoftHyphen, hyphenWidth, betweenWords, midWordBreak, canBreakMidWord, previousCharacterIsSpace, lastWidthMeasurement, layoutText, font, applyWordSpacing, wordSpacing)) |
| return false; |
| // If there is a hard-break available at this whitespace position then take it. |
| @@ -882,10 +941,23 @@ inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool |
| if (midWordBreak) { |
| m_width.commit(); |
| m_atEnd = true; |
| - } else if (!m_width.fitsOnLine() && !hyphenated |
| - && m_lineBreak.previousInSameNode() == softHyphenCharacter) { |
| - hyphenated = true; |
| - m_atEnd = true; |
| + } else if (!m_width.fitsOnLine()) { |
| + if (hyphenation && (m_nextObject || m_lineInfo.isEmpty())) { |
| + m_width.addUncommittedWidth(-wordMeasurement.width); |
| + DCHECK(lastSpace == static_cast<unsigned>(wordMeasurement.startOffset)); |
| + DCHECK(m_current.offset() == static_cast<unsigned>(wordMeasurement.endOffset)); |
| + if (hyphenate(layoutText, style, font, *hyphenation, lastSpaceWordSpacing, wordMeasurement)) { |
| + hyphenated = true; |
| + m_atEnd = true; |
| + } |
| + m_width.addUncommittedWidth(wordMeasurement.width); |
| + } |
| + if (!hyphenated |
| + && m_lineBreak.previousInSameNode() == softHyphenCharacter |
| + && !disableSoftHyphen) { |
| + hyphenated = true; |
| + m_atEnd = true; |
| + } |
| } |
| return false; |
| } |
| @@ -961,7 +1033,7 @@ inline bool BreakingContext::trailingSpaceExceedsAvailableWidth(bool canBreakMid |
| return false; |
| } |
| -inline bool BreakingContext::canBreakAtWhitespace(bool breakWords, WordMeasurement& wordMeasurement, bool stoppedIgnoringSpaces, bool& hyphenated, float charWidth, float& hyphenWidth, bool betweenWords, bool midWordBreak, bool canBreakMidWord, bool previousCharacterIsSpace, float lastWidthMeasurement, const LineLayoutText& layoutText, const Font& font, bool applyWordSpacing, float wordSpacing) |
| +inline bool BreakingContext::canBreakAtWhitespace(bool breakWords, WordMeasurement& wordMeasurement, bool stoppedIgnoringSpaces, float charWidth, bool& hyphenated, bool disableSoftHyphen, float& hyphenWidth, bool betweenWords, bool midWordBreak, bool canBreakMidWord, bool previousCharacterIsSpace, float lastWidthMeasurement, const LineLayoutText& layoutText, const Font& font, bool applyWordSpacing, float wordSpacing) |
| { |
| if (!m_autoWrap && !breakWords) |
| return false; |
| @@ -978,7 +1050,7 @@ inline bool BreakingContext::canBreakAtWhitespace(bool breakWords, WordMeasureme |
| m_lineInfo.setPreviousLineBrokeCleanly(true); |
| wordMeasurement.endOffset = m_lineBreak.offset(); |
| } |
| - if (m_lineBreak.getLineLayoutItem() && m_lineBreak.offset() && m_lineBreak.getLineLayoutItem().isText() && LineLayoutText(m_lineBreak.getLineLayoutItem()).textLength() && LineLayoutText(m_lineBreak.getLineLayoutItem()).characterAt(m_lineBreak.offset() - 1) == softHyphenCharacter) |
| + if (m_lineBreak.getLineLayoutItem() && m_lineBreak.offset() && m_lineBreak.getLineLayoutItem().isText() && LineLayoutText(m_lineBreak.getLineLayoutItem()).textLength() && LineLayoutText(m_lineBreak.getLineLayoutItem()).characterAt(m_lineBreak.offset() - 1) == softHyphenCharacter && !disableSoftHyphen) |
| hyphenated = true; |
| if (m_lineBreak.offset() && m_lineBreak.offset() != (unsigned)wordMeasurement.endOffset && !wordMeasurement.width) { |
| if (charWidth) { |