mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Merge most of it into RenderBox. Only RenderBoxes can have layers now. This also meant a bit of code could be cleaned up since some virtuals (e.g. updateFromStyle) are no longer needed since they're only called on RenderBoxes. collectSelfPaintingLayers is the only bit that's moved into RenderBoxModelObject instead of RenderBox. That's because we need to be able to recurse down into RenderInlines since they may contain RenderBoxes that have selfPaintingLayers. R=abarth@chromium.org Review URL: https://codereview.chromium.org/953673002
706 lines
27 KiB
C++
706 lines
27 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/config.h"
|
|
#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)
|
|
{
|
|
}
|
|
|
|
RenderInline* RenderInline::createAnonymous(Document* document)
|
|
{
|
|
RenderInline* renderer = new RenderInline(0);
|
|
renderer->setDocumentForAnonymous(document);
|
|
return renderer;
|
|
}
|
|
|
|
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
|