Adam Barth 21d7575716 Add Paragraph#getPositionForOffset
We'll use this function to position the caret when the user taps a text
input control.

Very little of the code in this patch is actually new. Most of it is
restoring code that we previously removed from the engine. I've made
some small changes to the restored code to handle the lack of a DOM. The
only major change is to RenderObject::createPositionWithAffinity, which
now just returns the values it captures instead of trying to compute a
DOM position.

TextAffinity and TextPosition are lifted from package:flutter. Once this
patch rolls into package:flutter, I'll remove the declarations there.
2016-01-29 23:07:51 -08:00

675 lines
26 KiB
C++

/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include "sky/engine/core/rendering/RenderInline.h"
#include "sky/engine/core/rendering/HitTestResult.h"
#include "sky/engine/core/rendering/InlineTextBox.h"
#include "sky/engine/core/rendering/RenderBlock.h"
#include "sky/engine/core/rendering/RenderGeometryMap.h"
#include "sky/engine/core/rendering/RenderLayer.h"
#include "sky/engine/core/rendering/RenderView.h"
#include "sky/engine/core/rendering/style/StyleInheritedData.h"
#include "sky/engine/platform/geometry/FloatQuad.h"
#include "sky/engine/platform/geometry/Region.h"
#include "sky/engine/platform/geometry/TransformState.h"
#include "sky/engine/platform/graphics/GraphicsContext.h"
namespace blink {
struct SameSizeAsRenderInline : public RenderBoxModelObject {
virtual ~SameSizeAsRenderInline() { }
RenderObjectChildList m_children;
RenderLineBoxList m_lineBoxes;
};
COMPILE_ASSERT(sizeof(RenderInline) == sizeof(SameSizeAsRenderInline), RenderInline_should_stay_small);
RenderInline::RenderInline()
{
}
void RenderInline::willBeDestroyed()
{
// Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will
// properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise.
children()->destroyLeftoverChildren();
if (!documentBeingDestroyed()) {
if (firstLineBox()) {
// If line boxes are contained inside a root, that means we're an inline.
// In that case, we need to remove all the line boxes so that the parent
// lines aren't pointing to deleted children. If the first line box does
// not have a parent that means they are either already disconnected or
// root lines that can just be destroyed without disconnecting.
if (firstLineBox()->parent()) {
for (InlineFlowBox* box = firstLineBox(); box; box = box->nextLineBox())
box->remove();
}
} else if (parent())
parent()->dirtyLinesFromChangedChild(this);
}
m_lineBoxes.deleteLineBoxes();
RenderBoxModelObject::willBeDestroyed();
}
void RenderInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
RenderBoxModelObject::styleDidChange(diff, oldStyle);
if (!alwaysCreateLineBoxes()) {
RenderStyle* newStyle = style();
bool alwaysCreateLineBoxesNew = hasBoxDecorationBackground() || newStyle->hasPadding() || newStyle->hasMargin() || newStyle->hasOutline();
if (oldStyle && alwaysCreateLineBoxesNew) {
dirtyLineBoxes(false);
setNeedsLayout();
}
setAlwaysCreateLineBoxes(alwaysCreateLineBoxesNew);
}
}
void RenderInline::updateAlwaysCreateLineBoxes(bool fullLayout)
{
// Once we have been tainted once, just assume it will happen again. This way effects like hover highlighting that change the
// background color will only cause a layout on the first rollover.
if (alwaysCreateLineBoxes())
return;
RenderStyle* parentStyle = parent()->style();
RenderInline* parentRenderInline = parent()->isRenderInline() ? toRenderInline(parent()) : 0;
bool alwaysCreateLineBoxesNew = (parentRenderInline && parentRenderInline->alwaysCreateLineBoxes())
|| (parentRenderInline && parentStyle->verticalAlign() != BASELINE)
|| style()->verticalAlign() != BASELINE
|| style()->textEmphasisMark() != TextEmphasisMarkNone
|| !parentStyle->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(style()->font().fontMetrics())
|| parentStyle->lineHeight() != style()->lineHeight();
if (alwaysCreateLineBoxesNew) {
if (!fullLayout)
dirtyLineBoxes(false);
setAlwaysCreateLineBoxes();
}
}
LayoutRect RenderInline::localCaretRect(InlineBox* inlineBox, int, LayoutUnit* extraWidthToEndOfLine)
{
if (firstChild()) {
// This condition is possible if the RenderInline is at an editing boundary,
// i.e. the VisiblePosition is:
// <RenderInline editingBoundary=true>|<RenderText> </RenderText></RenderInline>
// FIXME: need to figure out how to make this return a valid rect, note that
// there are no line boxes created in the above case.
return LayoutRect();
}
ASSERT_UNUSED(inlineBox, !inlineBox);
if (extraWidthToEndOfLine)
*extraWidthToEndOfLine = 0;
LayoutRect caretRect = localCaretRectForEmptyElement(borderAndPaddingWidth(), 0);
if (InlineBox* firstBox = firstLineBox())
caretRect.moveBy(roundedLayoutPoint(firstBox->topLeft()));
return caretRect;
}
void RenderInline::addChild(RenderObject* newChild, RenderObject* beforeChild)
{
RenderBoxModelObject::addChild(newChild, beforeChild);
newChild->setNeedsLayoutAndPrefWidthsRecalc();
}
void RenderInline::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, Vector<RenderBox*>& layers)
{
m_lineBoxes.paint(this, paintInfo, paintOffset, layers);
}
template<typename GeneratorContext>
void RenderInline::generateLineBoxRects(GeneratorContext& yield) const
{
if (!alwaysCreateLineBoxes())
generateCulledLineBoxRects(yield, this);
else if (InlineFlowBox* curr = firstLineBox()) {
for (; curr; curr = curr->nextLineBox())
yield(FloatRect(curr->topLeft(), curr->size()));
} else
yield(FloatRect());
}
template<typename GeneratorContext>
void RenderInline::generateCulledLineBoxRects(GeneratorContext& yield, const RenderInline* container) const
{
if (!culledInlineFirstLineBox()) {
yield(FloatRect());
return;
}
for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
if (curr->isFloatingOrOutOfFlowPositioned())
continue;
// We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
// direction (aligned to the root box's baseline).
if (curr->isBox()) {
RenderBox* currBox = toRenderBox(curr);
if (currBox->inlineBoxWrapper()) {
RootInlineBox& rootBox = currBox->inlineBoxWrapper()->root();
int logicalTop = rootBox.logicalTop() + (rootBox.renderer().style(rootBox.isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox.isFirstLineStyle())->font().fontMetrics().ascent());
int logicalHeight = container->style(rootBox.isFirstLineStyle())->font().fontMetrics().height();
yield(FloatRect(currBox->inlineBoxWrapper()->x() - currBox->marginLeft(), logicalTop, (currBox->width() + currBox->marginWidth()).toFloat(), logicalHeight));
}
} else if (curr->isRenderInline()) {
// If the child doesn't need line boxes either, then we can recur.
RenderInline* currInline = toRenderInline(curr);
if (!currInline->alwaysCreateLineBoxes())
currInline->generateCulledLineBoxRects(yield, container);
else {
for (InlineFlowBox* childLine = currInline->firstLineBox(); childLine; childLine = childLine->nextLineBox()) {
RootInlineBox& rootBox = childLine->root();
int logicalTop = rootBox.logicalTop() + (rootBox.renderer().style(rootBox.isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox.isFirstLineStyle())->font().fontMetrics().ascent());
int logicalHeight = container->style(rootBox.isFirstLineStyle())->font().fontMetrics().height();
yield(FloatRect(childLine->x() - childLine->marginLogicalLeft(),
logicalTop,
childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight(),
logicalHeight));
}
}
} else if (curr->isText()) {
RenderText* currText = toRenderText(curr);
for (InlineTextBox* childText = currText->firstTextBox(); childText; childText = childText->nextTextBox()) {
RootInlineBox& rootBox = childText->root();
int logicalTop = rootBox.logicalTop() + (rootBox.renderer().style(rootBox.isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox.isFirstLineStyle())->font().fontMetrics().ascent());
int logicalHeight = container->style(rootBox.isFirstLineStyle())->font().fontMetrics().height();
yield(FloatRect(childText->x(), logicalTop, childText->logicalWidth(), logicalHeight));
}
}
}
}
namespace {
class AbsoluteRectsGeneratorContext {
public:
AbsoluteRectsGeneratorContext(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset)
: m_rects(rects)
, m_accumulatedOffset(accumulatedOffset) { }
void operator()(const FloatRect& rect)
{
IntRect intRect = enclosingIntRect(rect);
intRect.move(m_accumulatedOffset.x(), m_accumulatedOffset.y());
m_rects.append(intRect);
}
private:
Vector<IntRect>& m_rects;
const LayoutPoint& m_accumulatedOffset;
};
class AbsoluteQuadsGeneratorContext {
public:
AbsoluteQuadsGeneratorContext(const RenderInline* renderer, Vector<FloatQuad>& quads)
: m_quads(quads)
, m_geometryMap()
{
m_geometryMap.pushMappingsToAncestor(renderer, 0);
}
void operator()(const FloatRect& rect)
{
m_quads.append(m_geometryMap.absoluteRect(rect));
}
private:
Vector<FloatQuad>& m_quads;
RenderGeometryMap m_geometryMap;
};
} // unnamed namespace
void RenderInline::absoluteQuads(Vector<FloatQuad>& quads) const
{
AbsoluteQuadsGeneratorContext context(this, quads);
generateLineBoxRects(context);
}
LayoutUnit RenderInline::offsetLeft() const
{
LayoutPoint topLeft;
if (InlineBox* firstBox = firstLineBoxIncludingCulling())
topLeft = flooredLayoutPoint(firstBox->topLeft());
return adjustedPositionRelativeToOffsetParent(topLeft).x();
}
LayoutUnit RenderInline::offsetTop() const
{
LayoutPoint topLeft;
if (InlineBox* firstBox = firstLineBoxIncludingCulling())
topLeft = flooredLayoutPoint(firstBox->topLeft());
return adjustedPositionRelativeToOffsetParent(topLeft).y();
}
static LayoutUnit computeMargin(const RenderInline* renderer, const Length& margin)
{
if (margin.isAuto())
return 0;
if (margin.isFixed())
return margin.value();
if (margin.isPercent())
return minimumValueForLength(margin, std::max<LayoutUnit>(0, renderer->containingBlock()->availableLogicalWidth()));
return 0;
}
LayoutUnit RenderInline::marginLeft() const
{
return computeMargin(this, style()->marginLeft());
}
LayoutUnit RenderInline::marginRight() const
{
return computeMargin(this, style()->marginRight());
}
LayoutUnit RenderInline::marginTop() const
{
return computeMargin(this, style()->marginTop());
}
LayoutUnit RenderInline::marginBottom() const
{
return computeMargin(this, style()->marginBottom());
}
LayoutUnit RenderInline::marginStart(const RenderStyle* otherStyle) const
{
return computeMargin(this, style()->marginStartUsing(otherStyle ? otherStyle : style()));
}
LayoutUnit RenderInline::marginEnd(const RenderStyle* otherStyle) const
{
return computeMargin(this, style()->marginEndUsing(otherStyle ? otherStyle : style()));
}
LayoutUnit RenderInline::marginBefore(const RenderStyle* otherStyle) const
{
return computeMargin(this, style()->marginBeforeUsing(otherStyle ? otherStyle : style()));
}
LayoutUnit RenderInline::marginAfter(const RenderStyle* otherStyle) const
{
return computeMargin(this, style()->marginAfterUsing(otherStyle ? otherStyle : style()));
}
const char* RenderInline::renderName() const
{
return "RenderInline";
}
bool RenderInline::nodeAtPoint(const HitTestRequest& request, HitTestResult& result,
const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset)
{
return m_lineBoxes.hitTest(this, request, result, locationInContainer, accumulatedOffset);
}
namespace {
class HitTestCulledInlinesGeneratorContext {
public:
HitTestCulledInlinesGeneratorContext(Region& region, const HitTestLocation& location) : m_intersected(false), m_region(region), m_location(location) { }
void operator()(const FloatRect& rect)
{
m_intersected = m_intersected || m_location.intersects(rect);
m_region.unite(enclosingIntRect(rect));
}
bool intersected() const { return m_intersected; }
private:
bool m_intersected;
Region& m_region;
const HitTestLocation& m_location;
};
} // unnamed namespace
bool RenderInline::hitTestCulledInline(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset)
{
ASSERT(result.isRectBasedTest() && !alwaysCreateLineBoxes());
if (!visibleToHitTestRequest(request))
return false;
HitTestLocation tmpLocation(locationInContainer, -toLayoutSize(accumulatedOffset));
Region regionResult;
HitTestCulledInlinesGeneratorContext context(regionResult, tmpLocation);
generateCulledLineBoxRects(context, this);
if (context.intersected()) {
updateHitTestResult(result, tmpLocation.point());
return regionResult.contains(tmpLocation.boundingBox());
}
return false;
}
PositionWithAffinity RenderInline::positionForPoint(const LayoutPoint& point)
{
// FIXME(sky): Now that we don't have continuations, can this whole function just be the following?
// return containingBlock()->positionForPoint(point);
// FIXME: Does not deal with relative positioned inlines (should it?)
RenderBlock* cb = containingBlock();
if (firstLineBox()) {
// This inline actually has a line box. We must have clicked in the border/padding of one of these boxes. We
// should try to find a result by asking our containing block.
return cb->positionForPoint(point);
}
// Translate the coords from the pre-anonymous block to the post-anonymous block.
return RenderBoxModelObject::positionForPoint(point);
}
namespace {
class LinesBoundingBoxGeneratorContext {
public:
LinesBoundingBoxGeneratorContext(FloatRect& rect) : m_rect(rect) { }
void operator()(const FloatRect& rect)
{
m_rect.uniteIfNonZero(rect);
}
private:
FloatRect& m_rect;
};
} // unnamed namespace
IntRect RenderInline::linesBoundingBox() const
{
if (!alwaysCreateLineBoxes()) {
ASSERT(!firstLineBox());
FloatRect floatResult;
LinesBoundingBoxGeneratorContext context(floatResult);
generateCulledLineBoxRects(context, this);
return enclosingIntRect(floatResult);
}
IntRect result;
// See <rdar://problem/5289721>, for an unknown reason the linked list here is sometimes inconsistent, first is non-zero and last is zero. We have been
// unable to reproduce this at all (and consequently unable to figure ot why this is happening). The assert will hopefully catch the problem in debug
// builds and help us someday figure out why. We also put in a redundant check of lastLineBox() to avoid the crash for now.
ASSERT(!firstLineBox() == !lastLineBox()); // Either both are null or both exist.
if (firstLineBox() && lastLineBox()) {
// Return the width of the minimal left side and the maximal right side.
float logicalLeftSide = 0;
float logicalRightSide = 0;
for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
if (curr == firstLineBox() || curr->logicalLeft() < logicalLeftSide)
logicalLeftSide = curr->logicalLeft();
if (curr == firstLineBox() || curr->logicalRight() > logicalRightSide)
logicalRightSide = curr->logicalRight();
}
float x = logicalLeftSide;
float y = firstLineBox()->y();
float width = logicalRightSide - logicalLeftSide;
float height = lastLineBox()->logicalBottom() - y;
result = enclosingIntRect(FloatRect(x, y, width, height));
}
return result;
}
InlineBox* RenderInline::culledInlineFirstLineBox() const
{
for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
if (curr->isFloatingOrOutOfFlowPositioned())
continue;
// We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
// direction (aligned to the root box's baseline).
if (curr->isBox())
return toRenderBox(curr)->inlineBoxWrapper();
if (curr->isRenderInline()) {
RenderInline* currInline = toRenderInline(curr);
InlineBox* result = currInline->firstLineBoxIncludingCulling();
if (result)
return result;
} else if (curr->isText()) {
RenderText* currText = toRenderText(curr);
if (currText->firstTextBox())
return currText->firstTextBox();
}
}
return 0;
}
InlineBox* RenderInline::culledInlineLastLineBox() const
{
for (RenderObject* curr = lastChild(); curr; curr = curr->previousSibling()) {
if (curr->isFloatingOrOutOfFlowPositioned())
continue;
// We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
// direction (aligned to the root box's baseline).
if (curr->isBox())
return toRenderBox(curr)->inlineBoxWrapper();
if (curr->isRenderInline()) {
RenderInline* currInline = toRenderInline(curr);
InlineBox* result = currInline->lastLineBoxIncludingCulling();
if (result)
return result;
} else if (curr->isText()) {
RenderText* currText = toRenderText(curr);
if (currText->lastTextBox())
return currText->lastTextBox();
}
}
return 0;
}
LayoutRect RenderInline::culledInlineVisualOverflowBoundingBox() const
{
FloatRect floatResult;
LinesBoundingBoxGeneratorContext context(floatResult);
generateCulledLineBoxRects(context, this);
LayoutRect result(enclosingLayoutRect(floatResult));
for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
if (curr->isFloatingOrOutOfFlowPositioned())
continue;
// For overflow we just have to propagate by hand and recompute it all.
if (curr->isBox()) {
RenderBox* currBox = toRenderBox(curr);
if (!currBox->hasSelfPaintingLayer() && currBox->inlineBoxWrapper()) {
LayoutRect logicalRect = currBox->visualOverflowRect();
logicalRect.moveBy(currBox->location());
result.uniteIfNonZero(logicalRect);
}
} else if (curr->isRenderInline()) {
// If the child doesn't need line boxes either, then we can recur.
RenderInline* currInline = toRenderInline(curr);
if (!currInline->alwaysCreateLineBoxes())
result.uniteIfNonZero(currInline->culledInlineVisualOverflowBoundingBox());
else
result.uniteIfNonZero(currInline->linesVisualOverflowBoundingBox());
} else if (curr->isText()) {
// FIXME; Overflow from text boxes is lost. We will need to cache this information in
// InlineTextBoxes.
RenderText* currText = toRenderText(curr);
result.uniteIfNonZero(currText->linesVisualOverflowBoundingBox());
}
}
return result;
}
LayoutRect RenderInline::linesVisualOverflowBoundingBox() const
{
if (!alwaysCreateLineBoxes())
return culledInlineVisualOverflowBoundingBox();
if (!firstLineBox() || !lastLineBox())
return LayoutRect();
// Return the width of the minimal left side and the maximal right side.
LayoutUnit logicalLeftSide = LayoutUnit::max();
LayoutUnit logicalRightSide = LayoutUnit::min();
for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
logicalLeftSide = std::min(logicalLeftSide, curr->logicalLeftVisualOverflow());
logicalRightSide = std::max(logicalRightSide, curr->logicalRightVisualOverflow());
}
RootInlineBox& firstRootBox = firstLineBox()->root();
RootInlineBox& lastRootBox = lastLineBox()->root();
LayoutUnit logicalTop = firstLineBox()->logicalTopVisualOverflow(firstRootBox.lineTop());
LayoutUnit logicalWidth = logicalRightSide - logicalLeftSide;
LayoutUnit logicalHeight = lastLineBox()->logicalBottomVisualOverflow(lastRootBox.lineBottom()) - logicalTop;
LayoutRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight);
return rect;
}
void RenderInline::mapLocalToContainer(const RenderBox* paintInvalidationContainer, TransformState& transformState, MapCoordinatesFlags mode) const
{
bool containerSkipped;
RenderObject* o = container(paintInvalidationContainer, &containerSkipped);
if (!o)
return;
if (mode & ApplyContainerFlip && o->isBox()) {
mode &= ~ApplyContainerFlip;
}
LayoutSize containerOffset = offsetFromContainer(o, roundedLayoutPoint(transformState.mappedPoint()));
bool preserve3D = mode & UseTransforms && (o->style()->preserves3D() || style()->preserves3D());
if (mode & UseTransforms && shouldUseTransformFromContainer(o)) {
TransformationMatrix t;
getTransformFromContainer(o, containerOffset, t);
transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
} else
transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
if (containerSkipped) {
// There can't be a transform between paintInvalidationContainer and o, because transforms create containers, so it should be safe
// to just subtract the delta between the paintInvalidationContainer and o.
LayoutSize containerOffset = paintInvalidationContainer->offsetFromAncestorContainer(o);
transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
return;
}
o->mapLocalToContainer(paintInvalidationContainer, transformState, mode);
}
void RenderInline::updateHitTestResult(HitTestResult& result, const LayoutPoint& point)
{
}
void RenderInline::dirtyLineBoxes(bool fullLayout)
{
if (fullLayout) {
m_lineBoxes.deleteLineBoxes();
return;
}
if (!alwaysCreateLineBoxes()) {
// We have to grovel into our children in order to dirty the appropriate lines.
for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
if (curr->isFloatingOrOutOfFlowPositioned())
continue;
if (curr->isBox() && !curr->needsLayout()) {
RenderBox* currBox = toRenderBox(curr);
if (currBox->inlineBoxWrapper())
currBox->inlineBoxWrapper()->root().markDirty();
} else if (!curr->selfNeedsLayout()) {
if (curr->isRenderInline()) {
RenderInline* currInline = toRenderInline(curr);
for (InlineFlowBox* childLine = currInline->firstLineBox(); childLine; childLine = childLine->nextLineBox())
childLine->root().markDirty();
} else if (curr->isText()) {
RenderText* currText = toRenderText(curr);
for (InlineTextBox* childText = currText->firstTextBox(); childText; childText = childText->nextTextBox())
childText->root().markDirty();
}
}
}
} else
m_lineBoxes.dirtyLineBoxes();
}
void RenderInline::deleteLineBoxTree()
{
m_lineBoxes.deleteLineBoxTree();
}
InlineFlowBox* RenderInline::createInlineFlowBox()
{
return new InlineFlowBox(*this);
}
InlineFlowBox* RenderInline::createAndAppendInlineFlowBox()
{
setAlwaysCreateLineBoxes();
InlineFlowBox* flowBox = createInlineFlowBox();
m_lineBoxes.appendLineBox(flowBox);
return flowBox;
}
LayoutUnit RenderInline::lineHeight(bool firstLine, LineDirectionMode /*direction*/, LinePositionMode /*linePositionMode*/) const
{
return style()->computedLineHeight();
}
int RenderInline::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
{
ASSERT(linePositionMode == PositionOnContainingLine);
const FontMetrics& fontMetrics = style(firstLine)->fontMetrics();
return fontMetrics.ascent(baselineType) + (lineHeight(firstLine, direction, linePositionMode) - fontMetrics.height()) / 2;
}
namespace {
class AbsoluteRectsIgnoringEmptyRectsGeneratorContext : public AbsoluteRectsGeneratorContext {
public:
AbsoluteRectsIgnoringEmptyRectsGeneratorContext(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset)
: AbsoluteRectsGeneratorContext(rects, accumulatedOffset) { }
void operator()(const FloatRect& rect)
{
if (!rect.isEmpty())
AbsoluteRectsGeneratorContext::operator()(rect);
}
};
} // unnamed namespace
void RenderInline::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderBox* paintContainer) const
{
AbsoluteRectsIgnoringEmptyRectsGeneratorContext context(rects, additionalOffset);
generateLineBoxRects(context);
addChildFocusRingRects(rects, additionalOffset, paintContainer);
}
} // namespace blink