summaryrefslogtreecommitdiffstats
path: root/WebCore/rendering/RenderBlockLineLayout.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/rendering/RenderBlockLineLayout.cpp')
-rw-r--r--WebCore/rendering/RenderBlockLineLayout.cpp174
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();