blob: 2ca87e90f607fe5a312de21186f22bd75fd16648 [file] [log] [blame]
/*
* 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/dom/StyleEngine.h"
#include "sky/engine/core/page/Page.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/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(Element* element)
: RenderBoxModelObject(element)
{
}
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()) {
// We can't wait for RenderBoxModelObject::destroy to clear the selection,
// because by then we will have nuked the line boxes.
// FIXME: The FrameSelection should be responsible for this when it
// is notified of DOM mutations.
if (isSelectionBorder())
view()->clearSelection();
// 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());
// We can not use addNodeToRectBasedTestResult to determine if we fully enclose the hit-test area
// because it can only handle rectangular targets.
result.addNodeToRectBasedTestResult(node(), request, locationInContainer);
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)
{
if (result.innerNode())
return;
Node* n = node();
LayoutPoint localPoint(point);
if (n) {
result.setInnerNode(n);
if (!result.innerNonSharedNode())
result.setInnerNonSharedNode(n);
result.setLocalPoint(localPoint);
}
}
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