Commit 28434077 authored by antti@apple.com's avatar antti@apple.com
Browse files

Multiple runs per line on simple line path

https://bugs.webkit.org/show_bug.cgi?id=123446

Reviewed by Andreas Kling.

By allowing multiple runs per line we can support text flows with consecutive whitespaces in the middle.

* rendering/SimpleLineLayout.cpp:
(WebCore::SimpleLineLayout::canUseFor):
        
    Remove space test.
    The improved test coverage found a few more cases that we need to disallow.

(WebCore::SimpleLineLayout::adjustRunOffsets):
            
    Round the run positions and widths so they match line boxes.
    Adjust for text-align.

(WebCore::SimpleLineLayout::create):
        
    Split lines with consecutive spaces into runs.

* rendering/SimpleLineLayout.h:
(WebCore::SimpleLineLayout::Run::Run):
* rendering/SimpleLineLayoutFunctions.cpp:
(WebCore::SimpleLineLayout::hitTestFlow):
(WebCore::SimpleLineLayout::collectFlowOverflow):
(WebCore::SimpleLineLayout::computeTextBoundingBox):
* rendering/SimpleLineLayoutResolver.h:
(WebCore::SimpleLineLayout::RunResolver::Iterator::resolver):
(WebCore::SimpleLineLayout::RunResolver::Iterator::lineIndex):
(WebCore::SimpleLineLayout::RunResolver::Run::Run):
(WebCore::SimpleLineLayout::RunResolver::Run::rect):
(WebCore::SimpleLineLayout::RunResolver::Run::baseline):
(WebCore::SimpleLineLayout::RunResolver::Run::text):
(WebCore::SimpleLineLayout::RunResolver::Run::lineIndex):
(WebCore::SimpleLineLayout::RunResolver::Iterator::Iterator):
(WebCore::SimpleLineLayout::RunResolver::Iterator::operator++):
        
    Removed unnecessary operators.

(WebCore::SimpleLineLayout::RunResolver::Iterator::operator==):
(WebCore::SimpleLineLayout::RunResolver::Iterator::operator!=):
(WebCore::SimpleLineLayout::RunResolver::Iterator::operator*):
(WebCore::SimpleLineLayout::RunResolver::Iterator::simpleRun):
(WebCore::SimpleLineLayout::RunResolver::RunResolver):
(WebCore::SimpleLineLayout::RunResolver::begin):
(WebCore::SimpleLineLayout::RunResolver::end):
        
    Resolver -> RunResolver

(WebCore::SimpleLineLayout::LineResolver::Iterator::Iterator):
(WebCore::SimpleLineLayout::LineResolver::Iterator::operator++):
(WebCore::SimpleLineLayout::LineResolver::Iterator::operator==):
(WebCore::SimpleLineLayout::LineResolver::Iterator::operator!=):
(WebCore::SimpleLineLayout::LineResolver::Iterator::operator*):
(WebCore::SimpleLineLayout::LineResolver::LineResolver):
(WebCore::SimpleLineLayout::LineResolver::begin):
(WebCore::SimpleLineLayout::LineResolver::end):
        
    Add LineResolver around RunResolver. It resolves the line rectangles.

