diff options
Diffstat (limited to 'WebCore/rendering/RenderBlockLineLayout.cpp')
-rw-r--r-- | WebCore/rendering/RenderBlockLineLayout.cpp | 174 |
1 files changed, 131 insertions, 43 deletions
diff --git a/WebCore/rendering/RenderBlockLineLayout.cpp b/WebCore/rendering/RenderBlockLineLayout.cpp index a7f3553..7cb1597 100644 --- a/WebCore/rendering/RenderBlockLineLayout.cpp +++ b/WebCore/rendering/RenderBlockLineLayout.cpp @@ -24,11 +24,13 @@ #include "BidiResolver.h" #include "CharacterNames.h" +#include "Hyphenation.h" #include "InlineIterator.h" #include "InlineTextBox.h" #include "Logging.h" #include "RenderArena.h" #include "RenderInline.h" +#include "RenderLayer.h" #include "RenderListMarker.h" #include "RenderView.h" #include "TrailingFloatsRootInlineBox.h" @@ -45,6 +47,10 @@ #include "HTMLNames.h" #endif // ANDROID_LAYOUT +#if ENABLE(SVG) +#include "SVGRootInlineBox.h" +#endif + using namespace std; using namespace WTF; using namespace Unicode; @@ -297,6 +303,8 @@ RootInlineBox* RenderBlock::constructLine(unsigned runCount, BidiRun* firstRun, text->setStart(r->m_start); text->setLen(r->m_stop - r->m_start); text->m_dirOverride = r->dirOverride(visuallyOrdered); + if (r->m_hasHyphen) + text->setHasHyphen(true); } } @@ -317,7 +325,7 @@ RootInlineBox* RenderBlock::constructLine(unsigned runCount, BidiRun* firstRun, return lastRootBox(); } -void RenderBlock::computeHorizontalPositionsForLine(RootInlineBox* lineBox, bool firstLine, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd) +void RenderBlock::computeHorizontalPositionsForLine(RootInlineBox* lineBox, bool firstLine, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd, GlyphOverflowAndFallbackFontsMap& textBoxDataMap) { // First determine our total width. int availableWidth = lineWidth(height(), firstLine); @@ -349,14 +357,23 @@ void RenderBlock::computeHorizontalPositionsForLine(RootInlineBox* lineBox, bool needsWordSpacing = !isSpaceOrNewline(rt->characters()[r->m_stop - 1]) && r->m_stop == length; } HashSet<const SimpleFontData*> fallbackFonts; - r->m_box->setWidth(rt->width(r->m_start, r->m_stop - r->m_start, totWidth, firstLine, &fallbackFonts)); - if (!fallbackFonts.isEmpty() -#if ENABLE(SVG) - && !isSVGText() -#endif - ) { + GlyphOverflow glyphOverflow; + int hyphenWidth = 0; + if (static_cast<InlineTextBox*>(r->m_box)->hasHyphen()) { + const AtomicString& hyphenString = rt->style()->hyphenString(); + hyphenWidth = rt->style(firstLine)->font().width(TextRun(hyphenString.characters(), hyphenString.length())); + } + r->m_box->setWidth(rt->width(r->m_start, r->m_stop - r->m_start, totWidth, firstLine, &fallbackFonts, &glyphOverflow) + hyphenWidth); + if (!fallbackFonts.isEmpty()) { + ASSERT(r->m_box->isText()); + GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(static_cast<InlineTextBox*>(r->m_box), make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).first; + ASSERT(it->second.first.isEmpty()); + copyToVector(fallbackFonts, it->second.first); + } + if ((glyphOverflow.top || glyphOverflow.bottom || glyphOverflow.left || glyphOverflow.right)) { ASSERT(r->m_box->isText()); - static_cast<InlineTextBox*>(r->m_box)->setFallbackFonts(fallbackFonts); + GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(static_cast<InlineTextBox*>(r->m_box), make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).first; + it->second.second = glyphOverflow; } } else if (!r->m_object->isRenderInline()) { RenderBox* renderBox = toRenderBox(r->m_object); @@ -473,12 +490,12 @@ void RenderBlock::computeHorizontalPositionsForLine(RootInlineBox* lineBox, bool // The widths of all runs are now known. We can now place every inline box (and // compute accurate widths for the inline flow boxes). needsWordSpacing = false; - lineBox->placeBoxesHorizontally(x, needsWordSpacing); + lineBox->placeBoxesHorizontally(x, needsWordSpacing, textBoxDataMap); } -void RenderBlock::computeVerticalPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun) +void RenderBlock::computeVerticalPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap) { - setHeight(lineBox->verticallyAlignBoxes(height())); + setHeight(lineBox->verticallyAlignBoxes(height(), textBoxDataMap)); lineBox->setBlockHeight(height()); // Now make sure we place replaced render objects correctly. @@ -504,12 +521,6 @@ void RenderBlock::computeVerticalPositionsForLine(RootInlineBox* lineBox, BidiRu lineBox->markDirty(false); } -// collects one line of the paragraph and transforms it to visual order -void RenderBlock::bidiReorderLine(InlineBidiResolver& resolver, const InlineIterator& end, bool previousLineBrokeCleanly) -{ - resolver.createBidiRunsForLine(end, style()->visuallyOrdered(), previousLineBrokeCleanly); -} - static inline bool isCollapsibleSpace(UChar character, RenderText* renderer) { if (character == ' ' || character == '\t' || character == softHyphen) @@ -609,7 +620,23 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i else if (fullLayout || o->needsLayout()) // Replaced elements toRenderBox(o)->dirtyLineBoxes(fullLayout); +#if defined(ANDROID_FLATTEN_IFRAME) || defined(ANDROID_FLATTEN_FRAMESET) + // Android frame flattening during layout() may cause the + // renderer to be destroyed, if for example a frames onresize handler + // deletes the frame - see http://trac.webkit.org/changeset/61070 for example. + // We may be able to remove this protector when we switch to the upstream + // frame flattening code. In the case of an anonymous render object like RenderListMarker + // the document is the DOM node associated with this RenderObject. + RefPtr<Node> protector(o->isAnonymous() ? o->document() : o->node()); +#endif o->layoutIfNeeded(); +#if defined(ANDROID_FLATTEN_IFRAME) || defined(ANDROID_FLATTEN_FRAMESET) + // Ensure that the renderer still exists. If it's gone away we will crash pretty + // fast by continuing to use the now invalid o pointer. If the renderer has gone, + // then there's no point in continuing to try to layout it's children so we break. + if (!protector->renderer()) + break; +#endif } } else if (o->isText() || (o->isRenderInline() && !endOfInline)) { hasInlineChild = true; @@ -665,12 +692,12 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i obj = obj->container(); } if (!isConstrained) { - int screenWidth = view()->frameView()->screenWidth(); + int textWrapWidth = view()->frameView()->textWrapWidth(); int padding = paddingLeft() + paddingRight(); - if (screenWidth > 0 && width() > (screenWidth + padding)) { + if (textWrapWidth > 0 && width() > (textWrapWidth + padding)) { // limit the content width (width excluding padding) to be - // (screenWidth - 2 * ANDROID_FCTS_MARGIN_PADDING) - int maxWidth = screenWidth - 2 * ANDROID_FCTS_MARGIN_PADDING + padding; + // (textWrapWidth - 2 * ANDROID_FCTS_MARGIN_PADDING) + int maxWidth = textWrapWidth - 2 * ANDROID_FCTS_MARGIN_PADDING + padding; setWidth(min(width(), maxWidth)); m_minPrefWidth = min(m_minPrefWidth, maxWidth); m_maxPrefWidth = min(m_maxPrefWidth, maxWidth); @@ -753,7 +780,6 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i bool endLineMatched = false; bool checkForEndLineMatch = endLine; bool checkForFloatsFromLastLine = false; - int lastHeight = height(); bool isLineEmpty = true; @@ -767,7 +793,8 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i isLineEmpty = true; EClear clear = CNONE; - end = findNextLineBreak(resolver, firstLine, isLineEmpty, previousLineBrokeCleanly, &clear); + bool hyphenated; + end = findNextLineBreak(resolver, firstLine, isLineEmpty, previousLineBrokeCleanly, hyphenated, &clear); if (resolver.position().atEnd()) { resolver.deleteRuns(); checkForFloatsFromLastLine = true; @@ -776,7 +803,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i ASSERT(end != resolver.position()); if (!isLineEmpty) { - bidiReorderLine(resolver, end, previousLineBrokeCleanly); + resolver.createBidiRunsForLine(end, style()->visuallyOrdered(), previousLineBrokeCleanly); ASSERT(resolver.position() == end); BidiRun* trailingSpaceRun = 0; @@ -833,19 +860,37 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i RootInlineBox* lineBox = 0; if (resolver.runCount()) { + if (hyphenated) + resolver.logicallyLastRun()->m_hasHyphen = true; lineBox = constructLine(resolver.runCount(), resolver.firstRun(), resolver.lastRun(), firstLine, !end.obj, end.obj && !end.pos ? end.obj : 0); if (lineBox) { lineBox->setEndsWithBreak(previousLineBrokeCleanly); +#if ENABLE(SVG) + bool isSVGRootInlineBox = lineBox->isSVGRootInlineBox(); +#else + bool isSVGRootInlineBox = false; +#endif + + GlyphOverflowAndFallbackFontsMap textBoxDataMap; + // Now we position all of our text runs horizontally. - computeHorizontalPositionsForLine(lineBox, firstLine, resolver.firstRun(), trailingSpaceRun, end.atEnd()); + if (!isSVGRootInlineBox) + computeHorizontalPositionsForLine(lineBox, firstLine, resolver.firstRun(), trailingSpaceRun, end.atEnd(), textBoxDataMap); // Now position our text runs vertically. - computeVerticalPositionsForLine(lineBox, resolver.firstRun()); + computeVerticalPositionsForLine(lineBox, resolver.firstRun(), textBoxDataMap); #if ENABLE(SVG) - // Special SVG text layout code - lineBox->computePerCharacterLayoutInformation(); + // SVG text layout code computes vertical & horizontal positions on its own. + // Note that we still need to execute computeVerticalPositionsForLine() as + // it calls InlineTextBox::positionLineBox(), which tracks whether the box + // contains reversed text or not. If we wouldn't do that editing and thus + // text selection in RTL boxes would not work as expected. + if (isSVGRootInlineBox) { + ASSERT(isSVGText()); + static_cast<SVGRootInlineBox*>(lineBox)->computePerCharacterLayoutInformation(); + } #endif #if PLATFORM(MAC) @@ -878,8 +923,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i } else m_floatingObjects->first(); for (FloatingObject* f = m_floatingObjects->current(); f; f = m_floatingObjects->next()) { - if (f->m_bottom > lastHeight) - lastRootBox()->floats().append(f->m_renderer); + lastRootBox()->floats().append(f->m_renderer); ASSERT(f->m_renderer == floats[floatIndex].object); // If a float's geometry has changed, give up on syncing with clean lines. if (floats[floatIndex].rect != IntRect(f->m_left, f->m_top, f->m_width, f->m_bottom - f->m_top)) @@ -889,7 +933,6 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i lastFloat = m_floatingObjects->last(); } - lastHeight = height(); lineMidpointState.reset(); resolver.setPosition(end); } @@ -939,7 +982,8 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i TrailingFloatsRootInlineBox* trailingFloatsLineBox = new (renderArena()) TrailingFloatsRootInlineBox(this); m_lineBoxes.appendLineBox(trailingFloatsLineBox); trailingFloatsLineBox->setConstructed(); - trailingFloatsLineBox->verticallyAlignBoxes(height()); + GlyphOverflowAndFallbackFontsMap textBoxDataMap; + trailingFloatsLineBox->verticallyAlignBoxes(height(), textBoxDataMap); trailingFloatsLineBox->setVerticalOverflowPositions(height(), bottomLayoutOverflow, height(), bottomVisualOverflow, 0); trailingFloatsLineBox->setBlockHeight(height()); } @@ -949,10 +993,8 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i m_floatingObjects->next(); } else m_floatingObjects->first(); - for (FloatingObject* f = m_floatingObjects->current(); f; f = m_floatingObjects->next()) { - if (f->m_bottom > lastHeight) - lastRootBox()->floats().append(f->m_renderer); - } + for (FloatingObject* f = m_floatingObjects->current(); f; f = m_floatingObjects->next()) + lastRootBox()->floats().append(f->m_renderer); lastFloat = m_floatingObjects->last(); } size_t floatCount = floats.size(); @@ -1236,7 +1278,7 @@ static inline bool shouldCollapseWhiteSpace(const RenderStyle* style, bool isLin static inline bool shouldPreserveNewline(RenderObject* object) { #if ENABLE(SVG) - if (object->isSVGText()) + if (object->isSVGInlineText()) return false; #endif @@ -1403,8 +1445,34 @@ static inline unsigned textWidth(RenderText* text, unsigned from, unsigned len, return font.width(TextRun(text->characters() + from, len, !collapseWhiteSpace, xPos)); } +static void tryHyphenating(RenderText* text, const Font& font, const AtomicString& localeIdentifier, int lastSpace, int pos, int xPos, int availableWidth, bool isFixedPitch, bool collapseWhiteSpace, int lastSpaceWordSpacing, InlineIterator& lineBreak, int nextBreakable, bool& hyphenated) +{ + const AtomicString& hyphenString = text->style()->hyphenString(); + int hyphenWidth = font.width(TextRun(hyphenString.characters(), hyphenString.length())); + + unsigned prefixLength = font.offsetForPosition(TextRun(text->characters() + lastSpace, pos - lastSpace, !collapseWhiteSpace, xPos + lastSpaceWordSpacing), availableWidth - xPos - hyphenWidth - lastSpaceWordSpacing, false); + if (!prefixLength) + return; + + prefixLength = lastHyphenLocation(text->characters() + lastSpace, pos - lastSpace, prefixLength + 1, localeIdentifier); + if (!prefixLength) + return; + +#if !ASSERT_DISABLED + int prefixWidth = hyphenWidth + textWidth(text, lastSpace, prefixLength, font, xPos, isFixedPitch, collapseWhiteSpace) + lastSpaceWordSpacing; + ASSERT(xPos + prefixWidth <= availableWidth); +#else + UNUSED_PARAM(isFixedPitch); +#endif + + lineBreak.obj = text; + lineBreak.pos = lastSpace + prefixLength; + lineBreak.nextBreakablePosition = nextBreakable; + hyphenated = true; +} + InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool firstLine, bool& isLineEmpty, bool& previousLineBrokeCleanly, - EClear* clear) + bool& hyphenated, EClear* clear) { ASSERT(resolver.position().block == this); @@ -1442,6 +1510,8 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool bool prevLineBrokeCleanly = previousLineBrokeCleanly; previousLineBrokeCleanly = false; + hyphenated = false; + bool autoWrapWasEverTrueOnLine = false; bool floatsFitOnLine = true; @@ -1460,7 +1530,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool autoWrapWasEverTrueOnLine = autoWrapWasEverTrueOnLine || autoWrap; #if ENABLE(SVG) - bool preserveNewline = o->isSVGText() ? false : RenderStyle::preserveNewline(currWS); + bool preserveNewline = o->isSVGInlineText() ? false : RenderStyle::preserveNewline(currWS); #else bool preserveNewline = RenderStyle::preserveNewline(currWS); #endif @@ -1615,8 +1685,10 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool int len = strlen - pos; const UChar* str = t->characters(); - const Font& f = t->style(firstLine)->font(); + RenderStyle* style = t->style(firstLine); + const Font& f = style->font(); bool isFixedPitch = f.isFixedPitch(); + bool canHyphenate = style->hyphens() == HyphensAuto && WebCore::canHyphenate(style->hyphenationLocale()); int lastSpace = pos; int wordSpacing = o->style()->wordSpacing(); @@ -1624,7 +1696,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool // Non-zero only when kerning is enabled, in which case we measure words with their trailing // space, then subtract its width. - int wordTrailingSpaceWidth = f.typesettingFeatures() & Kerning ? f.spaceWidth() + wordSpacing : 0; + int wordTrailingSpaceWidth = f.typesettingFeatures() & Kerning ? f.width(TextRun(&space, 1)) + wordSpacing : 0; int wrapW = tmpW + inlineWidth(o, !appliedStartWidth, true); int charWidth = 0; @@ -1686,6 +1758,11 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool len--; lastSpaceWordSpacing = 0; lastSpace = pos; // Cheesy hack to prevent adding in widths of the run twice. + if (style->hyphens() == HyphensNone) { + // Prevent a line break at the soft hyphen by ensuring that betweenWords is false + // in the next iteration. + atStart = true; + } continue; } @@ -1756,6 +1833,11 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool } } if (lineWasTooWide || w + tmpW > width) { + if (canHyphenate && w + tmpW > width) { + tryHyphenating(t, f, style->hyphenationLocale(), lastSpace, pos, w + tmpW - additionalTmpW, width, isFixedPitch, collapseWhiteSpace, lastSpaceWordSpacing, lBreak, nextBreakable, hyphenated); + if (hyphenated) + goto end; + } if (lBreak.obj && shouldPreserveNewline(lBreak.obj) && lBreak.obj->isText() && toRenderText(lBreak.obj)->textLength() && !toRenderText(lBreak.obj)->isWordBreak() && toRenderText(lBreak.obj)->characters()[lBreak.pos] == '\n') { if (!stoppedIgnoringSpaces && pos > 0) { // We need to stop right before the newline and then start up again. @@ -1861,9 +1943,15 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool } // IMPORTANT: pos is > length here! - if (!ignoringSpaces) - tmpW += textWidth(t, lastSpace, pos - lastSpace, f, w + tmpW, isFixedPitch, collapseWhiteSpace) + lastSpaceWordSpacing; + int additionalTmpW = ignoringSpaces ? 0 : textWidth(t, lastSpace, pos - lastSpace, f, w + tmpW, isFixedPitch, collapseWhiteSpace) + lastSpaceWordSpacing; + tmpW += additionalTmpW; tmpW += inlineWidth(o, !appliedStartWidth, true); + + if (canHyphenate && w + tmpW > width) { + tryHyphenating(t, f, style->hyphenationLocale(), lastSpace, pos, w + tmpW - additionalTmpW, width, isFixedPitch, collapseWhiteSpace, lastSpaceWordSpacing, lBreak, nextBreakable, hyphenated); + if (hyphenated) + goto end; + } } else ASSERT_NOT_REACHED(); |