LayoutState::LayoutState(LayoutBox& layoutObject, const LayoutSize& offset, LayoutUnit pageLogicalHeight, bool pageLogicalHeightChanged, bool containingBlockLogicalWidthChanged) : m_containingBlockLogicalWidthChanged(containingBlockLogicalWidthChanged) , m_next(layoutObject.view()->layoutState()) , m_layoutObject(layoutObject) { if (layoutObject.isLayoutFlowThread()) m_flowThread = toLayoutFlowThread(&layoutObject); else if (!layoutObject.isOutOfFlowPositioned() && !layoutObject.isColumnSpanAll()) m_flowThread = m_next->flowThread(); else m_flowThread = nullptr; layoutObject.view()->pushLayoutState(*this); bool fixed = layoutObject.isOutOfFlowPositioned() && layoutObject.style()->position() == FixedPosition; if (fixed) { // FIXME: This doesn't work correctly with transforms. FloatPoint fixedOffset = layoutObject.view()->localToAbsolute(FloatPoint(), IsFixed); m_layoutOffset = LayoutSize(fixedOffset.x(), fixedOffset.y()) + offset; } else { m_layoutOffset = m_next->m_layoutOffset + offset; } if (layoutObject.isOutOfFlowPositioned() && !fixed) { if (LayoutObject* container = layoutObject.container()) { if (container->style()->hasInFlowPosition() && container->isLayoutInline()) m_layoutOffset += toLayoutInline(container)->offsetForInFlowPositionedInline(layoutObject); } } // If we establish a new page height, then cache the offset to the top of the first page. // We can compare this later on to figure out what part of the page we're actually on, if (pageLogicalHeight || layoutObject.isLayoutFlowThread()) { m_pageLogicalHeight = pageLogicalHeight; bool isFlipped = layoutObject.style()->isFlippedBlocksWritingMode(); m_pageOffset = LayoutSize(m_layoutOffset.width() + (!isFlipped ? layoutObject.borderLeft() + layoutObject.paddingLeft() : layoutObject.borderRight() + layoutObject.paddingRight()), m_layoutOffset.height() + (!isFlipped ? layoutObject.borderTop() + layoutObject.paddingTop() : layoutObject.borderBottom() + layoutObject.paddingBottom())); m_pageLogicalHeightChanged = pageLogicalHeightChanged; m_isPaginated = true; } else if (m_layoutObject.isSVG() && !m_layoutObject.isSVGRoot()) { // Pagination inside SVG is not allowed. m_flowThread = nullptr; m_pageLogicalHeightChanged = false; m_isPaginated = false; } else { // If we don't establish a new page height, then propagate the old page height and offset down. m_pageLogicalHeight = m_next->m_pageLogicalHeight; m_pageLogicalHeightChanged = m_next->m_pageLogicalHeightChanged; m_pageOffset = m_next->m_pageOffset; // Disable pagination for objects we don't support. For now this includes overflow:scroll/auto, inline blocks and // writing mode roots. if (layoutObject.isUnsplittableForPagination()) { m_flowThread = nullptr; m_pageLogicalHeight = 0; m_isPaginated = false; } else { m_isPaginated = m_pageLogicalHeight || m_flowThread; } } // FIXME: <http://bugs.webkit.org/show_bug.cgi?id=13443> Apply control clip if present. }
static inline InlineFlowBox* flowBoxForLayoutObject(LayoutObject* layoutObject) { if (!layoutObject) return nullptr; if (layoutObject->isLayoutBlock()) { // If we're given a block element, it has to be a LayoutSVGText. ASSERT(layoutObject->isSVGText()); LayoutBlockFlow* layoutBlockFlow = toLayoutBlockFlow(layoutObject); // LayoutSVGText only ever contains a single line box. InlineFlowBox* flowBox = layoutBlockFlow->firstLineBox(); ASSERT(flowBox == layoutBlockFlow->lastLineBox()); return flowBox; } if (layoutObject->isLayoutInline()) { // We're given a LayoutSVGInline or objects that derive from it (LayoutSVGTSpan / LayoutSVGTextPath) LayoutInline* layoutInline = toLayoutInline(layoutObject); // LayoutSVGInline only ever contains a single line box. InlineFlowBox* flowBox = layoutInline->firstLineBox(); ASSERT(flowBox == layoutInline->lastLineBox()); return flowBox; } ASSERT_NOT_REACHED(); return nullptr; }
PaintInvalidationState::PaintInvalidationState(PaintInvalidationState& next, LayoutBoxModelObject& layoutObject, const LayoutBoxModelObject& paintInvalidationContainer) : m_clipped(false) , m_cachedOffsetsEnabled(true) , m_forcedSubtreeInvalidationWithinContainer(next.m_forcedSubtreeInvalidationWithinContainer) , m_forcedSubtreeInvalidationRectUpdateWithinContainer(next.m_forcedSubtreeInvalidationRectUpdateWithinContainer) , m_viewClippingAndScrollOffsetDisabled(false) , m_paintInvalidationContainer(paintInvalidationContainer) , m_pendingDelayedPaintInvalidations(next.pendingDelayedPaintInvalidationTargets()) , m_enclosingSelfPaintingLayer(next.enclosingSelfPaintingLayer(layoutObject)) { // FIXME: SVG could probably benefit from a stack-based optimization like html does. crbug.com/391054 bool establishesPaintInvalidationContainer = layoutObject == m_paintInvalidationContainer; bool fixed = layoutObject.style()->position() == FixedPosition; if (!layoutObject.supportsPaintInvalidationStateCachedOffsets() || !next.m_cachedOffsetsEnabled) m_cachedOffsetsEnabled = false; if (establishesPaintInvalidationContainer) { // When we hit a new paint invalidation container, we don't need to // continue forcing a check for paint invalidation, since we're // descending into a different invalidation container. (For instance if // our parents were moved, the entire container will just move.) m_forcedSubtreeInvalidationWithinContainer = false; } else { if (m_cachedOffsetsEnabled) { if (fixed) { FloatPoint fixedOffset = layoutObject.localToAncestorPoint(FloatPoint(), &m_paintInvalidationContainer, TraverseDocumentBoundaries); m_paintOffset = LayoutSize(fixedOffset.x(), fixedOffset.y()); } else { LayoutSize offset = layoutObject.isBox() && !layoutObject.isTableRow() ? toLayoutBox(layoutObject).locationOffset() : LayoutSize(); m_paintOffset = next.m_paintOffset + offset; } if (layoutObject.isOutOfFlowPositioned() && !fixed) { if (LayoutObject* container = layoutObject.container()) { if (container->style()->hasInFlowPosition() && container->isLayoutInline()) m_paintOffset += toLayoutInline(container)->offsetForInFlowPositionedInline(toLayoutBox(layoutObject)); } } if (layoutObject.style()->hasInFlowPosition() && layoutObject.hasLayer()) m_paintOffset += layoutObject.layer()->offsetForInFlowPosition(); } m_clipped = !fixed && next.m_clipped; if (m_clipped) m_clipRect = next.m_clipRect; } if (m_cachedOffsetsEnabled && layoutObject.isSVGRoot()) { const LayoutSVGRoot& svgRoot = toLayoutSVGRoot(layoutObject); m_svgTransform = AffineTransform(svgRoot.localToBorderBoxTransform()); if (svgRoot.shouldApplyViewportClip()) addClipRectRelativeToPaintOffset(LayoutSize(svgRoot.pixelSnappedSize())); } applyClipIfNeeded(layoutObject); // FIXME: <http://bugs.webkit.org/show_bug.cgi?id=13443> Apply control clip if present. }
LayoutState::LayoutState(LayoutBox& layoutObject, bool containingBlockLogicalWidthChanged) : m_containingBlockLogicalWidthChanged(containingBlockLogicalWidthChanged), m_next(layoutObject.view()->layoutState()), m_layoutObject(layoutObject) { if (layoutObject.isLayoutFlowThread()) m_flowThread = toLayoutFlowThread(&layoutObject); else m_flowThread = m_next->flowThread(); m_paginationStateChanged = m_next->m_paginationStateChanged; layoutObject.view()->pushLayoutState(*this); m_heightOffsetForTableHeaders = m_next->heightOffsetForTableHeaders(); if (layoutObject.isLayoutFlowThread()) { // Entering a new pagination context. m_paginationOffset = LayoutSize(); m_isPaginated = true; return; } // Disable pagination for objects we don't support. For now this includes // overflow:scroll/auto, inline blocks and writing mode roots. Additionally, // pagination inside SVG is not allowed. if (layoutObject.getPaginationBreakability() == LayoutBox::ForbidBreaks || (m_layoutObject.isSVG() && !m_layoutObject.isSVGRoot())) { m_flowThread = nullptr; m_isPaginated = false; return; } m_isPaginated = m_next->m_isPaginated; if (!m_isPaginated) return; // Now adjust the pagination offset, so that we can easily figure out how far // away we are from the start of the pagination context. m_paginationOffset = m_next->m_paginationOffset; bool fixed = layoutObject.isOutOfFlowPositioned() && layoutObject.style()->position() == FixedPosition; if (fixed) return; m_paginationOffset = m_next->m_paginationOffset + layoutObject.locationOffset(); if (!layoutObject.isOutOfFlowPositioned()) return; if (LayoutObject* container = layoutObject.container()) { if (container->style()->hasInFlowPosition() && container->isLayoutInline()) { m_paginationOffset += toLayoutInline(container)->offsetForInFlowPositionedInline( layoutObject); } } // FIXME: <http://bugs.webkit.org/show_bug.cgi?id=13443> Apply control clip if // present. }
float TextAutosizer::inflate(LayoutObject* parent, InflateBehavior behavior, float multiplier) { Cluster* cluster = currentCluster(); bool hasTextChild = false; LayoutObject* child = nullptr; if (parent->isLayoutBlock() && (parent->childrenInline() || behavior == DescendToInnerBlocks)) child = toLayoutBlock(parent)->firstChild(); else if (parent->isLayoutInline()) child = toLayoutInline(parent)->firstChild(); while (child) { if (child->isText()) { hasTextChild = true; // We only calculate this multiplier on-demand to ensure the parent block of this text // has entered layout. if (!multiplier) multiplier = cluster->m_flags & SUPPRESSING ? 1.0f : clusterMultiplier(cluster); applyMultiplier(child, multiplier); // FIXME: Investigate why MarkOnlyThis is sufficient. if (parent->isLayoutInline()) child->setPreferredLogicalWidthsDirty(MarkOnlyThis); } else if (child->isLayoutInline()) { multiplier = inflate(child, behavior, multiplier); } else if (child->isLayoutBlock() && behavior == DescendToInnerBlocks && !classifyBlock(child, INDEPENDENT | EXPLICIT_WIDTH | SUPPRESSING)) { multiplier = inflate(child, behavior, multiplier); } child = child->nextSibling(); } if (hasTextChild) { applyMultiplier(parent, multiplier); // Parent handles line spacing. } else if (!parent->isListItem()) { // For consistency, a block with no immediate text child should always have a // multiplier of 1. applyMultiplier(parent, 1); } if (parent->isListItem()) { float multiplier = clusterMultiplier(cluster); applyMultiplier(parent, multiplier); // The list item has to be treated special because we can have a tree such that you have // a list item for a form inside it. The list marker then ends up inside the form and when // we try to get the clusterMultiplier we have the wrong cluster root to work from and get // the wrong value. LayoutListItem* item = toLayoutListItem(parent); if (LayoutListMarker* marker = item->marker()) { applyMultiplier(marker, multiplier); marker->setPreferredLogicalWidthsDirty(MarkOnlyThis); } } return multiplier; }
TracedLayoutObject::TracedLayoutObject(const LayoutObject& object) : m_address((unsigned long) &object) , m_isAnonymous(object.isAnonymous()) , m_isPositioned(object.isOutOfFlowPositioned()) , m_isRelPositioned(object.isRelPositioned()) , m_isStickyPositioned(object.isStickyPositioned()) , m_isFloating(object.isFloating()) , m_selfNeeds(object.selfNeedsLayout()) , m_positionedMovement(object.needsPositionedMovementLayout()) , m_childNeeds(object.normalChildNeedsLayout()) , m_posChildNeeds(object.posChildNeedsLayout()) , m_isTableCell(object.isTableCell()) , m_name(String(object.name()).isolatedCopy()) , m_absRect(object.absoluteBoundingBoxRect()) { if (Node* node = object.node()) { m_tag = String(node->nodeName()).isolatedCopy(); if (node->isElementNode()) { Element& element = toElement(*node); if (element.hasID()) m_id = String(element.getIdAttribute()).isolatedCopy(); if (element.hasClass()) { for (size_t i = 0; i < element.classNames().size(); ++i) { m_classNames.append( String(element.classNames()[i]).isolatedCopy()); } } } } // FIXME: When the fixmes in LayoutTreeAsText::writeLayoutObject() are // fixed, deduplicate it with this. if (object.isText()) { m_rect = LayoutRect(toLayoutText(object).linesBoundingBox()); } else if (object.isLayoutInline()) { m_rect = LayoutRect(toLayoutInline(object).linesBoundingBox()); } else if (object.isBox()) { m_rect = toLayoutBox(&object)->frameRect(); } if (m_isTableCell) { const LayoutTableCell& c = toLayoutTableCell(object); if (c.row() && c.row()->rowIndexWasSet() && c.hasCol() && (!c.row()->section() || !c.row()->section()->needsCellRecalc())) { m_row = c.rowIndex(); m_col = c.col(); m_rowSpan = c.rowSpan(); m_colSpan = c.colSpan(); } } for (LayoutObject* child = object.slowFirstChild(); child; child = child->nextSibling()) { m_children.append(adoptRef(new TracedLayoutObject(*child))); } }
static void deriveBorderBoxFromContainerContext(const LayoutObject& object, PaintPropertyTreeBuilderContext& context) { if (!object.isBoxModelObject()) return; const LayoutBoxModelObject& boxModelObject = toLayoutBoxModelObject(object); switch (object.styleRef().position()) { case StaticPosition: break; case RelativePosition: context.paintOffset += boxModelObject.offsetForInFlowPosition(); break; case AbsolutePosition: { context.currentTransform = context.transformForAbsolutePosition; context.paintOffset = context.paintOffsetForAbsolutePosition; // Absolutely positioned content in an inline should be positioned relative to the inline. const LayoutObject* container = context.containerForAbsolutePosition; if (container && container->isInFlowPositioned() && container->isLayoutInline()) { DCHECK(object.isBox()); context.paintOffset += toLayoutInline(container)->offsetForInFlowPositionedInline(toLayoutBox(object)); } context.currentClip = context.clipForAbsolutePosition; break; } case StickyPosition: context.paintOffset += boxModelObject.offsetForInFlowPosition(); break; case FixedPosition: context.currentTransform = context.transformForFixedPosition; context.paintOffset = context.paintOffsetForFixedPosition; context.currentClip = context.clipForFixedPosition; break; default: ASSERT_NOT_REACHED(); } if (boxModelObject.isBox() && (!boxModelObject.isSVG() || boxModelObject.isSVGRoot())) { // TODO(pdr): Several calls in this function walk back up the tree to calculate containers // (e.g., topLeftLocation, offsetForInFlowPosition*). The containing block and other // containers can be stored on PaintPropertyTreeBuilderContext instead of recomputing them. context.paintOffset.moveBy(toLayoutBox(boxModelObject).topLeftLocation()); // This is a weird quirk that table cells paint as children of table rows, // but their location have the row's location baked-in. // Similar adjustment is done in LayoutTableCell::offsetFromContainer(). if (boxModelObject.isTableCell()) { LayoutObject* parentRow = boxModelObject.parent(); ASSERT(parentRow && parentRow->isTableRow()); context.paintOffset.moveBy(-toLayoutBox(parentRow)->topLeftLocation()); } } }
void BlockPainter::paintContinuationOutlines(const PaintInfo& info, const LayoutPoint& paintOffset) { LayoutInline* inlineCont = m_layoutBlock.inlineElementContinuation(); if (inlineCont && inlineCont->style()->hasOutline() && inlineCont->style()->visibility() == VISIBLE) { LayoutInline* inlineLayoutObject = toLayoutInline(inlineCont->node()->layoutObject()); LayoutBlock* cb = m_layoutBlock.containingBlock(); bool inlineEnclosedInSelfPaintingLayer = false; for (LayoutBoxModelObject* box = inlineLayoutObject; box != cb; box = box->parent()->enclosingBoxModelObject()) { if (box->hasSelfPaintingLayer()) { inlineEnclosedInSelfPaintingLayer = true; break; } } // Do not add continuations for outline painting by our containing block if we are a relative positioned // anonymous block (i.e. have our own layer), paint them straightaway instead. This is because a block depends on layoutObjects in its continuation table being // in the same layer. if (!inlineEnclosedInSelfPaintingLayer && !m_layoutBlock.hasLayer()) { cb->addContinuationWithOutline(inlineLayoutObject); } else if (!inlineLayoutObject->firstLineBox() || (!inlineEnclosedInSelfPaintingLayer && m_layoutBlock.hasLayer())) { // The outline might be painted multiple times if multiple blocks have the same inline element continuation, and the inline has a self-painting layer. ScopeRecorder scopeRecorder(*info.context); InlinePainter(*inlineLayoutObject).paintOutline(info, paintOffset - m_layoutBlock.locationOffset() + inlineLayoutObject->containingBlock()->location()); } } ContinuationOutlineTableMap* table = continuationOutlineTable(); if (table->isEmpty()) return; OwnPtr<ListHashSet<LayoutInline*>> continuations = table->take(&m_layoutBlock); if (!continuations) return; LayoutPoint accumulatedPaintOffset = paintOffset; // Paint each continuation outline. ListHashSet<LayoutInline*>::iterator end = continuations->end(); for (ListHashSet<LayoutInline*>::iterator it = continuations->begin(); it != end; ++it) { // Need to add in the coordinates of the intervening blocks. LayoutInline* flow = *it; LayoutBlock* block = flow->containingBlock(); for ( ; block && block != &m_layoutBlock; block = block->containingBlock()) accumulatedPaintOffset.moveBy(block->location()); ASSERT(block); InlinePainter(*flow).paintOutline(info, accumulatedPaintOffset); } }
static void deriveBorderBoxFromContainerContext( const LayoutObject& object, PaintPropertyTreeBuilderContext& context) { if (!object.isBoxModelObject()) return; const LayoutBoxModelObject& boxModelObject = toLayoutBoxModelObject(object); switch (object.styleRef().position()) { case StaticPosition: break; case RelativePosition: context.current.paintOffset += boxModelObject.offsetForInFlowPosition(); break; case AbsolutePosition: { if (context.isUnderMultiColumnSpanner) { const LayoutObject* container = boxModelObject.container(); if (container != context.containerForAbsolutePosition) { // The container of the absolute-position is not in the normal tree- // walk order. context.containerForAbsolutePosition = toLayoutBoxModelObject(container); // The container is never a LayoutInline. In the example above // overrideContaineringBlockContextFromRealContainingBlock(), if we // change the container to an inline, there will be an anonymous // blocks created because the spanner is always a block. overrideContaineringBlockContextFromRealContainingBlock( toLayoutBlock(*container), context.current); } } else { DCHECK(context.containerForAbsolutePosition == boxModelObject.container()); context.current = context.absolutePosition; } // Absolutely positioned content in an inline should be positioned // relative to the inline. const LayoutObject* container = context.containerForAbsolutePosition; if (container && container->isInFlowPositioned() && container->isLayoutInline()) { DCHECK(object.isBox()); context.current.paintOffset += toLayoutInline(container)->offsetForInFlowPositionedInline( toLayoutBox(object)); } break; } case StickyPosition: context.current.paintOffset += boxModelObject.offsetForInFlowPosition(); break; case FixedPosition: if (context.isUnderMultiColumnSpanner) { // The container of the fixed-position object may or may not be in the // normal tree-walk order. overrideContaineringBlockContextFromRealContainingBlock( toLayoutBlock(*boxModelObject.container()), context.current); } else { context.current = context.fixedPosition; } break; default: ASSERT_NOT_REACHED(); } // SVGForeignObject needs paint offset because its viewport offset is baked // into its location(), while its localSVGTransform() doesn't contain the // offset. if (boxModelObject.isBox() && (!boxModelObject.isSVG() || boxModelObject.isSVGRoot() || boxModelObject.isSVGForeignObject())) { // TODO(pdr): Several calls in this function walk back up the tree to // calculate containers (e.g., topLeftLocation, offsetForInFlowPosition*). // The containing block and other containers can be stored on // PaintPropertyTreeBuilderContext instead of recomputing them. context.current.paintOffset.moveBy( toLayoutBox(boxModelObject).topLeftLocation()); // This is a weird quirk that table cells paint as children of table rows, // but their location have the row's location baked-in. // Similar adjustment is done in LayoutTableCell::offsetFromContainer(). if (boxModelObject.isTableCell()) { LayoutObject* parentRow = boxModelObject.parent(); ASSERT(parentRow && parentRow->isTableRow()); context.current.paintOffset.moveBy( -toLayoutBox(parentRow)->topLeftLocation()); } } }
void LineBoxList::dirtyLinesFromChangedChild(LayoutObject* container, LayoutObject* child) { if (!container->parent() || (container->isLayoutBlock() && (container->selfNeedsLayout() || !container->isLayoutBlockFlow()))) return; LayoutInline* inlineContainer = container->isLayoutInline() ? toLayoutInline(container) : 0; InlineBox* firstBox = inlineContainer ? inlineContainer->firstLineBoxIncludingCulling() : firstLineBox(); // If we have no first line box, then just bail early. if (!firstBox) { // For an empty inline, go ahead and propagate the check up to our parent, unless the parent // is already dirty. if (container->isInline() && !container->ancestorLineBoxDirty()) { container->parent()->dirtyLinesFromChangedChild(container); container->setAncestorLineBoxDirty(); // Mark the container to avoid dirtying the same lines again across multiple destroy() calls of the same subtree. } return; } // Try to figure out which line box we belong in. First try to find a previous // line box by examining our siblings. If we didn't find a line box, then use our // parent's first line box. RootInlineBox* box = 0; LayoutObject* curr = 0; ListHashSet<LayoutObject*, 16> potentialLineBreakObjects; potentialLineBreakObjects.add(child); for (curr = child->previousSibling(); curr; curr = curr->previousSibling()) { potentialLineBreakObjects.add(curr); if (curr->isFloatingOrOutOfFlowPositioned()) continue; if (curr->isReplaced()) { InlineBox* wrapper = toLayoutBox(curr)->inlineBoxWrapper(); if (wrapper) box = &wrapper->root(); } else if (curr->isText()) { InlineTextBox* textBox = toLayoutText(curr)->lastTextBox(); if (textBox) box = &textBox->root(); } else if (curr->isLayoutInline()) { InlineBox* lastSiblingBox = toLayoutInline(curr)->lastLineBoxIncludingCulling(); if (lastSiblingBox) box = &lastSiblingBox->root(); } if (box) break; } if (!box) { if (inlineContainer && !inlineContainer->alwaysCreateLineBoxes()) { // https://bugs.webkit.org/show_bug.cgi?id=60778 // We may have just removed a <br> with no line box that was our first child. In this case // we won't find a previous sibling, but firstBox can be pointing to a following sibling. // This isn't good enough, since we won't locate the root line box that encloses the removed // <br>. We have to just over-invalidate a bit and go up to our parent. if (!inlineContainer->ancestorLineBoxDirty()) { inlineContainer->parent()->dirtyLinesFromChangedChild(inlineContainer); inlineContainer->setAncestorLineBoxDirty(); // Mark the container to avoid dirtying the same lines again across multiple destroy() calls of the same subtree. } return; } box = &firstBox->root(); } // If we found a line box, then dirty it. if (box) { RootInlineBox* adjacentBox; box->markDirty(); // dirty the adjacent lines that might be affected // NOTE: we dirty the previous line because RootInlineBox objects cache // the address of the first object on the next line after a BR, which we may be // invalidating here. For more info, see how LayoutBlock::layoutInlineChildren // calls setLineBreakInfo with the result of findNextLineBreak. findNextLineBreak, // despite the name, actually returns the first LayoutObject after the BR. // <rdar://problem/3849947> "Typing after pasting line does not appear until after window resize." adjacentBox = box->prevRootBox(); if (adjacentBox) adjacentBox->markDirty(); adjacentBox = box->nextRootBox(); // If |child| or any of its immediately previous siblings with culled lineboxes is the object after a line-break in |box| or the linebox after it // then that means |child| actually sits on the linebox after |box| (or is its line-break object) and so we need to dirty it as well. if (adjacentBox && (potentialLineBreakObjects.contains(box->lineBreakObj()) || potentialLineBreakObjects.contains(adjacentBox->lineBreakObj()) || child->isBR() || isIsolated(container->style()->unicodeBidi()))) adjacentBox->markDirty(); } }
bool InspectorHighlight::buildNodeQuads(Node* node, FloatQuad* content, FloatQuad* padding, FloatQuad* border, FloatQuad* margin) { LayoutObject* layoutObject = node->layoutObject(); if (!layoutObject) return false; FrameView* containingView = layoutObject->frameView(); if (!containingView) return false; if (!layoutObject->isBox() && !layoutObject->isLayoutInline()) return false; LayoutRect contentBox; LayoutRect paddingBox; LayoutRect borderBox; LayoutRect marginBox; if (layoutObject->isBox()) { LayoutBox* layoutBox = toLayoutBox(layoutObject); // LayoutBox returns the "pure" content area box, exclusive of the scrollbars (if present), which also count towards the content area in CSS. const int verticalScrollbarWidth = layoutBox->verticalScrollbarWidth(); const int horizontalScrollbarHeight = layoutBox->horizontalScrollbarHeight(); contentBox = layoutBox->contentBoxRect(); contentBox.setWidth(contentBox.width() + verticalScrollbarWidth); contentBox.setHeight(contentBox.height() + horizontalScrollbarHeight); paddingBox = layoutBox->paddingBoxRect(); paddingBox.setWidth(paddingBox.width() + verticalScrollbarWidth); paddingBox.setHeight(paddingBox.height() + horizontalScrollbarHeight); borderBox = layoutBox->borderBoxRect(); marginBox = LayoutRect(borderBox.x() - layoutBox->marginLeft(), borderBox.y() - layoutBox->marginTop(), borderBox.width() + layoutBox->marginWidth(), borderBox.height() + layoutBox->marginHeight()); } else { LayoutInline* layoutInline = toLayoutInline(layoutObject); // LayoutInline's bounding box includes paddings and borders, excludes margins. borderBox = LayoutRect(layoutInline->linesBoundingBox()); paddingBox = LayoutRect(borderBox.x() + layoutInline->borderLeft(), borderBox.y() + layoutInline->borderTop(), borderBox.width() - layoutInline->borderLeft() - layoutInline->borderRight(), borderBox.height() - layoutInline->borderTop() - layoutInline->borderBottom()); contentBox = LayoutRect(paddingBox.x() + layoutInline->paddingLeft(), paddingBox.y() + layoutInline->paddingTop(), paddingBox.width() - layoutInline->paddingLeft() - layoutInline->paddingRight(), paddingBox.height() - layoutInline->paddingTop() - layoutInline->paddingBottom()); // Ignore marginTop and marginBottom for inlines. marginBox = LayoutRect(borderBox.x() - layoutInline->marginLeft(), borderBox.y(), borderBox.width() + layoutInline->marginWidth(), borderBox.height()); } *content = layoutObject->localToAbsoluteQuad(FloatRect(contentBox)); *padding = layoutObject->localToAbsoluteQuad(FloatRect(paddingBox)); *border = layoutObject->localToAbsoluteQuad(FloatRect(borderBox)); *margin = layoutObject->localToAbsoluteQuad(FloatRect(marginBox)); contentsQuadToViewport(containingView, *content); contentsQuadToViewport(containingView, *padding); contentsQuadToViewport(containingView, *border); contentsQuadToViewport(containingView, *margin); return true; }
void InlineFlowBoxPainter::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, const LayoutUnit lineTop, const LayoutUnit lineBottom) { LayoutRect overflowRect(m_inlineFlowBox.visualOverflowRect(lineTop, lineBottom)); m_inlineFlowBox.flipForWritingMode(overflowRect); overflowRect.moveBy(paintOffset); if (!paintInfo.rect.intersects(pixelSnappedIntRect(overflowRect))) return; if (paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) { // Add ourselves to the paint info struct's list of inlines that need to paint their // outlines. if (m_inlineFlowBox.layoutObject().style()->visibility() == VISIBLE && m_inlineFlowBox.layoutObject().style()->hasOutline() && !m_inlineFlowBox.isRootInlineBox()) { LayoutInline& inlineFlow = toLayoutInline(m_inlineFlowBox.layoutObject()); LayoutBlock* cb = 0; bool containingBlockPaintsContinuationOutline = inlineFlow.continuation() || inlineFlow.isInlineElementContinuation(); if (containingBlockPaintsContinuationOutline) { // FIXME: See https://bugs.webkit.org/show_bug.cgi?id=54690. We currently don't reconnect inline continuations // after a child removal. As a result, those merged inlines do not get seperated and hence not get enclosed by // anonymous blocks. In this case, it is better to bail out and paint it ourself. LayoutBlock* enclosingAnonymousBlock = m_inlineFlowBox.layoutObject().containingBlock(); if (!enclosingAnonymousBlock->isAnonymousBlock()) { containingBlockPaintsContinuationOutline = false; } else { cb = enclosingAnonymousBlock->containingBlock(); for (LayoutBoxModelObject* box = m_inlineFlowBox.boxModelObject(); box != cb; box = box->parent()->enclosingBoxModelObject()) { if (box->hasSelfPaintingLayer()) { containingBlockPaintsContinuationOutline = false; break; } } } } if (containingBlockPaintsContinuationOutline) { // Add ourselves to the containing block of the entire continuation so that it can // paint us atomically. cb->addContinuationWithOutline(toLayoutInline(m_inlineFlowBox.layoutObject().node()->layoutObject())); } else if (!inlineFlow.isInlineElementContinuation()) { paintInfo.outlineObjects()->add(&inlineFlow); } } } else if (paintInfo.phase == PaintPhaseMask) { DrawingRecorder recorder(*paintInfo.context, m_inlineFlowBox, DisplayItem::paintPhaseToDrawingType(paintInfo.phase), pixelSnappedIntRect(overflowRect)); if (!recorder.canUseCachedDrawing()) paintMask(paintInfo, paintOffset); return; } else if (paintInfo.phase == PaintPhaseForeground) { // Paint our background, border and box-shadow. paintBoxDecorationBackground(paintInfo, paintOffset); } // Paint our children. if (paintInfo.phase != PaintPhaseSelfOutline) { PaintInfo childInfo(paintInfo); childInfo.phase = paintInfo.phase == PaintPhaseChildOutlines ? PaintPhaseOutline : paintInfo.phase; if (childInfo.paintingRoot && childInfo.paintingRoot->isDescendantOf(&m_inlineFlowBox.layoutObject())) childInfo.paintingRoot = 0; else childInfo.updatePaintingRootForChildren(&m_inlineFlowBox.layoutObject()); for (InlineBox* curr = m_inlineFlowBox.firstChild(); curr; curr = curr->nextOnLine()) { if (curr->layoutObject().isText() || !curr->boxModelObject()->hasSelfPaintingLayer()) curr->paint(childInfo, paintOffset, lineTop, lineBottom); } } }
void LayoutReplaced::computePositionedLogicalWidth( LogicalExtentComputedValues& computedValues) const { // The following is based off of the W3C Working Draft from April 11, 2006 of // CSS 2.1: Section 10.3.8 "Absolutely positioned, replaced elements" // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-width> // (block-style-comments in this function correspond to text from the spec and // the numbers correspond to numbers in spec). // We don't use containingBlock(), since we may be positioned by an enclosing // relative positioned inline. const LayoutBoxModelObject* containerBlock = toLayoutBoxModelObject(container()); const LayoutUnit containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock); const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, false); // To match WinIE, in quirks mode use the parent's 'direction' property // instead of the the container block's. TextDirection containerDirection = containerBlock->style()->direction(); // Variables to solve. bool isHorizontal = isHorizontalWritingMode(); Length logicalLeft = style()->logicalLeft(); Length logicalRight = style()->logicalRight(); Length marginLogicalLeft = isHorizontal ? style()->marginLeft() : style()->marginTop(); Length marginLogicalRight = isHorizontal ? style()->marginRight() : style()->marginBottom(); LayoutUnit& marginLogicalLeftAlias = style()->isLeftToRightDirection() ? computedValues.m_margins.m_start : computedValues.m_margins.m_end; LayoutUnit& marginLogicalRightAlias = style()->isLeftToRightDirection() ? computedValues.m_margins.m_end : computedValues.m_margins.m_start; // --------------------------------------------------------------------------- // 1. The used value of 'width' is determined as for inline replaced // elements. // --------------------------------------------------------------------------- // NOTE: This value of width is final in that the min/max width calculations // are dealt with in computeReplacedWidth(). This means that the steps to // produce correct max/min in the non-replaced version, are not necessary. computedValues.m_extent = computeReplacedLogicalWidth() + borderAndPaddingLogicalWidth(); const LayoutUnit availableSpace = containerLogicalWidth - computedValues.m_extent; // --------------------------------------------------------------------------- // 2. If both 'left' and 'right' have the value 'auto', then if 'direction' // of the containing block is 'ltr', set 'left' to the static position; // else if 'direction' is 'rtl', set 'right' to the static position. // --------------------------------------------------------------------------- // see FIXME 1 computeInlineStaticDistance(logicalLeft, logicalRight, this, containerBlock, containerLogicalWidth); // --------------------------------------------------------------------------- // 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left' // or 'margin-right' with '0'. // --------------------------------------------------------------------------- if (logicalLeft.isAuto() || logicalRight.isAuto()) { if (marginLogicalLeft.isAuto()) marginLogicalLeft.setValue(Fixed, 0); if (marginLogicalRight.isAuto()) marginLogicalRight.setValue(Fixed, 0); } // --------------------------------------------------------------------------- // 4. If at this point both 'margin-left' and 'margin-right' are still 'auto', // solve the equation under the extra constraint that the two margins must // get equal values, unless this would make them negative, in which case // when the direction of the containing block is 'ltr' ('rtl'), set // 'margin-left' ('margin-right') to zero and solve for 'margin-right' // ('margin-left'). // --------------------------------------------------------------------------- LayoutUnit logicalLeftValue; LayoutUnit logicalRightValue; if (marginLogicalLeft.isAuto() && marginLogicalRight.isAuto()) { // 'left' and 'right' cannot be 'auto' due to step 3 ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto())); logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth); logicalRightValue = valueForLength(logicalRight, containerLogicalWidth); LayoutUnit difference = availableSpace - (logicalLeftValue + logicalRightValue); if (difference > LayoutUnit()) { marginLogicalLeftAlias = difference / 2; // split the difference marginLogicalRightAlias = difference - marginLogicalLeftAlias; // account for odd valued differences } else { // Use the containing block's direction rather than the parent block's // per CSS 2.1 reference test abspos-replaced-width-margin-000. if (containerDirection == LTR) { marginLogicalLeftAlias = LayoutUnit(); marginLogicalRightAlias = difference; // will be negative } else { marginLogicalLeftAlias = difference; // will be negative marginLogicalRightAlias = LayoutUnit(); } } // ------------------------------------------------------------------------- // 5. If at this point there is an 'auto' left, solve the equation for that // value. // ------------------------------------------------------------------------- } else if (logicalLeft.isAuto()) { marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth); marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth); logicalRightValue = valueForLength(logicalRight, containerLogicalWidth); // Solve for 'left' logicalLeftValue = availableSpace - (logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias); } else if (logicalRight.isAuto()) { marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth); marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth); logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth); // Solve for 'right' logicalRightValue = availableSpace - (logicalLeftValue + marginLogicalLeftAlias + marginLogicalRightAlias); } else if (marginLogicalLeft.isAuto()) { marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth); logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth); logicalRightValue = valueForLength(logicalRight, containerLogicalWidth); // Solve for 'margin-left' marginLogicalLeftAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalRightAlias); } else if (marginLogicalRight.isAuto()) { marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth); logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth); logicalRightValue = valueForLength(logicalRight, containerLogicalWidth); // Solve for 'margin-right' marginLogicalRightAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalLeftAlias); } else { // Nothing is 'auto', just calculate the values. marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth); marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth); logicalRightValue = valueForLength(logicalRight, containerLogicalWidth); logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth); // If the containing block is right-to-left, then push the left position as // far to the right as possible if (containerDirection == RTL) { int totalLogicalWidth = (computedValues.m_extent + logicalLeftValue + logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias) .toInt(); logicalLeftValue = containerLogicalWidth - (totalLogicalWidth - logicalLeftValue); } } // --------------------------------------------------------------------------- // 6. If at this point the values are over-constrained, ignore the value for // either 'left' (in case the 'direction' property of the containing block // is 'rtl') or 'right' (in case 'direction' is 'ltr') and solve for that // value. // --------------------------------------------------------------------------- // NOTE: Constraints imposed by the width of the containing block and its // content have already been accounted for above. // // FIXME: Deal with differing writing modes here. Our offset needs to be in // the containing block's coordinate space, so that // can make the result here rather complicated to compute. // // Use computed values to calculate the horizontal position. // // FIXME: This hack is needed to calculate the logical left position for a // 'rtl' relatively positioned, inline containing block because right now, it // is using the logical left position of the first line box when really it // should use the last line box. When this is fixed elsewhere, this block // should be removed. if (containerBlock->isLayoutInline() && !containerBlock->style()->isLeftToRightDirection()) { const LayoutInline* flow = toLayoutInline(containerBlock); InlineFlowBox* firstLine = flow->firstLineBox(); InlineFlowBox* lastLine = flow->lastLineBox(); if (firstLine && lastLine && firstLine != lastLine) { computedValues.m_position = logicalLeftValue + marginLogicalLeftAlias + lastLine->borderLogicalLeft() + (lastLine->logicalLeft() - firstLine->logicalLeft()); return; } } LayoutUnit logicalLeftPos = logicalLeftValue + marginLogicalLeftAlias; computeLogicalLeftPositionedOffset(logicalLeftPos, this, computedValues.m_extent, containerBlock, containerLogicalWidth); computedValues.m_position = logicalLeftPos; }
InlineFlowBox* SnapToLinesLayouter::findFirstLineBox() const { if (!m_cueBox.firstChild()->isLayoutInline()) return nullptr; return toLayoutInline(m_cueBox.firstChild())->firstLineBox(); }