(WebCore::SimpleLineLayout::runResolver):
(WebCore::SimpleLineLayout::lineResolver):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@158196 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 2a0b24cf
2013-10-29 Antti Koivisto <antti@apple.com>
Multiple runs per line on simple line path
https://bugs.webkit.org/show_bug.cgi?id=123446
Reviewed by Andreas Kling.
By allowing multiple runs per line we can support text flows with consecutive whitespaces in the middle.
* rendering/SimpleLineLayout.cpp:
(WebCore::SimpleLineLayout::canUseFor):
Remove space test.
The improved test coverage found a few more cases that we need to disallow.
(WebCore::SimpleLineLayout::adjustRunOffsets):
Round the run positions and widths so they match line boxes.
Adjust for text-align.
(WebCore::SimpleLineLayout::create):
Split lines with consecutive spaces into runs.
* rendering/SimpleLineLayout.h:
(WebCore::SimpleLineLayout::Run::Run):
* rendering/SimpleLineLayoutFunctions.cpp:
(WebCore::SimpleLineLayout::hitTestFlow):
(WebCore::SimpleLineLayout::collectFlowOverflow):
(WebCore::SimpleLineLayout::computeTextBoundingBox):
* rendering/SimpleLineLayoutResolver.h:
(WebCore::SimpleLineLayout::RunResolver::Iterator::resolver):
(WebCore::SimpleLineLayout::RunResolver::Iterator::lineIndex):
(WebCore::SimpleLineLayout::RunResolver::Run::Run):
(WebCore::SimpleLineLayout::RunResolver::Run::rect):
(WebCore::SimpleLineLayout::RunResolver::Run::baseline):
(WebCore::SimpleLineLayout::RunResolver::Run::text):
(WebCore::SimpleLineLayout::RunResolver::Run::lineIndex):
(WebCore::SimpleLineLayout::RunResolver::Iterator::Iterator):
(WebCore::SimpleLineLayout::RunResolver::Iterator::operator++):
Removed unnecessary operators.
(WebCore::SimpleLineLayout::RunResolver::Iterator::operator==):
(WebCore::SimpleLineLayout::RunResolver::Iterator::operator!=):
(WebCore::SimpleLineLayout::RunResolver::Iterator::operator*):
(WebCore::SimpleLineLayout::RunResolver::Iterator::simpleRun):
(WebCore::SimpleLineLayout::RunResolver::RunResolver):
(WebCore::SimpleLineLayout::RunResolver::begin):
(WebCore::SimpleLineLayout::RunResolver::end):
Resolver -> RunResolver
(WebCore::SimpleLineLayout::LineResolver::Iterator::Iterator):
(WebCore::SimpleLineLayout::LineResolver::Iterator::operator++):
(WebCore::SimpleLineLayout::LineResolver::Iterator::operator==):
(WebCore::SimpleLineLayout::LineResolver::Iterator::operator!=):
(WebCore::SimpleLineLayout::LineResolver::Iterator::operator*):
(WebCore::SimpleLineLayout::LineResolver::LineResolver):
(WebCore::SimpleLineLayout::LineResolver::begin):
(WebCore::SimpleLineLayout::LineResolver::end):
Add LineResolver around RunResolver. It resolves the line rectangles.
(WebCore::SimpleLineLayout::runResolver):
(WebCore::SimpleLineLayout::lineResolver):
2013-10-29 Chris Fleizach <cfleizach@apple.com>
AX: elements with explicit tabindex should expose AXFocused as writable, since mouse clicks can focus it
......
......@@ -78,6 +78,8 @@ bool canUseFor(const RenderBlockFlow& flow)
return false;
if (flow.isRubyText() || flow.isRubyBase())
return false;
if (flow.parent()->isDeprecatedFlexibleBox())
return false;
// These tests only works during layout. Outside layout this function may give false positives.
if (flow.view().layoutState()) {
#if ENABLE(CSS_SHAPES)
......@@ -154,18 +156,10 @@ bool canUseFor(const RenderBlockFlow& flow)
return false;
unsigned length = textRenderer.textLength();
unsigned consecutiveSpaceCount = 0;
for (unsigned i = 0; i < length; ++i) {
// This rejects anything with more than one consecutive whitespace, except at the beginning or end.
// This is because we don't currently do subruns within lines. Fixing this would improve coverage significantly.
UChar character = textRenderer.characterAt(i);
if (isWhitespace(character)) {
++consecutiveSpaceCount;
if (character == ' ')
continue;
}
if (consecutiveSpaceCount != i && consecutiveSpaceCount > 1)
return false;
consecutiveSpaceCount = 0;
// These would be easy to support.
if (character == noBreakSpace)
......@@ -173,14 +167,12 @@ bool canUseFor(const RenderBlockFlow& flow)
if (character == softHyphen)
return false;
static const UChar lowestRTLCharacter = 0x590;
if (character >= lowestRTLCharacter) {
UCharDirection direction = u_charDirection(character);
if (direction == U_RIGHT_TO_LEFT || direction == U_RIGHT_TO_LEFT_ARABIC
|| direction == U_RIGHT_TO_LEFT_EMBEDDING || direction == U_RIGHT_TO_LEFT_OVERRIDE
|| direction == U_LEFT_TO_RIGHT_EMBEDDING || direction == U_LEFT_TO_RIGHT_OVERRIDE)
return false;
}
UCharDirection direction = u_charDirection(character);
if (direction == U_RIGHT_TO_LEFT || direction == U_RIGHT_TO_LEFT_ARABIC
|| direction == U_RIGHT_TO_LEFT_EMBEDDING || direction == U_RIGHT_TO_LEFT_OVERRIDE
|| direction == U_LEFT_TO_RIGHT_EMBEDDING || direction == U_LEFT_TO_RIGHT_OVERRIDE
|| direction == U_POP_DIRECTIONAL_FORMAT || direction == U_BOUNDARY_NEUTRAL)
return false;
if (!primaryFontData.glyphForCharacter(character))
return false;
......@@ -231,6 +223,17 @@ static float computeLineLeft(ETextAlign textAlign, float remainingWidth)
return 0;
}
static void adjustRunOffsets(Vector<Run, 4>& lineRuns, ETextAlign textAlign, float lineWidth, float availableWidth)
{
float lineLeft = computeLineLeft(textAlign, availableWidth - lineWidth);
for (unsigned i = 0; i < lineRuns.size(); ++i) {
float adjustedLeft = floor(lineLeft + lineRuns[i].left);
float adjustedRight = ceil(lineLeft + lineRuns[i].left + lineRuns[i].width);
lineRuns[i].left = adjustedLeft;
lineRuns[i].width = adjustedRight - adjustedLeft;
}
}
std::unique_ptr<Layout> create(RenderBlockFlow& flow)
{
auto layout = std::make_unique<Layout>();
......@@ -251,54 +254,73 @@ std::unique_ptr<Layout> create(RenderBlockFlow& flow)
while (lineEndOffset < textLength) {
lineEndOffset = skipWhitespaces(textRenderer, lineEndOffset, textLength);
unsigned lineStartOffset = lineEndOffset;
unsigned runEndOffset = lineEndOffset;
unsigned wordEndOffset = lineEndOffset;
LineWidth lineWidth(flow, false, DoNotIndentText);
while (runEndOffset < textLength) {
ASSERT(!isWhitespace(textRenderer.characterAt(runEndOffset)));
bool previousWasSpaceBetweenRuns = runEndOffset > lineStartOffset && isWhitespace(textRenderer.characterAt(runEndOffset - 1));
unsigned runStartOffset = previousWasSpaceBetweenRuns ? runEndOffset - 1 : runEndOffset;
Vector<Run, 4> lineRuns;
lineRuns.uncheckedAppend(Run(lineStartOffset, 0));
while (wordEndOffset < textLength) {
ASSERT(!isWhitespace(textRenderer.characterAt(wordEndOffset)));
bool previousWasSpaceBetweenWords = wordEndOffset > lineStartOffset && isWhitespace(textRenderer.characterAt(wordEndOffset - 1));
unsigned wordStartOffset = previousWasSpaceBetweenWords ? wordEndOffset - 1 : wordEndOffset;
++runEndOffset;
while (runEndOffset < textLength) {
if (runEndOffset > lineStartOffset && isBreakable(lineBreakIterator, runEndOffset, nextBreakable, false))
++wordEndOffset;
while (wordEndOffset < textLength) {
if (wordEndOffset > lineStartOffset && isBreakable(lineBreakIterator, wordEndOffset, nextBreakable, false))
break;
++runEndOffset;
++wordEndOffset;
}
unsigned runLength = runEndOffset - runStartOffset;
bool includeEndSpace = runEndOffset < textLength && textRenderer.characterAt(runEndOffset) == ' ';
unsigned wordLength = wordEndOffset - wordStartOffset;
bool includeEndSpace = wordEndOffset < textLength && textRenderer.characterAt(wordEndOffset) == ' ';
float wordWidth;
if (includeEndSpace)
wordWidth = textWidth(textRenderer, runStartOffset, runLength + 1, lineWidth.committedWidth(), style) - wordTrailingSpaceWidth;
wordWidth = textWidth(textRenderer, wordStartOffset, wordLength + 1, lineWidth.committedWidth(), style) - wordTrailingSpaceWidth;
else
wordWidth = textWidth(textRenderer, runStartOffset, runLength, lineWidth.committedWidth(), style);
wordWidth = textWidth(textRenderer, wordStartOffset, wordLength, lineWidth.committedWidth(), style);
lineWidth.addUncommittedWidth(wordWidth);
if (!lineWidth.fitsOnLine()) {
if (!lineWidth.committedWidth()) {
lineWidth.commit();
lineEndOffset = runEndOffset;
}
// Move to the next line if the current one is full and we have something on it.
if (!lineWidth.fitsOnLine() && lineWidth.committedWidth())
break;
if (wordStartOffset > lineEndOffset) {
// There were more than one consecutive whitespace.
ASSERT(previousWasSpaceBetweenWords);
// Include space to the end of the previous run.
lineRuns.last().textLength++;
lineRuns.last().width += wordTrailingSpaceWidth;
// Start a new run on the same line.
float previousRight = lineRuns.last().left + lineRuns.last().width;
lineRuns.append(Run(wordStartOffset + 1, previousRight));
}
lineWidth.commit();
lineEndOffset = runEndOffset;
runEndOffset = skipWhitespaces(textRenderer, runEndOffset, textLength);
lineRuns.last().width = lineWidth.committedWidth() - lineRuns.last().left;
lineRuns.last().textLength = wordEndOffset - lineRuns.last().textOffset;
lineEndOffset = wordEndOffset;
wordEndOffset = skipWhitespaces(textRenderer, wordEndOffset, textLength);
if (!lineWidth.fitsOnLine()) {
// The first run on the line overflows.
ASSERT(lineRuns.size() == 1);
break;
}
}
if (lineStartOffset == lineEndOffset)
continue;
float alignedLeft = computeLineLeft(textAlign, lineWidth.availableWidth() - lineWidth.committedWidth());
float alignedRight = alignedLeft + lineWidth.committedWidth();
adjustRunOffsets(lineRuns, textAlign, lineWidth.committedWidth(), lineWidth.availableWidth());
Run run;
run.textOffset = lineStartOffset;
run.textLength = lineEndOffset - lineStartOffset;
run.left = floor(alignedLeft);
run.width = ceil(alignedRight) - run.left;
for (unsigned i = 0; i < lineRuns.size(); ++i)
layout->runs.append(lineRuns[i]);
layout->runs.append(run);
layout->runs.last().isEndOfLine = true;
layout->lineCount++;
}
......
......@@ -38,8 +38,17 @@ namespace SimpleLineLayout {
bool canUseFor(const RenderBlockFlow&);
struct Run {
Run(unsigned textOffset, float left)
: textOffset(textOffset)
, textLength(0)
, isEndOfLine(false)
, left(left)
, width(0)
{ }
unsigned textOffset;
unsigned textLength;
unsigned textLength : 31;
unsigned isEndOfLine : 1;
float left;
float width;
};
......
......@@ -85,8 +85,7 @@ bool hitTestFlow(const RenderBlockFlow& flow, const Layout& layout, const HitTes
auto resolver = lineResolver(flow, layout);
for (auto it = resolver.begin(), end = resolver.end(); it != end; ++it) {
auto line = *it;
auto lineRect = line.rect();
auto lineRect = *it;
lineRect.moveBy(accumulatedOffset);
if (!locationInContainer.intersects(lineRect))
continue;
......@@ -102,8 +101,7 @@ void collectFlowOverflow(RenderBlockFlow& flow, const Layout& layout)
{
auto resolver = lineResolver(flow, layout);
for (auto it = resolver.begin(), end = resolver.end(); it != end; ++it) {
auto line = *it;
auto rect = line.rect();
auto rect = *it;
flow.addLayoutOverflow(rect);
flow.addVisualOverflow(rect);
}
......@@ -116,21 +114,23 @@ IntRect computeTextBoundingBox(const RenderText& textRenderer, const Layout& lay
auto end = resolver.end();
if (it == end)
return IntRect();
auto firstLineRect = (*it).rect();
auto firstLineRect = *it;
float left = firstLineRect.x();
float right = firstLineRect.maxX();
float bottom = firstLineRect.maxY();
for (++it; it != end; ++it) {
auto line = *it;
auto rect = line.rect();
auto rect = *it;
if (rect.x() < left)
left = rect.x();
if (rect.maxX() > right)
right = rect.maxX();
if (rect.maxY() > bottom)
bottom = rect.maxY();
}
float x = firstLineRect.x();
float y = firstLineRect.y();
float width = right - left;
float height = (*resolver.last()).rect().maxY() - y;
float height = bottom - y;
return enclosingIntRect(FloatRect(x, y, width, height));
}
......
......@@ -36,47 +36,48 @@
namespace WebCore {
namespace SimpleLineLayout {
class Resolver {
class RunResolver {
public:
class Iterator;
class Run {
public:
Run(const Resolver&, unsigned lineIndex);
explicit Run(const Iterator&);
LayoutRect rect() const;
LayoutPoint baseline() const;
String text() const;
unsigned lineIndex() const;
private:
const Resolver& m_resolver;
unsigned m_lineIndex;
const Iterator& m_iterator;
};
class Iterator {
public:
Iterator(const Resolver&, unsigned lineIndex);
Iterator(const RunResolver&, unsigned lineIndex);
Iterator& operator++();
Iterator& operator--();
bool operator==(const Iterator&) const;
bool operator!=(const Iterator&) const;
Run operator*() const;
const RunResolver& resolver() const { return m_resolver; }
const SimpleLineLayout::Run& simpleRun() const;
unsigned lineIndex() const { return m_lineIndex; }
private:
const Resolver& m_resolver;
const RunResolver& m_resolver;
unsigned m_runIndex;
unsigned m_lineIndex;
};
Resolver(const RenderBlockFlow&, const Layout&);
unsigned size() const;
RunResolver(const RenderBlockFlow&, const Layout&);
Iterator begin() const;
Iterator end() const;
Iterator last() const;
Iterator operator[](unsigned) const;
private:
const Layout& m_layout;
......@@ -88,73 +89,110 @@ private:
const LayoutPoint m_contentOffset;
};
Resolver runResolver(const RenderBlockFlow&, const Layout&);
Resolver lineResolver(const RenderBlockFlow&, const Layout&);
class LineResolver {
public:
class Iterator;
inline Resolver::Run::Run(const Resolver& resolver, unsigned lineIndex)
: m_resolver(resolver)
, m_lineIndex(lineIndex)
class Iterator {
public:
explicit Iterator(RunResolver::Iterator);
Iterator& operator++();
bool operator==(const Iterator&) const;
bool operator!=(const Iterator&) const;
const LayoutRect operator*() const;
private:
RunResolver::Iterator m_runIterator;
LayoutRect m_rect;
};
LineResolver(const RenderBlockFlow&, const Layout&);
Iterator begin() const;
Iterator end() const;
private:
RunResolver m_runResolver;
};
RunResolver runResolver(const RenderBlockFlow&, const Layout&);
LineResolver lineResolver(const RenderBlockFlow&, const Layout&);
inline RunResolver::Run::Run(const Iterator& iterator)
: m_iterator(iterator)
{
}
inline LayoutRect Resolver::Run::rect() const
inline LayoutRect RunResolver::Run::rect() const
{
auto& run = m_resolver.m_layout.runs[m_lineIndex];
auto& resolver = m_iterator.resolver();
auto& run = m_iterator.simpleRun();
LayoutPoint linePosition(run.left, m_resolver.m_lineHeight * m_lineIndex + m_resolver.m_baseline - m_resolver.m_ascent);
LayoutSize lineSize(run.width, m_resolver.m_ascent + m_resolver.m_descent);
return LayoutRect(linePosition + m_resolver.m_contentOffset, lineSize);
LayoutPoint linePosition(run.left, resolver.m_lineHeight * m_iterator.lineIndex() + resolver.m_baseline - resolver.m_ascent);
LayoutSize lineSize(run.width, resolver.m_ascent + resolver.m_descent);
return LayoutRect(linePosition + resolver.m_contentOffset, lineSize);
}
inline LayoutPoint Resolver::Run::baseline() const
inline LayoutPoint RunResolver::Run::baseline() const
{
auto& run = m_resolver.m_layout.runs[m_lineIndex];
auto& resolver = m_iterator.resolver();
auto& run = m_iterator.simpleRun();
float baselineY = m_resolver.m_lineHeight * m_lineIndex + m_resolver.m_baseline;
return LayoutPoint(run.left, baselineY) + m_resolver.m_contentOffset;
float baselineY = resolver.m_lineHeight * m_iterator.lineIndex() + resolver.m_baseline;
return LayoutPoint(run.left, baselineY) + resolver.m_contentOffset;
}
inline String Resolver::Run::text() const
inline String RunResolver::Run::text() const
{
auto& run = m_resolver.m_layout.runs[m_lineIndex];
return m_resolver.m_string.substringSharingImpl(run.textOffset, run.textLength);
auto& resolver = m_iterator.resolver();
auto& run = m_iterator.simpleRun();
return resolver.m_string.substringSharingImpl(run.textOffset, run.textLength);
}
inline Resolver::Iterator::Iterator(const Resolver& resolver, unsigned lineIndex)
: m_resolver(resolver)
, m_lineIndex(lineIndex)
inline unsigned RunResolver::Run::lineIndex() const
{
return m_iterator.lineIndex();
}
inline Resolver::Iterator& Resolver::Iterator::operator++()
inline RunResolver::Iterator::Iterator(const RunResolver& resolver, unsigned runIndex)
: m_resolver(resolver)
, m_runIndex(runIndex)
, m_lineIndex(0)
{
++m_lineIndex;
return *this;
}
inline Resolver::Iterator& Resolver::Iterator::operator--()
inline RunResolver::Iterator& RunResolver::Iterator::operator++()
{
--m_lineIndex;
if (simpleRun().isEndOfLine)
++m_lineIndex;
++m_runIndex;
return *this;
}
inline bool Resolver::Iterator::operator==(const Iterator& other) const
inline bool RunResolver::Iterator::operator==(const Iterator& other) const
{
ASSERT(&m_resolver == &other.m_resolver);
return m_lineIndex == other.m_lineIndex;
return m_runIndex == other.m_runIndex;
}
inline bool Resolver::Iterator::operator!=(const Iterator& other) const
inline bool RunResolver::Iterator::operator!=(const Iterator& other) const
{
return !(*this == other);
}
inline Resolver::Run Resolver::Iterator::operator*() const
inline RunResolver::Run RunResolver::Iterator::operator*() const
{
return Run(m_resolver, m_lineIndex);
return Run(*this);
}
inline Resolver::Resolver(const RenderBlockFlow& flow, const Layout& layout)
inline const SimpleLineLayout::Run& RunResolver::Iterator::simpleRun() const
{
return m_resolver.m_layout.runs[m_runIndex];
}
inline RunResolver::RunResolver(const RenderBlockFlow& flow, const Layout& layout)
: m_layout(layout)
, m_string(toRenderText(*flow.firstChild()).text())
, m_lineHeight(lineHeightFromFlow(flow))
......@@ -165,41 +203,73 @@ inline Resolver::Resolver(const RenderBlockFlow& flow, const Layout& layout)
{
}
inline unsigned Resolver::size() const
inline RunResolver::Iterator RunResolver::begin() const
{
return m_layout.runs.size();
return Iterator(*this, 0);
}
inline Resolver::Iterator Resolver::begin() const
inline RunResolver::Iterator RunResolver::end() const
{
return Iterator(*this, 0);
return Iterator(*this, m_layout.runs.size());
}
inline LineResolver::Iterator::Iterator(RunResolver::Iterator runIterator)
: m_runIterator(runIterator)
{
}
inline LineResolver::Iterator& LineResolver::Iterator::operator++()
{
unsigned previousLine = m_runIterator.lineIndex();
while ((++m_runIterator).lineIndex() == previousLine) { }
return *this;
}
inline bool LineResolver::Iterator::operator==(const Iterator& other) const
{
return m_runIterator == other.m_runIterator;
}
inline bool LineResolver::Iterator::operator!=(const Iterator& other) const
{
return m_runIterator != other.m_runIterator;
}
inline const LayoutRect LineResolver::Iterator::operator*() const
{
unsigned currentLine = m_runIterator.lineIndex();
auto it = m_runIterator;
LayoutRect rect = (*it).rect();
while ((++it).lineIndex() == currentLine)
rect.unite((*it).rect());
return rect;
}
inline Resolver::Iterator Resolver::end() const
inline LineResolver::LineResolver(const RenderBlockFlow& flow, const Layout& layout)
: m_runResolver(flow, layout)
{
return Iterator(*this, size());
}
inline Resolver::Iterator Resolver::last() const
inline LineResolver::Iterator LineResolver::begin() const
{
ASSERT(size());
return Iterator(*this, size() - 1);
return Iterator(m_runResolver.begin());
}
inline Resolver::Iterator Resolver::operator[](unsigned index) const
inline LineResolver::Iterator LineResolver::end() const
{
ASSERT(index < size());
return Iterator(*this, index);
return Iterator(m_runResolver.end());
}
inline Resolver runResolver(const RenderBlockFlow& flow, const Layout& layout)
inline RunResolver runResolver(const RenderBlockFlow& flow, const Layout& layout)
{
return Resolver(flow, layout);