blob: 02286c733ec291a86b1ed9ed0a9ad331ad5299be [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 "core/layout/LayoutInline.h"
#include "core/dom/Fullscreen.h"
#include "core/dom/StyleEngine.h"
#include "core/layout/HitTestResult.h"
#include "core/layout/LayoutBlock.h"
#include "core/layout/LayoutFullScreen.h"
#include "core/layout/LayoutGeometryMap.h"
#include "core/layout/LayoutTheme.h"
#include "core/layout/LayoutView.h"
#include "core/layout/api/LineLayoutBoxModel.h"
#include "core/layout/line/InlineTextBox.h"
#include "core/paint/BoxPainter.h"
#include "core/paint/InlinePainter.h"
#include "core/paint/ObjectPainter.h"
#include "core/paint/PaintLayer.h"
#include "core/style/StyleInheritedData.h"
#include "platform/geometry/FloatQuad.h"
#include "platform/geometry/Region.h"
#include "platform/geometry/TransformState.h"
namespace blink {
struct SameSizeAsLayoutInline : public LayoutBoxModelObject {
virtual ~SameSizeAsLayoutInline() {}
LayoutObjectChildList m_children;
LineBoxList m_lineBoxes;
};
static_assert(sizeof(LayoutInline) == sizeof(SameSizeAsLayoutInline),
"LayoutInline should stay small");
LayoutInline::LayoutInline(Element* element) : LayoutBoxModelObject(element) {
setChildrenInline(true);
}
void LayoutInline::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();
// Destroy our continuation before anything other than anonymous children.
// The reason we don't destroy it before anonymous children is that they may
// have continuations of their own that are anonymous children of our
// continuation.
LayoutBoxModelObject* continuation = this->continuation();
if (continuation) {
continuation->destroy();
setContinuation(nullptr);
}
if (!documentBeingDestroyed()) {
if (firstLineBox()) {
// We can't wait for LayoutBoxModelObject::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();
LayoutBoxModelObject::willBeDestroyed();
}
LayoutInline* LayoutInline::inlineElementContinuation() const {
LayoutBoxModelObject* continuation = this->continuation();
if (!continuation || continuation->isInline())
return toLayoutInline(continuation);
return toLayoutBlockFlow(continuation)->inlineElementContinuation();
}
void LayoutInline::updateFromStyle() {
LayoutBoxModelObject::updateFromStyle();
// FIXME: Is this still needed. Was needed for run-ins, since run-in is
// considered a block display type.
setInline(true);
// FIXME: Support transforms and reflections on inline flows someday.
setHasTransformRelatedProperty(false);
setHasReflection(false);
}
static LayoutObject* inFlowPositionedInlineAncestor(LayoutObject* p) {
while (p && p->isLayoutInline()) {
if (p->isInFlowPositioned())
return p;
p = p->parent();
}
return nullptr;
}
static void updateInFlowPositionOfAnonymousBlockContinuations(
LayoutObject* block,
const ComputedStyle& newStyle,
const ComputedStyle& oldStyle,
LayoutObject* containingBlockOfEndOfContinuation) {
for (; block && block != containingBlockOfEndOfContinuation &&
block->isAnonymousBlock();
block = block->nextSibling()) {
LayoutBlockFlow* blockFlow = toLayoutBlockFlow(block);
if (!blockFlow->isAnonymousBlockContinuation())
continue;
// If we are no longer in-flow positioned but our descendant block(s) still
// have an in-flow positioned ancestor then their containing anonymous block
// should keep its in-flow positioning.
if (oldStyle.hasInFlowPosition() &&
inFlowPositionedInlineAncestor(blockFlow->inlineElementContinuation()))
continue;
RefPtr<ComputedStyle> newBlockStyle =
ComputedStyle::clone(block->styleRef());
newBlockStyle->setPosition(newStyle.position());
block->setStyle(newBlockStyle);
}
}
void LayoutInline::styleDidChange(StyleDifference diff,
const ComputedStyle* oldStyle) {
LayoutBoxModelObject::styleDidChange(diff, oldStyle);
// Ensure that all of the split inlines pick up the new style. We only do this
// if we're an inline, since we don't want to propagate a block's style to the
// other inlines. e.g., <font>foo <h4>goo</h4> moo</font>. The <font> inlines
// before and after the block share the same style, but the block doesn't need
// to pass its style on to anyone else.
const ComputedStyle& newStyle = styleRef();
LayoutInline* continuation = inlineElementContinuation();
LayoutInline* endOfContinuation = nullptr;
for (LayoutInline* currCont = continuation; currCont;
currCont = currCont->inlineElementContinuation()) {
LayoutBoxModelObject* nextCont = currCont->continuation();
currCont->setContinuation(nullptr);
currCont->setStyle(mutableStyle());
currCont->setContinuation(nextCont);
endOfContinuation = currCont;
}
if (continuation && oldStyle) {
ASSERT(endOfContinuation);
LayoutObject* block = containingBlock()->nextSibling();
// If an inline's in-flow positioning has changed then any descendant blocks
// will need to change their styles accordingly.
if (block && block->isAnonymousBlock() &&
newStyle.position() != oldStyle->position() &&
(newStyle.hasInFlowPosition() || oldStyle->hasInFlowPosition()))
updateInFlowPositionOfAnonymousBlockContinuations(
block, newStyle, *oldStyle, endOfContinuation->containingBlock());
}
if (!alwaysCreateLineBoxes()) {
bool alwaysCreateLineBoxesNew =
hasSelfPaintingLayer() || hasBoxDecorationBackground() ||
newStyle.hasPadding() || newStyle.hasMargin() || newStyle.hasOutline();
if (oldStyle && alwaysCreateLineBoxesNew) {
dirtyLineBoxes(false);
setNeedsLayoutAndFullPaintInvalidation(
LayoutInvalidationReason::StyleChange);
}
setAlwaysCreateLineBoxes(alwaysCreateLineBoxesNew);
}
// If we are changing to/from static, we need to reposition
// out-of-flow positioned descendants.
if (oldStyle && oldStyle->position() != newStyle.position() &&
(newStyle.position() == StaticPosition ||
oldStyle->position() == StaticPosition)) {
LayoutBlock* absContainingBlock = nullptr;
if (oldStyle->position() == StaticPosition) {
absContainingBlock = containingBlockForAbsolutePosition();
} else {
// When position was not static, containingBlockForAbsolutePosition
// for our children is our existing containingBlock.
absContainingBlock = containingBlock();
}
if (absContainingBlock)
absContainingBlock->removePositionedObjects(this, NewContainingBlock);
}
propagateStyleToAnonymousChildren();
}
void LayoutInline::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;
const ComputedStyle& parentStyle = parent()->styleRef();
LayoutInline* parentLayoutInline =
parent()->isLayoutInline() ? toLayoutInline(parent()) : 0;
bool checkFonts = document().inNoQuirksMode();
bool alwaysCreateLineBoxesNew =
(parentLayoutInline && parentLayoutInline->alwaysCreateLineBoxes()) ||
(parentLayoutInline &&
parentStyle.verticalAlign() != EVerticalAlign::Baseline) ||
style()->verticalAlign() != EVerticalAlign::Baseline ||
style()->getTextEmphasisMark() != TextEmphasisMarkNone ||
(checkFonts &&
(!styleRef().hasIdenticalAscentDescentAndLineGap(parentStyle) ||
parentStyle.lineHeight() != styleRef().lineHeight()));
if (!alwaysCreateLineBoxesNew && checkFonts &&
document().styleEngine().usesFirstLineRules()) {
// Have to check the first line style as well.
const ComputedStyle& firstLineParentStyle = parent()->styleRef(true);
const ComputedStyle& childStyle = styleRef(true);
alwaysCreateLineBoxesNew =
!firstLineParentStyle.hasIdenticalAscentDescentAndLineGap(childStyle) ||
childStyle.verticalAlign() != EVerticalAlign::Baseline ||
firstLineParentStyle.lineHeight() != childStyle.lineHeight();
}
if (alwaysCreateLineBoxesNew) {
if (!fullLayout)
dirtyLineBoxes(false);
setAlwaysCreateLineBoxes();
}
}
LayoutRect LayoutInline::localCaretRect(InlineBox* inlineBox,
int,
LayoutUnit* extraWidthToEndOfLine) {
if (firstChild()) {
// This condition is possible if the LayoutInline is at an editing boundary,
// i.e. the VisiblePosition is:
// <LayoutInline editingBoundary=true>|<LayoutText>
// </LayoutText></LayoutInline>
// 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();
}
DCHECK(!inlineBox);
if (extraWidthToEndOfLine)
*extraWidthToEndOfLine = LayoutUnit();
LayoutRect caretRect =
localCaretRectForEmptyElement(borderAndPaddingWidth(), LayoutUnit());
if (InlineBox* firstBox = firstLineBox())
caretRect.moveBy(firstBox->location());
return caretRect;
}
void LayoutInline::addChild(LayoutObject* newChild, LayoutObject* beforeChild) {
// Any table-part dom child of an inline element has anonymous wrappers in the
// layout tree so we need to climb up to the enclosing anonymous table wrapper
// and add the new child before that.
// TODO(rhogan): If newChild is a table part we want to insert it into the
// same table as beforeChild.
while (beforeChild && beforeChild->isTablePart())
beforeChild = beforeChild->parent();
if (continuation())
return addChildToContinuation(newChild, beforeChild);
return addChildIgnoringContinuation(newChild, beforeChild);
}
static LayoutBoxModelObject* nextContinuation(LayoutObject* layoutObject) {
if (layoutObject->isInline() && !layoutObject->isAtomicInlineLevel())
return toLayoutInline(layoutObject)->continuation();
return toLayoutBlockFlow(layoutObject)->inlineElementContinuation();
}
LayoutBoxModelObject* LayoutInline::continuationBefore(
LayoutObject* beforeChild) {
if (beforeChild && beforeChild->parent() == this)
return this;
LayoutBoxModelObject* curr = nextContinuation(this);
LayoutBoxModelObject* nextToLast = this;
LayoutBoxModelObject* last = this;
while (curr) {
if (beforeChild && beforeChild->parent() == curr) {
if (curr->slowFirstChild() == beforeChild)
return last;
return curr;
}
nextToLast = last;
last = curr;
curr = nextContinuation(curr);
}
if (!beforeChild && !last->slowFirstChild())
return nextToLast;
return last;
}
void LayoutInline::addChildIgnoringContinuation(LayoutObject* newChild,
LayoutObject* beforeChild) {
// Make sure we don't append things after :after-generated content if we have
// it.
if (!beforeChild && isAfterContent(lastChild()))
beforeChild = lastChild();
if (!newChild->isInline() && !newChild->isFloatingOrOutOfFlowPositioned() &&
!newChild->isTablePart()) {
// We are placing a block inside an inline. We have to perform a split of
// this inline into continuations. This involves creating an anonymous
// block box to hold |newChild|. We then make that block box a continuation
// of this inline. We take all of the children after |beforeChild| and put
// them in a clone of this object.
RefPtr<ComputedStyle> newStyle =
ComputedStyle::createAnonymousStyleWithDisplay(
containingBlock()->styleRef(), EDisplay::Block);
// If inside an inline affected by in-flow positioning the block needs to be
// affected by it too. Giving the block a layer like this allows it to
// collect the x/y offsets from inline parents later.
if (LayoutObject* positionedAncestor = inFlowPositionedInlineAncestor(this))
newStyle->setPosition(positionedAncestor->style()->position());
LayoutBlockFlow* newBox = LayoutBlockFlow::createAnonymous(&document());
newBox->setStyle(std::move(newStyle));
LayoutBoxModelObject* oldContinuation = continuation();
setContinuation(newBox);
splitFlow(beforeChild, newBox, newChild, oldContinuation);
return;
}
LayoutBoxModelObject::addChild(newChild, beforeChild);
newChild->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(
LayoutInvalidationReason::ChildChanged);
}
LayoutInline* LayoutInline::clone() const {
LayoutInline* cloneInline = new LayoutInline(node());
cloneInline->setStyle(mutableStyle());
cloneInline->setIsInsideFlowThread(isInsideFlowThread());
return cloneInline;
}
void LayoutInline::moveChildrenToIgnoringContinuation(
LayoutInline* to,
LayoutObject* startChild) {
LayoutObject* child = startChild;
while (child) {
LayoutObject* currentChild = child;
child = currentChild->nextSibling();
to->addChildIgnoringContinuation(
children()->removeChildNode(this, currentChild), nullptr);
}
}
void LayoutInline::splitInlines(LayoutBlockFlow* fromBlock,
LayoutBlockFlow* toBlock,
LayoutBlockFlow* middleBlock,
LayoutObject* beforeChild,
LayoutBoxModelObject* oldCont) {
ASSERT(isDescendantOf(fromBlock));
// If we're splitting the inline containing the fullscreened element,
// |beforeChild| may be the layoutObject for the fullscreened element.
// However, that layoutObject is wrapped in a LayoutFullScreen, so |this| is
// not its parent. Since the splitting logic expects |this| to be the parent,
// set |beforeChild| to be the LayoutFullScreen.
if (Fullscreen* fullscreen = Fullscreen::fromIfExists(document())) {
const Element* fullScreenElement = fullscreen->currentFullScreenElement();
if (fullScreenElement && beforeChild &&
beforeChild->node() == fullScreenElement)
beforeChild = fullscreen->fullScreenLayoutObject();
}
// FIXME: Because splitting is O(n^2) as tags nest pathologically, we cap the
// depth at which we're willing to clone.
// There will eventually be a better approach to this problem that will let us
// nest to a much greater depth (see bugzilla bug 13430) but for now we have a
// limit. This *will* result in incorrect rendering, but the alternative is to
// hang forever.
const unsigned cMaxSplitDepth = 200;
Vector<LayoutInline*> inlinesToClone;
LayoutInline* topMostInline = this;
for (LayoutObject* o = this; o != fromBlock; o = o->parent()) {
topMostInline = toLayoutInline(o);
if (inlinesToClone.size() < cMaxSplitDepth)
inlinesToClone.push_back(topMostInline);
// Keep walking up the chain to ensure |topMostInline| is a child of
// |fromBlock|, to avoid assertion failure when |fromBlock|'s children are
// moved to |toBlock| below.
}
// Create a new clone of the top-most inline in |inlinesToClone|.
LayoutInline* topMostInlineToClone = inlinesToClone.back();
LayoutInline* cloneInline = topMostInlineToClone->clone();
// Now we are at the block level. We need to put the clone into the |toBlock|.
toBlock->children()->appendChildNode(toBlock, cloneInline);
// Now take all the children after |topMostInline| and remove them from the
// |fromBlock| and put them into the toBlock.
fromBlock->moveChildrenTo(toBlock, topMostInline->nextSibling(), nullptr,
true);
LayoutInline* currentParent = topMostInlineToClone;
LayoutInline* cloneInlineParent = cloneInline;
// Clone the inlines from top to down to ensure any new object will be added
// into a rooted tree.
// Note that we have already cloned the top-most one, so the loop begins from
// size - 2 (except if we have reached |cMaxDepth| in which case we sacrifice
// correct rendering for performance).
for (int i = static_cast<int>(inlinesToClone.size()) - 2; i >= 0; --i) {
// Hook the clone up as a continuation of |currentInline|.
LayoutBoxModelObject* oldCont = currentParent->continuation();
currentParent->setContinuation(cloneInline);
cloneInline->setContinuation(oldCont);
// Create a new clone.
LayoutInline* current = inlinesToClone[i];
cloneInline = current->clone();
// Insert our |cloneInline| as the first child of |cloneInlineParent|.
cloneInlineParent->addChildIgnoringContinuation(cloneInline, nullptr);
// Now we need to take all of the children starting from the first child
// *after* |current| and append them all to the |cloneInlineParent|.
currentParent->moveChildrenToIgnoringContinuation(cloneInlineParent,
current->nextSibling());
currentParent = current;
cloneInlineParent = cloneInline;
}
// The last inline to clone is |this|, and the current |cloneInline| is cloned
// from |this|.
ASSERT(this == inlinesToClone.front());
// Hook |cloneInline| up as the continuation of the middle block.
cloneInline->setContinuation(oldCont);
middleBlock->setContinuation(cloneInline);
// Now take all of the children from |beforeChild| to the end and remove
// them from |this| and place them in the clone.
moveChildrenToIgnoringContinuation(cloneInline, beforeChild);
}
void LayoutInline::splitFlow(LayoutObject* beforeChild,
LayoutBlockFlow* newBlockBox,
LayoutObject* newChild,
LayoutBoxModelObject* oldCont) {
LayoutBlockFlow* block = toLayoutBlockFlow(containingBlock());
LayoutBlockFlow* pre = nullptr;
// Delete our line boxes before we do the inline split into continuations.
block->deleteLineBoxTree();
bool reusedAnonymousBlock = false;
if (block->isAnonymousBlock()) {
LayoutBlock* outerContainingBlock = block->containingBlock();
if (outerContainingBlock && outerContainingBlock->isLayoutBlockFlow() &&
!outerContainingBlock->createsAnonymousWrapper()) {
// We can reuse this block and make it the preBlock of the next
// continuation.
block->removePositionedObjects(nullptr);
block->removeFloatingObjects();
pre = block;
block = toLayoutBlockFlow(outerContainingBlock);
reusedAnonymousBlock = true;
}
}
// No anonymous block available for use. Make one.
if (!reusedAnonymousBlock)
pre = toLayoutBlockFlow(block->createAnonymousBlock());
LayoutBlockFlow* post = toLayoutBlockFlow(pre->createAnonymousBlock());
LayoutObject* boxFirst =
!reusedAnonymousBlock ? block->firstChild() : pre->nextSibling();
if (!reusedAnonymousBlock)
block->children()->insertChildNode(block, pre, boxFirst);
block->children()->insertChildNode(block, newBlockBox, boxFirst);
block->children()->insertChildNode(block, post, boxFirst);
block->setChildrenInline(false);
if (!reusedAnonymousBlock) {
LayoutObject* o = boxFirst;
while (o) {
LayoutObject* no = o;
o = no->nextSibling();
pre->children()->appendChildNode(
pre, block->children()->removeChildNode(block, no));
no->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(
LayoutInvalidationReason::AnonymousBlockChange);
}
}
splitInlines(pre, post, newBlockBox, beforeChild, oldCont);
// We already know the newBlockBox isn't going to contain inline kids, so
// avoid wasting time in makeChildrenNonInline by just setting this explicitly
// up front.
newBlockBox->setChildrenInline(false);
newBlockBox->addChild(newChild);
// Always just do a full layout in order to ensure that line boxes (especially
// wrappers for images) get deleted properly. Because objects moves from the
// pre block into the post block, we want to make new line boxes instead of
// leaving the old line boxes around.
pre->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(
LayoutInvalidationReason::AnonymousBlockChange);
block->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(
LayoutInvalidationReason::AnonymousBlockChange);
post->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(
LayoutInvalidationReason::AnonymousBlockChange);
}
void LayoutInline::addChildToContinuation(LayoutObject* newChild,
LayoutObject* beforeChild) {
// A continuation always consists of two potential candidates: an inline or an
// anonymous block box holding block children.
LayoutBoxModelObject* flow = continuationBefore(beforeChild);
ASSERT(!beforeChild || beforeChild->parent()->isAnonymousBlock() ||
beforeChild->parent()->isLayoutInline());
LayoutBoxModelObject* beforeChildParent = nullptr;
if (beforeChild) {
beforeChildParent = toLayoutBoxModelObject(beforeChild->parent());
} else {
LayoutBoxModelObject* cont = nextContinuation(flow);
if (cont)
beforeChildParent = cont;
else
beforeChildParent = flow;
}
// TODO(rhogan): Should we treat out-of-flows and floats as through they're
// inline below?
if (newChild->isFloatingOrOutOfFlowPositioned())
return beforeChildParent->addChildIgnoringContinuation(newChild,
beforeChild);
// A table part will be wrapped by an inline anonymous table when it is added
// to the layout tree, so treat it as inline when deciding where to add it.
bool childInline = newChild->isInline() || newChild->isTablePart();
bool bcpInline = beforeChildParent->isInline();
bool flowInline = flow->isInline();
if (flow == beforeChildParent)
return flow->addChildIgnoringContinuation(newChild, beforeChild);
// The goal here is to match up if we can, so that we can coalesce and create
// the minimal # of continuations needed for the inline.
if (childInline == bcpInline || (beforeChild && beforeChild->isInline()))
return beforeChildParent->addChildIgnoringContinuation(newChild,
beforeChild);
if (flowInline == childInline) {
// Just treat like an append.
return flow->addChildIgnoringContinuation(newChild, 0);
}
return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
}
void LayoutInline::paint(const PaintInfo& paintInfo,
const LayoutPoint& paintOffset) const {
InlinePainter(*this).paint(paintInfo, paintOffset);
}
template <typename GeneratorContext>
void LayoutInline::generateLineBoxRects(GeneratorContext& yield) const {
if (!alwaysCreateLineBoxes()) {
generateCulledLineBoxRects(yield, this);
} else if (InlineFlowBox* curr = firstLineBox()) {
for (; curr; curr = curr->nextLineBox())
yield(LayoutRect(curr->location(), curr->size()));
}
}
static inline void computeItemTopHeight(const LayoutInline* container,
const RootInlineBox& rootBox,
LayoutUnit* top,
LayoutUnit* height) {
bool firstLine = rootBox.isFirstLineStyle();
const SimpleFontData* fontData =
rootBox.getLineLayoutItem().style(firstLine)->font().primaryFont();
const SimpleFontData* containerFontData =
container->style(firstLine)->font().primaryFont();
DCHECK(fontData && containerFontData);
if (!fontData || !containerFontData) {
*top = LayoutUnit();
*height = LayoutUnit();
return;
}
auto metrics = fontData->getFontMetrics();
auto containerMetrics = containerFontData->getFontMetrics();
*top = rootBox.logicalTop() + (metrics.ascent() - containerMetrics.ascent());
*height = LayoutUnit(containerMetrics.height());
}
template <typename GeneratorContext>
void LayoutInline::generateCulledLineBoxRects(
GeneratorContext& yield,
const LayoutInline* container) const {
if (!culledInlineFirstLineBox())
return;
bool isHorizontal = style()->isHorizontalWritingMode();
LayoutUnit logicalTop, logicalHeight;
for (LayoutObject* 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()) {
LayoutBox* currBox = toLayoutBox(curr);
if (currBox->inlineBoxWrapper()) {
RootInlineBox& rootBox = currBox->inlineBoxWrapper()->root();
computeItemTopHeight(container, rootBox, &logicalTop, &logicalHeight);
if (isHorizontal) {
yield(LayoutRect(
currBox->inlineBoxWrapper()->x() - currBox->marginLeft(),
logicalTop, currBox->size().width() + currBox->marginWidth(),
logicalHeight));
} else {
yield(LayoutRect(logicalTop, currBox->inlineBoxWrapper()->y() -
currBox->marginTop(),
logicalHeight,
currBox->size().height() + currBox->marginHeight()));
}
}
} else if (curr->isLayoutInline()) {
// If the child doesn't need line boxes either, then we can recur.
LayoutInline* currInline = toLayoutInline(curr);
if (!currInline->alwaysCreateLineBoxes()) {
currInline->generateCulledLineBoxRects(yield, container);
} else {
for (InlineFlowBox* childLine = currInline->firstLineBox(); childLine;
childLine = childLine->nextLineBox()) {
RootInlineBox& rootBox = childLine->root();
computeItemTopHeight(container, rootBox, &logicalTop, &logicalHeight);
LayoutUnit logicalWidth =
childLine->logicalWidth() + childLine->marginLogicalWidth();
if (isHorizontal) {
yield(LayoutRect(
LayoutUnit(childLine->x() - childLine->marginLogicalLeft()),
logicalTop, logicalWidth, logicalHeight));
} else {
yield(LayoutRect(
logicalTop,
LayoutUnit(childLine->y() - childLine->marginLogicalLeft()),
logicalHeight, logicalWidth));
}
}
}
} else if (curr->isText()) {
LayoutText* currText = toLayoutText(curr);
for (InlineTextBox* childText = currText->firstTextBox(); childText;
childText = childText->nextTextBox()) {
RootInlineBox& rootBox = childText->root();
computeItemTopHeight(container, rootBox, &logicalTop, &logicalHeight);
if (isHorizontal)
yield(LayoutRect(childText->x(), logicalTop,
childText->logicalWidth(), logicalHeight));
else
yield(LayoutRect(logicalTop, childText->y(), logicalHeight,
childText->logicalWidth()));
}
}
}
}
namespace {
class AbsoluteRectsGeneratorContext {
public:
AbsoluteRectsGeneratorContext(Vector<IntRect>& rects,
const LayoutPoint& accumulatedOffset)
: m_rects(rects), m_accumulatedOffset(accumulatedOffset) {}
void operator()(const LayoutRect& rect) {
IntRect intRect = enclosingIntRect(rect);
intRect.move(m_accumulatedOffset.x().toInt(),
m_accumulatedOffset.y().toInt());
m_rects.push_back(intRect);
}
private:
Vector<IntRect>& m_rects;
const LayoutPoint& m_accumulatedOffset;
};
} // unnamed namespace
void LayoutInline::absoluteRects(Vector<IntRect>& rects,
const LayoutPoint& accumulatedOffset) const {
AbsoluteRectsGeneratorContext context(rects, accumulatedOffset);
generateLineBoxRects(context);
if (rects.isEmpty())
context(LayoutRect());
if (const LayoutBoxModelObject* continuation = this->continuation()) {
if (continuation->isBox()) {
const LayoutBox* box = toLayoutBox(continuation);
continuation->absoluteRects(
rects,
toLayoutPoint(accumulatedOffset - containingBlock()->location() +
box->locationOffset()));
} else {
continuation->absoluteRects(
rects,
toLayoutPoint(accumulatedOffset - containingBlock()->location()));
}
}
}
namespace {
class AbsoluteQuadsGeneratorContext {
public:
AbsoluteQuadsGeneratorContext(const LayoutInline* layoutObject,
Vector<FloatQuad>& quads,
MapCoordinatesFlags mode)
: m_quads(quads), m_geometryMap(mode) {
m_geometryMap.pushMappingsToAncestor(layoutObject, 0);
}
void operator()(const FloatRect& rect) {
m_quads.push_back(m_geometryMap.absoluteRect(rect));
}
void operator()(const LayoutRect& rect) { operator()(FloatRect(rect)); }
private:
Vector<FloatQuad>& m_quads;
LayoutGeometryMap m_geometryMap;
};
} // unnamed namespace
void LayoutInline::absoluteQuadsForSelf(Vector<FloatQuad>& quads,
MapCoordinatesFlags mode) const {
AbsoluteQuadsGeneratorContext context(this, quads, mode);
generateLineBoxRects(context);
if (quads.isEmpty())
context(FloatRect());
}
LayoutPoint LayoutInline::firstLineBoxTopLeft() const {
if (InlineBox* firstBox = firstLineBoxIncludingCulling())
return firstBox->location();
return LayoutPoint();
}
LayoutUnit LayoutInline::offsetLeft(const Element* parent) const {
return adjustedPositionRelativeTo(firstLineBoxTopLeft(), parent).x();
}
LayoutUnit LayoutInline::offsetTop(const Element* parent) const {
return adjustedPositionRelativeTo(firstLineBoxTopLeft(), parent).y();
}
static LayoutUnit computeMargin(const LayoutInline* layoutObject,
const Length& margin) {
if (margin.isFixed())
return LayoutUnit(margin.value());
if (margin.isPercentOrCalc())
return minimumValueForLength(
margin,
std::max(LayoutUnit(),
layoutObject->containingBlock()->availableLogicalWidth()));
return LayoutUnit();
}
LayoutRectOutsets LayoutInline::marginBoxOutsets() const {
return LayoutRectOutsets(marginTop(), marginRight(), marginBottom(),
marginLeft());
}
LayoutUnit LayoutInline::marginLeft() const {
return computeMargin(this, style()->marginLeft());
}
LayoutUnit LayoutInline::marginRight() const {
return computeMargin(this, style()->marginRight());
}
LayoutUnit LayoutInline::marginTop() const {
return computeMargin(this, style()->marginTop());
}
LayoutUnit LayoutInline::marginBottom() const {
return computeMargin(this, style()->marginBottom());
}
LayoutUnit LayoutInline::marginStart(const ComputedStyle* otherStyle) const {
return computeMargin(
this, style()->marginStartUsing(otherStyle ? otherStyle : style()));
}
LayoutUnit LayoutInline::marginEnd(const ComputedStyle* otherStyle) const {
return computeMargin(
this, style()->marginEndUsing(otherStyle ? otherStyle : style()));
}
LayoutUnit LayoutInline::marginBefore(const ComputedStyle* otherStyle) const {
return computeMargin(
this, style()->marginBeforeUsing(otherStyle ? otherStyle : style()));
}
LayoutUnit LayoutInline::marginAfter(const ComputedStyle* otherStyle) const {
return computeMargin(
this, style()->marginAfterUsing(otherStyle ? otherStyle : style()));
}
LayoutUnit LayoutInline::marginOver() const {
return computeMargin(this, style()->marginOver());
}
LayoutUnit LayoutInline::marginUnder() const {
return computeMargin(this, style()->marginUnder());
}
bool LayoutInline::nodeAtPoint(HitTestResult& result,
const HitTestLocation& locationInContainer,
const LayoutPoint& accumulatedOffset,
HitTestAction hitTestAction) {
return m_lineBoxes.hitTest(LineLayoutBoxModel(this), result,
locationInContainer, accumulatedOffset,
hitTestAction);
}
namespace {
class HitTestCulledInlinesGeneratorContext {
public:
HitTestCulledInlinesGeneratorContext(Region& region,
const HitTestLocation& location)
: m_intersected(false), m_region(region), m_location(location) {}
void operator()(const FloatRect& rect) {
if (m_location.intersects(rect)) {
m_intersected = true;
m_region.unite(enclosingIntRect(rect));
}
}
void operator()(const LayoutRect& rect) {
if (m_location.intersects(rect)) {
m_intersected = true;
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 LayoutInline::hitTestCulledInline(
HitTestResult& result,
const HitTestLocation& locationInContainer,
const LayoutPoint& accumulatedOffset) {
ASSERT(!alwaysCreateLineBoxes());
if (!visibleToHitTestRequest(result.hitTestRequest()))
return false;
HitTestLocation adjustedLocation(locationInContainer,
-toLayoutSize(accumulatedOffset));
Region regionResult;
HitTestCulledInlinesGeneratorContext context(regionResult, adjustedLocation);
generateCulledLineBoxRects(context, this);
if (context.intersected()) {
updateHitTestResult(result, adjustedLocation.point());
if (result.addNodeToListBasedTestResult(node(), adjustedLocation,
regionResult) == StopHitTesting)
return true;
}
return false;
}
PositionWithAffinity LayoutInline::positionForPoint(const LayoutPoint& point) {
// FIXME: Does not deal with relative positioned inlines (should it?)
// If there are continuations, test them first because our containing block
// will not check them.
LayoutBoxModelObject* continuation = this->continuation();
while (continuation) {
if (continuation->isInline() || continuation->slowFirstChild())
return continuation->positionForPoint(point);
continuation = toLayoutBlockFlow(continuation)->inlineElementContinuation();
}
if (firstLineBoxIncludingCulling()) {
// 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 containingBlock()->positionForPoint(point);
}
return LayoutBoxModelObject::positionForPoint(point);
}
namespace {
class LinesBoundingBoxGeneratorContext {
public:
LinesBoundingBoxGeneratorContext(FloatRect& rect) : m_rect(rect) {}
void operator()(const FloatRect& rect) { m_rect.uniteIfNonZero(rect); }
void operator()(const LayoutRect& rect) { operator()(FloatRect(rect)); }
private:
FloatRect& m_rect;
};
} // unnamed namespace
LayoutRect LayoutInline::linesBoundingBox() const {
if (!alwaysCreateLineBoxes()) {
ASSERT(!firstLineBox());
FloatRect floatResult;
LinesBoundingBoxGeneratorContext context(floatResult);
generateCulledLineBoxRects(context, this);
return enclosingLayoutRect(floatResult);
}
LayoutRect 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.
LayoutUnit logicalLeftSide;
LayoutUnit logicalRightSide;
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();
}
bool isHorizontal = style()->isHorizontalWritingMode();
LayoutUnit x = isHorizontal ? logicalLeftSide : firstLineBox()->x();
LayoutUnit y = isHorizontal ? firstLineBox()->y() : logicalLeftSide;
LayoutUnit width = isHorizontal ? logicalRightSide - logicalLeftSide
: lastLineBox()->logicalBottom() - x;
LayoutUnit height = isHorizontal ? lastLineBox()->logicalBottom() - y
: logicalRightSide - logicalLeftSide;
result = LayoutRect(x, y, width, height);
}
return result;
}
InlineBox* LayoutInline::culledInlineFirstLineBox() const {
for (LayoutObject* 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 toLayoutBox(curr)->inlineBoxWrapper();
if (curr->isLayoutInline()) {
LayoutInline* currInline = toLayoutInline(curr);
InlineBox* result = currInline->firstLineBoxIncludingCulling();
if (result)
return result;
} else if (curr->isText()) {
LayoutText* currText = toLayoutText(curr);
if (currText->firstTextBox())
return currText->firstTextBox();
}
}
return nullptr;
}
InlineBox* LayoutInline::culledInlineLastLineBox() const {
for (LayoutObject* 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 toLayoutBox(curr)->inlineBoxWrapper();
if (curr->isLayoutInline()) {
LayoutInline* currInline = toLayoutInline(curr);
InlineBox* result = currInline->lastLineBoxIncludingCulling();
if (result)
return result;
} else if (curr->isText()) {
LayoutText* currText = toLayoutText(curr);
if (currText->lastTextBox())
return currText->lastTextBox();
}
}
return nullptr;
}
LayoutRect LayoutInline::culledInlineVisualOverflowBoundingBox() const {
FloatRect floatResult;
LinesBoundingBoxGeneratorContext context(floatResult);
generateCulledLineBoxRects(context, this);
LayoutRect result(enclosingLayoutRect(floatResult));
bool isHorizontal = style()->isHorizontalWritingMode();
for (LayoutObject* 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()) {
LayoutBox* currBox = toLayoutBox(curr);
if (!currBox->hasSelfPaintingLayer() && currBox->inlineBoxWrapper()) {
LayoutRect logicalRect =
currBox->logicalVisualOverflowRectForPropagation(styleRef());
if (isHorizontal) {
logicalRect.moveBy(currBox->location());
result.uniteIfNonZero(logicalRect);
} else {
logicalRect.moveBy(currBox->location());
result.uniteIfNonZero(logicalRect.transposedRect());
}
}
} else if (curr->isLayoutInline()) {
// If the child doesn't need line boxes either, then we can recur.
LayoutInline* currInline = toLayoutInline(curr);
if (!currInline->alwaysCreateLineBoxes())
result.uniteIfNonZero(
currInline->culledInlineVisualOverflowBoundingBox());
else if (!currInline->hasSelfPaintingLayer())
result.uniteIfNonZero(currInline->visualOverflowRect());
} else if (curr->isText()) {
LayoutText* currText = toLayoutText(curr);
result.uniteIfNonZero(currText->visualOverflowRect());
}
}
return result;
}
LayoutRect LayoutInline::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);
if (!style()->isHorizontalWritingMode())
rect = rect.transposedRect();
return rect;
}
LayoutRect LayoutInline::absoluteVisualRect() const {
if (!continuation()) {
LayoutRect rect = visualOverflowRect();
mapToVisualRectInAncestorSpace(view(), rect);
return rect;
}
FloatRect floatResult;
LinesBoundingBoxGeneratorContext context(floatResult);
LayoutInline* endContinuation = inlineElementContinuation();
while (LayoutInline* nextContinuation =
endContinuation->inlineElementContinuation())
endContinuation = nextContinuation;
for (LayoutBlock* currBlock = containingBlock();
currBlock && currBlock->isAnonymousBlock();
currBlock = toLayoutBlock(currBlock->nextSibling())) {
bool walkChildrenOnly = !currBlock->childrenInline();
for (LayoutObject* curr = currBlock->firstChild(); curr;
curr = curr->nextSibling()) {
LayoutRect rect(curr->localVisualRect());
context(FloatRect(rect));
if (walkChildrenOnly)
continue;
for (LayoutObject* walker = curr; walker;
walker = walker->nextInPreOrder(curr)) {
if (walker != endContinuation)
continue;
LayoutRect rect(enclosingIntRect(floatResult));
mapToVisualRectInAncestorSpace(view(), rect);
return rect;
}
}
}
return LayoutRect();
}
LayoutRect LayoutInline::localVisualRect() const {
// If we don't create line boxes, we don't have any invalidations to do.
if (!alwaysCreateLineBoxes())
return LayoutRect();
if (style()->visibility() != EVisibility::kVisible)
return LayoutRect();
return visualOverflowRect();
}
LayoutRect LayoutInline::visualOverflowRect() const {
LayoutRect overflowRect = linesVisualOverflowBoundingBox();
LayoutUnit outlineOutset(style()->outlineOutsetExtent());
if (outlineOutset) {
Vector<LayoutRect> rects;
if (document().inNoQuirksMode()) {
// We have already included outline extents of line boxes in
// linesVisualOverflowBoundingBox(), so the following just add outline
// rects for children and continuations.
addOutlineRectsForChildrenAndContinuations(
rects, LayoutPoint(), outlineRectsShouldIncludeBlockVisualOverflow());
} else {
// In non-standard mode, because the difference in
// LayoutBlock::minLineHeightForReplacedObject(),
// linesVisualOverflowBoundingBox() may not cover outline rects of lines
// containing replaced objects.
addOutlineRects(rects, LayoutPoint(),
outlineRectsShouldIncludeBlockVisualOverflow());
}
if (!rects.isEmpty()) {
LayoutRect outlineRect = unionRectEvenIfEmpty(rects);
outlineRect.inflate(outlineOutset);
overflowRect.unite(outlineRect);
}
}
return overflowRect;
}
bool LayoutInline::mapToVisualRectInAncestorSpace(
const LayoutBoxModelObject* ancestor,
LayoutRect& rect,
VisualRectFlags visualRectFlags) const {
if (ancestor == this)
return true;
LayoutObject* container = this->container();
ASSERT(container == parent());
if (!container)
return true;
if (style()->hasInFlowPosition() && layer()) {
// Apply the in-flow position offset when invalidating a rectangle. The
// layer is translated, but the layout box isn't, so we need to do this to
// get the right dirty rect. Since this is called from LayoutObject::
// setStyle, the relative position flag on the LayoutObject has been
// cleared, so use the one on the style().
rect.move(layer()->offsetForInFlowPosition());
}
LayoutBox* containerBox =
container->isBox() ? toLayoutBox(container) : nullptr;
if (containerBox && container != ancestor &&
!containerBox->mapScrollingContentsRectToBoxSpace(rect, visualRectFlags))
return false;
// TODO(wkorman): Generalize Ruby specialization and/or document more clearly.
if (containerBox && !isRuby())
containerBox->flipForWritingMode(rect);
return container->mapToVisualRectInAncestorSpace(ancestor, rect,
visualRectFlags);
}
LayoutSize LayoutInline::offsetFromContainer(
const LayoutObject* container) const {
ASSERT(container == this->container());
LayoutSize offset;
if (isInFlowPositioned())
offset += offsetForInFlowPosition();
if (container->hasOverflowClip())
offset -= toLayoutBox(container)->scrolledContentOffset();
return offset;
}
PaintLayerType LayoutInline::layerTypeRequired() const {
return isInFlowPositioned() || createsGroup() || hasClipPath() ||
style()->shouldCompositeForCurrentAnimations() ||
style()->hasCompositorProxy() || style()->containsPaint()
? NormalPaintLayer
: NoPaintLayer;
}
void LayoutInline::childBecameNonInline(LayoutObject* child) {
// We have to split the parent flow.
LayoutBlockFlow* newBox =
toLayoutBlockFlow(containingBlock()->createAnonymousBlock());
LayoutBoxModelObject* oldContinuation = continuation();
setContinuation(newBox);
LayoutObject* beforeChild = child->nextSibling();
children()->removeChildNode(this, child);
splitFlow(beforeChild, newBox, child, oldContinuation);
}
void LayoutInline::updateHitTestResult(HitTestResult& result,
const LayoutPoint& point) {
if (result.innerNode())
return;
Node* n = node();
LayoutPoint localPoint(point);
if (n) {
if (isInlineElementContinuation()) {
// We're in the continuation of a split inline. Adjust our local point to
// be in the coordinate space of the principal layoutObject's containing
// block. This will end up being the innerNode.
LayoutBlock* firstBlock = n->layoutObject()->containingBlock();
// Get our containing block.
LayoutBox* block = containingBlock();
localPoint.moveBy(block->location() - firstBlock->locationOffset());
}
result.setNodeAndPosition(n, localPoint);
}
}
void LayoutInline::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 (LayoutObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
if (curr->isFloatingOrOutOfFlowPositioned())
continue;
if (curr->isBox() && !curr->needsLayout()) {
LayoutBox* currBox = toLayoutBox(curr);
if (currBox->inlineBoxWrapper())
currBox->inlineBoxWrapper()->root().markDirty();
} else if (!curr->selfNeedsLayout()) {
if (curr->isLayoutInline()) {
LayoutInline* currInline = toLayoutInline(curr);
for (InlineFlowBox* childLine = currInline->firstLineBox(); childLine;
childLine = childLine->nextLineBox())
childLine->root().markDirty();
} else if (curr->isText()) {
LayoutText* currText = toLayoutText(curr);
for (InlineTextBox* childText = currText->firstTextBox(); childText;
childText = childText->nextTextBox())
childText->root().markDirty();
}
}
}
} else {
m_lineBoxes.dirtyLineBoxes();
}
}
InlineFlowBox* LayoutInline::createInlineFlowBox() {
return new InlineFlowBox(LineLayoutItem(this));
}
InlineFlowBox* LayoutInline::createAndAppendInlineFlowBox() {
setAlwaysCreateLineBoxes();
InlineFlowBox* flowBox = createInlineFlowBox();
m_lineBoxes.appendLineBox(flowBox);
return flowBox;
}
LayoutUnit LayoutInline::lineHeight(
bool firstLine,
LineDirectionMode /*direction*/,
LinePositionMode /*linePositionMode*/) const {
if (firstLine && document().styleEngine().usesFirstLineRules()) {
const ComputedStyle* s = style(firstLine);
if (s != style())
return LayoutUnit(s->computedLineHeight());
}
return LayoutUnit(style()->computedLineHeight());
}
int LayoutInline::baselinePosition(FontBaseline baselineType,
bool firstLine,
LineDirectionMode direction,
LinePositionMode linePositionMode) const {
ASSERT(linePositionMode == PositionOnContainingLine);
const SimpleFontData* fontData = style(firstLine)->font().primaryFont();
DCHECK(fontData);
if (!fontData)
return -1;
const FontMetrics& fontMetrics = fontData->getFontMetrics();
return (fontMetrics.ascent(baselineType) +
(lineHeight(firstLine, direction, linePositionMode) -
fontMetrics.height()) /
2)
.toInt();
}
LayoutSize LayoutInline::offsetForInFlowPositionedInline(
const LayoutBox& child) const {
// FIXME: This function isn't right with mixed writing modes.
ASSERT(isInFlowPositioned());
if (!isInFlowPositioned())
return LayoutSize();
// When we have an enclosing relpositioned inline, we need to add in the
// offset of the first line box from the rest of the content, but only in the
// cases where we know we're positioned relative to the inline itself.
LayoutSize logicalOffset;
LayoutUnit inlinePosition;
LayoutUnit blockPosition;
if (firstLineBox()) {
inlinePosition = firstLineBox()->logicalLeft();
blockPosition = firstLineBox()->logicalTop();
} else {
inlinePosition = layer()->staticInlinePosition();
blockPosition = layer()->staticBlockPosition();
}
// Per http://www.w3.org/TR/CSS2/visudet.html#abs-non-replaced-width an
// absolute positioned box with a static position should locate itself as
// though it is a normal flow box in relation to its containing block. If this
// relative-positioned inline has a negative offset we need to compensate for
// it so that we align the positioned object with the edge of its containing
// block.
if (child.style()->hasStaticInlinePosition(
style()->isHorizontalWritingMode()))
logicalOffset.setWidth(
std::max(LayoutUnit(), -offsetForInFlowPosition().width()));
else
logicalOffset.setWidth(inlinePosition);
if (!child.style()->hasStaticBlockPosition(
style()->isHorizontalWritingMode()))
logicalOffset.setHeight(blockPosition);
return style()->isHorizontalWritingMode() ? logicalOffset
: logicalOffset.transposedSize();
}
void LayoutInline::imageChanged(WrappedImagePtr, const IntRect*) {
if (!parent())
return;
// FIXME: We can do better.
setShouldDoFullPaintInvalidation();
}
namespace {
class AbsoluteLayoutRectsGeneratorContext {
public:
AbsoluteLayoutRectsGeneratorContext(Vector<LayoutRect>& rects,
const LayoutPoint& accumulatedOffset)
: m_rects(rects), m_accumulatedOffset(accumulatedOffset) {}
void operator()(const FloatRect& rect) { operator()(LayoutRect(rect)); }
void operator()(const LayoutRect& rect) {
LayoutRect layoutRect(rect);
layoutRect.moveBy(m_accumulatedOffset);
m_rects.push_back(layoutRect);
}
private:
Vector<LayoutRect>& m_rects;
const LayoutPoint& m_accumulatedOffset;
};
} // unnamed namespace
void LayoutInline::addOutlineRects(
Vector<LayoutRect>& rects,
const LayoutPoint& additionalOffset,
IncludeBlockVisualOverflowOrNot includeBlockOverflows) const {
AbsoluteLayoutRectsGeneratorContext context(rects, additionalOffset);
generateLineBoxRects(context);
addOutlineRectsForChildrenAndContinuations(rects, additionalOffset,
includeBlockOverflows);
}
void LayoutInline::addOutlineRectsForChildrenAndContinuations(
Vector<LayoutRect>& rects,
const LayoutPoint& additionalOffset,
IncludeBlockVisualOverflowOrNot includeBlockOverflows) const {
addOutlineRectsForNormalChildren(rects, additionalOffset,
includeBlockOverflows);
addOutlineRectsForContinuations(rects, additionalOffset,
includeBlockOverflows);
}
void LayoutInline::addOutlineRectsForContinuations(
Vector<LayoutRect>& rects,
const LayoutPoint& additionalOffset,
IncludeBlockVisualOverflowOrNot includeBlockOverflows) const {
if (LayoutBoxModelObject* continuation = this->continuation()) {
if (continuation->isInline())
continuation->addOutlineRects(
rects,
additionalOffset + (continuation->containingBlock()->location() -
containingBlock()->location()),
includeBlockOverflows);
else
continuation->addOutlineRects(
rects, additionalOffset + (toLayoutBox(continuation)->location() -
containingBlock()->location()),
includeBlockOverflows);
}
}
FloatRect LayoutInline::localBoundingBoxRectForAccessibility() const {
Vector<LayoutRect> rects;
addOutlineRects(rects, LayoutPoint(), IncludeBlockVisualOverflow);
return FloatRect(unionRect(rects));
}
void LayoutInline::computeSelfHitTestRects(
Vector<LayoutRect>& rects,
const LayoutPoint& layerOffset) const {
AbsoluteLayoutRectsGeneratorContext context(rects, layerOffset);
generateLineBoxRects(context);
}
void LayoutInline::addAnnotatedRegions(Vector<AnnotatedRegionValue>& regions) {
// Convert the style regions to absolute coordinates.
if (style()->visibility() != EVisibility::kVisible)
return;
if (style()->getDraggableRegionMode() == DraggableRegionNone)
return;
AnnotatedRegionValue region;
region.draggable = style()->getDraggableRegionMode() == DraggableRegionDrag;
region.bounds = LayoutRect(linesBoundingBox());
LayoutObject* container = containingBlock();
if (!container)
container = this;
FloatPoint absPos = container->localToAbsolute();
region.bounds.setX(LayoutUnit(absPos.x() + region.bounds.x()));
region.bounds.setY(LayoutUnit(absPos.y() + region.bounds.y()));
regions.push_back(region);
}
void LayoutInline::invalidateDisplayItemClients(
PaintInvalidationReason invalidationReason) const {
ObjectPaintInvalidator paintInvalidator(*this);
paintInvalidator.invalidateDisplayItemClient(*this, invalidationReason);
for (InlineFlowBox* box = firstLineBox(); box; box = box->nextLineBox())
paintInvalidator.invalidateDisplayItemClient(*box, invalidationReason);
}
// TODO(lunalu): Not to just dump 0, 0 as the x and y here
LayoutRect LayoutInline::debugRect() const {
IntRect linesBox = enclosingIntRect(linesBoundingBox());
return LayoutRect(IntRect(0, 0, linesBox.width(), linesBox.height()));
}
} // namespace blink