diff options
Diffstat (limited to 'Source/WebCore/rendering/RenderBlockLineLayout.cpp')
| -rwxr-xr-x | Source/WebCore/rendering/RenderBlockLineLayout.cpp | 271 |
1 files changed, 197 insertions, 74 deletions
diff --git a/Source/WebCore/rendering/RenderBlockLineLayout.cpp b/Source/WebCore/rendering/RenderBlockLineLayout.cpp index 77994804b..21fd552f7 100755 --- a/Source/WebCore/rendering/RenderBlockLineLayout.cpp +++ b/Source/WebCore/rendering/RenderBlockLineLayout.cpp @@ -46,7 +46,7 @@ #include <wtf/unicode/CharacterNames.h> #if ENABLE(CSS_EXCLUSIONS) -#include "WrapShapeInfo.h" +#include "ExclusionShapeInsideInfo.h" #endif #if ENABLE(SVG) @@ -64,9 +64,9 @@ namespace WebCore { const unsigned cMaxLineDepth = 200; #if ENABLE(CSS_EXCLUSIONS) -static inline WrapShapeInfo* layoutWrapShapeInfo(const RenderBlock* block) +static inline ExclusionShapeInsideInfo* layoutExclusionShapeInsideInfo(const RenderBlock* block) { - return block->view()->layoutState()->wrapShapeInfo(); + return block->view()->layoutState()->exclusionShapeInsideInfo(); } #endif @@ -87,16 +87,16 @@ public: { ASSERT(block); #if ENABLE(CSS_EXCLUSIONS) - WrapShapeInfo* wrapShapeInfo = layoutWrapShapeInfo(m_block); + ExclusionShapeInsideInfo* exclusionShapeInsideInfo = layoutExclusionShapeInsideInfo(m_block); // FIXME: Bug 91878: Add support for multiple segments, currently we only support one - if (wrapShapeInfo && wrapShapeInfo->hasSegments()) - m_segment = &wrapShapeInfo->segments()[0]; + if (exclusionShapeInsideInfo && exclusionShapeInsideInfo->hasSegments()) + m_segment = &exclusionShapeInsideInfo->segments()[0]; #endif updateAvailableWidth(); } #if ENABLE(SUBPIXEL_LAYOUT) - bool fitsOnLine() const { return currentWidth() <= m_availableWidth + LayoutUnit::epsilon(); } - bool fitsOnLine(float extra) const { return currentWidth() + extra <= m_availableWidth + LayoutUnit::epsilon(); } + bool fitsOnLine() const { return currentWidth() <= m_availableWidth; } + bool fitsOnLine(float extra) const { return currentWidth() + extra <= m_availableWidth; } #else bool fitsOnLine() const { return currentWidth() <= m_availableWidth; } bool fitsOnLine(float extra) const { return currentWidth() + extra <= m_availableWidth; } @@ -290,6 +290,14 @@ static inline LayoutUnit borderPaddingMarginEnd(RenderInline* child) return child->marginEnd() + child->paddingEnd() + child->borderEnd(); } +static bool shouldAddBorderPaddingMargin(RenderObject* child, bool &checkSide) +{ + if (!child || (child->isText() && !toRenderText(child)->textLength())) + return true; + checkSide = false; + return checkSide; +} + static LayoutUnit inlineLogicalWidth(RenderObject* child, bool start = true, bool end = true) { unsigned lineDepth = 1; @@ -297,10 +305,12 @@ static LayoutUnit inlineLogicalWidth(RenderObject* child, bool start = true, boo RenderObject* parent = child->parent(); while (parent->isRenderInline() && lineDepth++ < cMaxLineDepth) { RenderInline* parentAsRenderInline = toRenderInline(parent); - if (start && !child->previousSibling()) + if (start && shouldAddBorderPaddingMargin(child->previousSibling(), start)) extraWidth += borderPaddingMarginStart(parentAsRenderInline); - if (end && !child->nextSibling()) + if (end && shouldAddBorderPaddingMargin(child->nextSibling(), end)) extraWidth += borderPaddingMarginEnd(parentAsRenderInline); + if (!start && !end) + return extraWidth; child = parent; parent = child->parent(); } @@ -505,25 +515,34 @@ InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj, const LineInfo& l return result; } +template <typename CharacterType> +static inline bool endsWithASCIISpaces(const CharacterType* characters, unsigned pos, unsigned end) +{ + while (isASCIISpace(characters[pos])) { + pos++; + if (pos >= end) + return true; + } + return false; +} + static bool reachedEndOfTextRenderer(const BidiRunList<BidiRun>& bidiRuns) { BidiRun* run = bidiRuns.logicallyLastRun(); if (!run) return true; - unsigned int pos = run->stop(); + unsigned pos = run->stop(); RenderObject* r = run->m_object; if (!r->isText() || r->isBR()) return false; RenderText* renderText = toRenderText(r); - if (pos >= renderText->textLength()) + unsigned length = renderText->textLength(); + if (pos >= length) return true; - while (isASCIISpace(renderText->characters()[pos])) { - pos++; - if (pos >= renderText->textLength()) - return true; - } - return false; + if (renderText->is8Bit()) + return endsWithASCIISpaces(renderText->characters8(), pos, length); + return endsWithASCIISpaces(renderText->characters16(), pos, length); } RootInlineBox* RenderBlock::constructLine(BidiRunList<BidiRun>& bidiRuns, const LineInfo& lineInfo) @@ -682,22 +701,43 @@ static inline float measureHyphenWidth(RenderText* renderer, const Font& font) return font.width(RenderBlock::constructTextRun(renderer, font, style->hyphenString().string(), style)); } +class WordMeasurement { +public: + WordMeasurement() + : renderer(0) + , width(0) + , startOffset(0) + , endOffset(0) + { + } + + RenderText* renderer; + float width; + int startOffset; + int endOffset; + HashSet<const SimpleFontData*> fallbackFonts; +}; + static inline void setLogicalWidthForTextRun(RootInlineBox* lineBox, BidiRun* run, RenderText* renderer, float xPos, const LineInfo& lineInfo, - GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache) + GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& wordMeasurements) { +#if !(PLATFORM(CHROMIUM) && OS(DARWIN)) + UNUSED_PARAM(wordMeasurements); +#endif HashSet<const SimpleFontData*> fallbackFonts; GlyphOverflow glyphOverflow; + const Font& font = renderer->style(lineInfo.isFirstLine())->font(); // Always compute glyph overflow if the block's line-box-contain value is "glyphs". if (lineBox->fitsToGlyphs()) { // If we don't stick out of the root line's font box, then don't bother computing our glyph overflow. This optimization // will keep us from computing glyph bounds in nearly all cases. bool includeRootLine = lineBox->includesRootLineBoxFontOrLeading(); int baselineShift = lineBox->verticalPositionForBox(run->m_box, verticalPositionCache); - int rootDescent = includeRootLine ? lineBox->renderer()->style(lineInfo.isFirstLine())->font().fontMetrics().descent() : 0; - int rootAscent = includeRootLine ? lineBox->renderer()->style(lineInfo.isFirstLine())->font().fontMetrics().ascent() : 0; - int boxAscent = renderer->style(lineInfo.isFirstLine())->font().fontMetrics().ascent() - baselineShift; - int boxDescent = renderer->style(lineInfo.isFirstLine())->font().fontMetrics().descent() + baselineShift; + int rootDescent = includeRootLine ? font.fontMetrics().descent() : 0; + int rootAscent = includeRootLine ? font.fontMetrics().ascent() : 0; + int boxAscent = font.fontMetrics().ascent() - baselineShift; + int boxDescent = font.fontMetrics().descent() + baselineShift; if (boxAscent > rootDescent || boxDescent > rootAscent) glyphOverflow.computeBounds = true; } @@ -707,18 +747,59 @@ static inline void setLogicalWidthForTextRun(RootInlineBox* lineBox, BidiRun* ru const Font& font = renderer->style(lineInfo.isFirstLine())->font(); hyphenWidth = measureHyphenWidth(renderer, font); } - run->m_box->setLogicalWidth(renderer->width(run->m_start, run->m_stop - run->m_start, xPos, lineInfo.isFirstLine(), &fallbackFonts, &glyphOverflow) + hyphenWidth); + float measuredWidth = 0; + +#if !(PLATFORM(CHROMIUM) && OS(DARWIN)) + bool kerningIsEnabled = font.typesettingFeatures() & Kerning; + + // Since we don't cache glyph overflows, we need to re-measure the run if + // the style is linebox-contain: glyph. + + if (!lineBox->fitsToGlyphs() && renderer->canUseSimpleFontCodePath()) { + int lastEndOffset = run->m_start; + for (size_t i = 0, size = wordMeasurements.size(); i < size && lastEndOffset < run->m_stop; ++i) { + const WordMeasurement& wordMeasurement = wordMeasurements[i]; + if (wordMeasurement.width <=0 || wordMeasurement.startOffset == wordMeasurement.endOffset) + continue; + if (wordMeasurement.renderer != renderer || wordMeasurement.startOffset != lastEndOffset || wordMeasurement.endOffset > run->m_stop) + continue; + + lastEndOffset = wordMeasurement.endOffset; + if (kerningIsEnabled && lastEndOffset == run->m_stop) { + measuredWidth += renderer->width(wordMeasurement.startOffset, lastEndOffset - wordMeasurement.startOffset, xPos, lineInfo.isFirstLine()); + if (i > 0) + measuredWidth += renderer->style()->wordSpacing(); + } else + measuredWidth += wordMeasurement.width; + if (!wordMeasurement.fallbackFonts.isEmpty()) { + HashSet<const SimpleFontData*>::const_iterator end = wordMeasurement.fallbackFonts.end(); + for (HashSet<const SimpleFontData*>::const_iterator it = wordMeasurement.fallbackFonts.begin(); it != end; ++it) + fallbackFonts.add(*it); + } + } + if (measuredWidth && lastEndOffset != run->m_stop) { + // If we don't have enough cached data, we'll measure the run again. + measuredWidth = 0; + fallbackFonts.clear(); + } + } +#endif + + if (!measuredWidth) + measuredWidth = renderer->width(run->m_start, run->m_stop - run->m_start, xPos, lineInfo.isFirstLine(), &fallbackFonts, &glyphOverflow); + + run->m_box->setLogicalWidth(measuredWidth + hyphenWidth); if (!fallbackFonts.isEmpty()) { ASSERT(run->m_box->isText()); GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(toInlineTextBox(run->m_box), make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).iterator; - ASSERT(it->second.first.isEmpty()); - copyToVector(fallbackFonts, it->second.first); + ASSERT(it->value.first.isEmpty()); + copyToVector(fallbackFonts, it->value.first); run->m_box->parent()->clearDescendantsHaveSameLineHeightAndBaseline(); } if ((glyphOverflow.top || glyphOverflow.bottom || glyphOverflow.left || glyphOverflow.right)) { ASSERT(run->m_box->isText()); GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(toInlineTextBox(run->m_box), make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).iterator; - it->second.second = glyphOverflow; + it->value.second = glyphOverflow; run->m_box->clearKnownToHaveNoOverflow(); } } @@ -797,7 +878,7 @@ void RenderBlock::updateLogicalWidthForAlignment(const ETextAlign& textAlign, Bi } void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, const LineInfo& lineInfo, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd, - GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache) + GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& wordMeasurements) { ETextAlign textAlign = textAlignmentForLine(!reachedEnd && !lineBox->endsWithBreak()); @@ -808,10 +889,10 @@ void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, float logicalLeft = pixelSnappedLogicalLeftOffsetForLine(logicalHeight(), firstLine, lineLogicalHeight); float logicalRight = pixelSnappedLogicalRightOffsetForLine(logicalHeight(), firstLine, lineLogicalHeight); #if ENABLE(CSS_EXCLUSIONS) - WrapShapeInfo* wrapShapeInfo = layoutWrapShapeInfo(this); - if (wrapShapeInfo && wrapShapeInfo->hasSegments()) { - logicalLeft = max<float>(roundToInt(wrapShapeInfo->segments()[0].logicalLeft), logicalLeft); - logicalRight = min<float>(floorToInt(wrapShapeInfo->segments()[0].logicalRight), logicalRight); + ExclusionShapeInsideInfo* exclusionShapeInsideInfo = layoutExclusionShapeInsideInfo(this); + if (exclusionShapeInsideInfo && exclusionShapeInsideInfo->hasSegments()) { + logicalLeft = max<float>(roundToInt(exclusionShapeInsideInfo->segments()[0].logicalLeft), logicalLeft); + logicalRight = min<float>(floorToInt(exclusionShapeInsideInfo->segments()[0].logicalRight), logicalRight); } #endif float availableLogicalWidth = logicalRight - logicalLeft; @@ -833,18 +914,22 @@ void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, if (textAlign == JUSTIFY && r != trailingSpaceRun) { if (!isAfterExpansion) toInlineTextBox(r->m_box)->setCanHaveLeadingExpansion(true); - unsigned opportunitiesInRun = Font::expansionOpportunityCount(rt->characters() + r->m_start, r->m_stop - r->m_start, r->m_box->direction(), isAfterExpansion); + unsigned opportunitiesInRun; + if (rt->is8Bit()) + opportunitiesInRun = Font::expansionOpportunityCount(rt->characters8() + r->m_start, r->m_stop - r->m_start, r->m_box->direction(), isAfterExpansion); + else + opportunitiesInRun = Font::expansionOpportunityCount(rt->characters16() + r->m_start, r->m_stop - r->m_start, r->m_box->direction(), isAfterExpansion); expansionOpportunities.append(opportunitiesInRun); expansionOpportunityCount += opportunitiesInRun; } if (int length = rt->textLength()) { - if (!r->m_start && needsWordSpacing && isSpaceOrNewline(rt->characters()[r->m_start])) + if (!r->m_start && needsWordSpacing && isSpaceOrNewline(rt->characterAt(r->m_start))) totalLogicalWidth += rt->style(lineInfo.isFirstLine())->font().wordSpacing(); - needsWordSpacing = !isSpaceOrNewline(rt->characters()[r->m_stop - 1]) && r->m_stop == length; + needsWordSpacing = !isSpaceOrNewline(rt->characterAt(r->m_stop - 1)) && r->m_stop == length; } - setLogicalWidthForTextRun(lineBox, r, rt, totalLogicalWidth, lineInfo, textBoxDataMap, verticalPositionCache); + setLogicalWidthForTextRun(lineBox, r, rt, totalLogicalWidth, lineInfo, textBoxDataMap, verticalPositionCache, wordMeasurements); } else { isAfterExpansion = false; if (!r->m_object->isRenderInline()) { @@ -1071,7 +1156,7 @@ static inline void constructBidiRuns(InlineBidiResolver& topResolver, BidiRunLis } // This function constructs line boxes for all of the text runs in the resolver and computes their position. -RootInlineBox* RenderBlock::createLineBoxesFromBidiRuns(BidiRunList<BidiRun>& bidiRuns, const InlineIterator& end, LineInfo& lineInfo, VerticalPositionCache& verticalPositionCache, BidiRun* trailingSpaceRun) +RootInlineBox* RenderBlock::createLineBoxesFromBidiRuns(BidiRunList<BidiRun>& bidiRuns, const InlineIterator& end, LineInfo& lineInfo, VerticalPositionCache& verticalPositionCache, BidiRun* trailingSpaceRun, WordMeasurements& wordMeasurements) { if (!bidiRuns.runCount()) return 0; @@ -1095,7 +1180,7 @@ RootInlineBox* RenderBlock::createLineBoxesFromBidiRuns(BidiRunList<BidiRun>& bi // Now we position all of our text runs horizontally. if (!isSVGRootInlineBox) - computeInlineDirectionPositionsForLine(lineBox, lineInfo, bidiRuns.firstRun(), trailingSpaceRun, end.atEnd(), textBoxDataMap, verticalPositionCache); + computeInlineDirectionPositionsForLine(lineBox, lineInfo, bidiRuns.firstRun(), trailingSpaceRun, end.atEnd(), textBoxDataMap, verticalPositionCache, wordMeasurements); // Now position our text runs vertically. computeBlockDirectionPositionsForLine(lineBox, bidiRuns.firstRun(), textBoxDataMap, verticalPositionCache); @@ -1301,16 +1386,16 @@ void RenderBlock::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, Inlin #if ENABLE(CSS_EXCLUSIONS) LayoutUnit absoluteLogicalTop; - WrapShapeInfo* wrapShapeInfo = layoutWrapShapeInfo(this); - if (wrapShapeInfo) { - if (wrapShapeInfo != this->wrapShapeInfo()) { + ExclusionShapeInsideInfo* exclusionShapeInsideInfo = layoutExclusionShapeInsideInfo(this); + if (exclusionShapeInsideInfo) { + if (exclusionShapeInsideInfo != this->exclusionShapeInsideInfo()) { // FIXME: If layout state is disabled, the offset will be incorrect. LayoutSize layoutOffset = view()->layoutState()->layoutOffset(); absoluteLogicalTop = logicalTop() + (isHorizontalWritingMode() ? layoutOffset.height() : layoutOffset.width()); } // Begin layout at the logical top of our shape inside. - if (logicalHeight() + absoluteLogicalTop < wrapShapeInfo->shapeLogicalTop()) - setLogicalHeight(wrapShapeInfo->shapeLogicalTop() - absoluteLogicalTop); + if (logicalHeight() + absoluteLogicalTop < exclusionShapeInsideInfo->shapeLogicalTop()) + setLogicalHeight(exclusionShapeInsideInfo->shapeLogicalTop() - absoluteLogicalTop); } #endif @@ -1335,13 +1420,14 @@ void RenderBlock::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, Inlin #if ENABLE(CSS_EXCLUSIONS) // FIXME: Bug 95361: It is possible for a line to grow beyond lineHeight, in which // case these segments may be incorrect. - if (wrapShapeInfo) { + if (exclusionShapeInsideInfo) { LayoutUnit lineTop = logicalHeight() + absoluteLogicalTop; LayoutUnit lineBottom = lineTop + lineHeight(layoutState.lineInfo().isFirstLine(), isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes); - wrapShapeInfo->computeSegmentsForLine(lineTop, lineBottom); + exclusionShapeInsideInfo->computeSegmentsForLine(lineTop, lineBottom); } #endif - end = lineBreaker.nextLineBreak(resolver, layoutState.lineInfo(), renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines); + WordMeasurements wordMeasurements; + end = lineBreaker.nextLineBreak(resolver, layoutState.lineInfo(), renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements); if (resolver.position().atEnd()) { // FIXME: We shouldn't be creating any runs in nextLineBreak to begin with! // Once BidiRunList is separated from BidiResolver this will not be needed. @@ -1383,7 +1469,7 @@ void RenderBlock::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, Inlin // inline flow boxes. LayoutUnit oldLogicalHeight = logicalHeight(); - RootInlineBox* lineBox = createLineBoxesFromBidiRuns(bidiRuns, end, layoutState.lineInfo(), verticalPositionCache, trailingSpaceRun); + RootInlineBox* lineBox = createLineBoxesFromBidiRuns(bidiRuns, end, layoutState.lineInfo(), verticalPositionCache, trailingSpaceRun, wordMeasurements); bidiRuns.deleteRuns(); resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced by an ASSERT (or just removed). @@ -2031,7 +2117,7 @@ static bool shouldSkipWhitespaceAfterStartObject(RenderBlock* block, RenderObjec RenderObject* next = bidiNextSkippingEmptyInlines(block, o); if (next && !next->isBR() && next->isText() && toRenderText(next)->textLength() > 0) { RenderText* nextText = toRenderText(next); - UChar nextChar = nextText->characters()[0]; + UChar nextChar = nextText->characterAt(0); if (nextText->style()->isCollapsibleWhiteSpace(nextChar)) { addMidpoint(lineMidpointState, InlineIterator(0, o, 0)); return true; @@ -2041,32 +2127,40 @@ static bool shouldSkipWhitespaceAfterStartObject(RenderBlock* block, RenderObjec return false; } -static inline float textWidth(RenderText* text, unsigned from, unsigned len, const Font& font, float xPos, bool isFixedPitch, bool collapseWhiteSpace, TextLayout* layout = 0) +static ALWAYS_INLINE float textWidth(RenderText* text, unsigned from, unsigned len, const Font& font, float xPos, bool isFixedPitch, bool collapseWhiteSpace, HashSet<const SimpleFontData*>* fallbackFonts = 0, TextLayout* layout = 0) { + GlyphOverflow glyphOverflow; if (isFixedPitch || (!from && len == text->textLength()) || text->style()->hasTextCombine()) - return text->width(from, len, font, xPos); + return text->width(from, len, font, xPos, fallbackFonts, &glyphOverflow); if (layout) - return Font::width(*layout, from, len); + return Font::width(*layout, from, len, fallbackFonts); - TextRun run = RenderBlock::constructTextRun(text, font, text->characters() + from, len, text->style()); + TextRun run = RenderBlock::constructTextRun(text, font, text, from, len, text->style()); run.setCharactersLength(text->textLength() - from); ASSERT(run.charactersLength() >= run.length()); run.setCharacterScanForCodePath(!text->canUseSimpleFontCodePath()); run.setTabSize(!collapseWhiteSpace, text->style()->tabSize()); run.setXPos(xPos); - return font.width(run); + return font.width(run, fallbackFonts, &glyphOverflow); } -static void tryHyphenating(RenderText* text, const Font& font, const AtomicString& localeIdentifier, unsigned consecutiveHyphenatedLines, int consecutiveHyphenatedLinesLimit, int minimumPrefixLength, int minimumSuffixLength, int lastSpace, int pos, float xPos, int availableWidth, bool isFixedPitch, bool collapseWhiteSpace, int lastSpaceWordSpacing, InlineIterator& lineBreak, int nextBreakable, bool& hyphenated) +static void tryHyphenating(RenderText* text, const Font& font, const AtomicString& localeIdentifier, unsigned consecutiveHyphenatedLines, int consecutiveHyphenatedLinesLimit, int minimumPrefixLimit, int minimumSuffixLimit, unsigned lastSpace, unsigned pos, float xPos, int availableWidth, bool isFixedPitch, bool collapseWhiteSpace, int lastSpaceWordSpacing, InlineIterator& lineBreak, int nextBreakable, bool& hyphenated) { // Map 'hyphenate-limit-{before,after}: auto;' to 2. - if (minimumPrefixLength < 0) + unsigned minimumPrefixLength; + unsigned minimumSuffixLength; + + if (minimumPrefixLimit < 0) minimumPrefixLength = 2; + else + minimumPrefixLength = static_cast<unsigned>(minimumPrefixLimit); - if (minimumSuffixLength < 0) + if (minimumSuffixLimit < 0) minimumSuffixLength = 2; + else + minimumSuffixLength = static_cast<unsigned>(minimumSuffixLimit); if (pos - lastSpace <= minimumSuffixLength) return; @@ -2082,7 +2176,7 @@ static void tryHyphenating(RenderText* text, const Font& font, const AtomicStrin if (maxPrefixWidth <= font.pixelSize() * 5 / 4) return; - TextRun run = RenderBlock::constructTextRun(text, font, text->characters() + lastSpace, pos - lastSpace, text->style()); + TextRun run = RenderBlock::constructTextRun(text, font, text, lastSpace, pos - lastSpace, text->style()); run.setCharactersLength(text->textLength() - lastSpace); ASSERT(run.charactersLength() >= run.length()); @@ -2090,22 +2184,22 @@ static void tryHyphenating(RenderText* text, const Font& font, const AtomicStrin run.setXPos(xPos + lastSpaceWordSpacing); unsigned prefixLength = font.offsetForPosition(run, maxPrefixWidth, false); - if (prefixLength < static_cast<unsigned>(minimumPrefixLength)) + if (prefixLength < minimumPrefixLength) return; - prefixLength = lastHyphenLocation(text->characters() + lastSpace, pos - lastSpace, min(prefixLength, static_cast<unsigned>(pos - lastSpace - minimumSuffixLength)) + 1, localeIdentifier); - if (!prefixLength || prefixLength < static_cast<unsigned>(minimumPrefixLength)) + prefixLength = lastHyphenLocation(text->characters() + lastSpace, pos - lastSpace, min(prefixLength, pos - lastSpace - minimumSuffixLength) + 1, localeIdentifier); + if (!prefixLength || prefixLength < minimumPrefixLength) return; // When lastSapce is a space, which it always is except sometimes at the beginning of a line or after collapsed // space, it should not count towards hyphenate-limit-before. - if (prefixLength == static_cast<unsigned>(minimumPrefixLength)) { - UChar characterAtLastSpace = text->characters()[lastSpace]; + if (prefixLength == minimumPrefixLength) { + UChar characterAtLastSpace = text->characterAt(lastSpace); if (characterAtLastSpace == ' ' || characterAtLastSpace == '\n' || characterAtLastSpace == '\t' || characterAtLastSpace == noBreakSpace) return; } - ASSERT(pos - lastSpace - prefixLength >= static_cast<unsigned>(minimumSuffixLength)); + ASSERT(pos - lastSpace - prefixLength >= minimumSuffixLength); #if !ASSERT_DISABLED float prefixWidth = hyphenWidth + textWidth(text, lastSpace, prefixLength, font, xPos, isFixedPitch, collapseWhiteSpace) + lastSpaceWordSpacing; @@ -2210,7 +2304,7 @@ void RenderBlock::LineBreaker::reset() m_clear = CNONE; } -InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resolver, LineInfo& lineInfo, RenderTextInfo& renderTextInfo, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines) +InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resolver, LineInfo& lineInfo, RenderTextInfo& renderTextInfo, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines, WordMeasurements& wordMeasurements) { reset(); @@ -2425,9 +2519,10 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol bool isFixedPitch = f.isFixedPitch(); bool canHyphenate = style->hyphens() == HyphensAuto && WebCore::canHyphenate(style->locale()); - int lastSpace = current.m_pos; + unsigned lastSpace = current.m_pos; float wordSpacing = currentStyle->wordSpacing(); float lastSpaceWordSpacing = 0; + float wordSpacingForWordMeasurement = 0; float wrapW = width.uncommittedWidth() + inlineLogicalWidth(current.m_obj, !appliedStartWidth, true); float charWidth = 0; @@ -2450,7 +2545,7 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol renderTextInfo.m_text = t; renderTextInfo.m_font = &f; renderTextInfo.m_layout = f.createLayout(t, width.currentWidth(), collapseWhiteSpace); - renderTextInfo.m_lineBreakIterator.reset(t->characters(), t->textLength(), style->locale()); + renderTextInfo.m_lineBreakIterator.reset(t->text(), style->locale()); } else if (renderTextInfo.m_layout && renderTextInfo.m_font != &f) { renderTextInfo.m_font = &f; renderTextInfo.m_layout = f.createLayout(t, width.currentWidth(), collapseWhiteSpace); @@ -2483,7 +2578,7 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol if ((breakAll || breakWords) && !midWordBreak) { wrapW += charWidth; bool midWordBreakIsBeforeSurrogatePair = U16_IS_LEAD(c) && current.m_pos + 1 < t->textLength() && U16_IS_TRAIL(t->characters()[current.m_pos + 1]); - charWidth = textWidth(t, current.m_pos, midWordBreakIsBeforeSurrogatePair ? 2 : 1, f, width.committedWidth() + wrapW, isFixedPitch, collapseWhiteSpace, textLayout); + charWidth = textWidth(t, current.m_pos, midWordBreakIsBeforeSurrogatePair ? 2 : 1, f, width.committedWidth() + wrapW, isFixedPitch, collapseWhiteSpace, 0, textLayout); midWordBreak = width.committedWidth() + wrapW + charWidth > width.availableWidth(); } @@ -2498,6 +2593,7 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol // new point. ignoringSpaces = false; lastSpaceWordSpacing = 0; + wordSpacingForWordMeasurement = 0; lastSpace = current.m_pos; // e.g., "Foo goo", don't add in any of the ignored spaces. addMidpoint(lineMidpointState, InlineIterator(0, current.m_obj, current.m_pos)); stoppedIgnoringSpaces = true; @@ -2507,18 +2603,28 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol } } + wordMeasurements.grow(wordMeasurements.size() + 1); + WordMeasurement& wordMeasurement = wordMeasurements.last(); + + wordMeasurement.renderer = t; + wordMeasurement.endOffset = current.m_pos; + wordMeasurement.startOffset = lastSpace; + float additionalTmpW; if (wordTrailingSpaceWidth && currentCharacterIsSpace) - additionalTmpW = textWidth(t, lastSpace, current.m_pos + 1 - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, textLayout) - wordTrailingSpaceWidth + lastSpaceWordSpacing; + additionalTmpW = textWidth(t, lastSpace, current.m_pos + 1 - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, &wordMeasurement.fallbackFonts, textLayout) - wordTrailingSpaceWidth; else - additionalTmpW = textWidth(t, lastSpace, current.m_pos - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, textLayout) + lastSpaceWordSpacing; + additionalTmpW = textWidth(t, lastSpace, current.m_pos - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, &wordMeasurement.fallbackFonts, textLayout); + + wordMeasurement.width = additionalTmpW + wordSpacingForWordMeasurement; + additionalTmpW += lastSpaceWordSpacing; width.addUncommittedWidth(additionalTmpW); if (!appliedStartWidth) { width.addUncommittedWidth(inlineLogicalWidth(current.m_obj, true, false)); appliedStartWidth = true; } - applyWordSpacing = wordSpacing && currentCharacterIsSpace; + applyWordSpacing = wordSpacing && currentCharacterIsSpace; if (!width.committedWidth() && autoWrap && !width.fitsOnLine()) width.fitBelowFloats(); @@ -2528,7 +2634,7 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol // as candidate width for this line. bool lineWasTooWide = false; if (width.fitsOnLine() && currentCharacterIsWS && currentStyle->breakOnlyAfterWhiteSpace() && !midWordBreak) { - float charWidth = textWidth(t, current.m_pos, 1, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, textLayout) + (applyWordSpacing ? wordSpacing : 0); + float charWidth = textWidth(t, current.m_pos, 1, f, width.currentWidth(), isFixedPitch, 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. // If the line needs the extra whitespace to be too long, @@ -2554,9 +2660,16 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol } lBreak.increment(); lineInfo.setPreviousLineBrokeCleanly(true); + wordMeasurement.endOffset = lBreak.m_pos; } - if (lBreak.m_obj && lBreak.m_pos && lBreak.m_obj->isText() && toRenderText(lBreak.m_obj)->textLength() && toRenderText(lBreak.m_obj)->characters()[lBreak.m_pos - 1] == softHyphen && style->hyphens() != HyphensNone) + if (lBreak.m_obj && lBreak.m_pos && lBreak.m_obj->isText() && toRenderText(lBreak.m_obj)->textLength() && toRenderText(lBreak.m_obj)->characterAt(lBreak.m_pos - 1) == softHyphen && style->hyphens() != HyphensNone) m_hyphenated = true; + if (lBreak.m_pos && lBreak.m_pos != (unsigned)wordMeasurement.endOffset && !wordMeasurement.width) { + if (charWidth) { + wordMeasurement.endOffset = lBreak.m_pos; + wordMeasurement.width = charWidth; + } + } goto end; // Didn't fit. Jump to the end. } else { if (!betweenWords || (midWordBreak && !autoWrap)) @@ -2599,6 +2712,7 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol if (betweenWords) { lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0; + wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurement.width) ? wordSpacing : 0; lastSpace = current.m_pos; } @@ -2621,6 +2735,7 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol // new point. ignoringSpaces = false; lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0; + wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurements.last().width) ? wordSpacing : 0; lastSpace = current.m_pos; // e.g., "Foo goo", don't add in any of the ignored spaces. addMidpoint(lineMidpointState, InlineIterator(0, current.m_obj, current.m_pos)); } @@ -2653,8 +2768,16 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol atStart = false; } + wordMeasurements.grow(wordMeasurements.size() + 1); + WordMeasurement& wordMeasurement = wordMeasurements.last(); + wordMeasurement.renderer = t; + // IMPORTANT: current.m_pos is > length here! - float additionalTmpW = ignoringSpaces ? 0 : textWidth(t, lastSpace, current.m_pos - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, textLayout) + lastSpaceWordSpacing; + float additionalTmpW = ignoringSpaces ? 0 : textWidth(t, lastSpace, current.m_pos - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, &wordMeasurement.fallbackFonts, textLayout); + wordMeasurement.startOffset = lastSpace; + wordMeasurement.endOffset = current.m_pos; + wordMeasurement.width = ignoringSpaces ? 0 : additionalTmpW + wordSpacingForWordMeasurement; + additionalTmpW += lastSpaceWordSpacing; width.addUncommittedWidth(additionalTmpW + inlineLogicalWidth(current.m_obj, !appliedStartWidth, includeEndWidth)); includeEndWidth = false; @@ -2680,7 +2803,7 @@ InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resol else { RenderText* nextText = toRenderText(next); if (nextText->textLength()) { - UChar c = nextText->characters()[0]; + UChar c = nextText->characterAt(0); checkForBreak = (c == ' ' || c == '\t' || (c == '\n' && !next->preservesNewline())); // If the next item on the line is text, and if we did not end with // a space, then the next text run continues our word (and so it needs to |
