void SVGLayoutSupport::layoutChildren(LayoutObject* firstChild, bool forceLayout, bool screenScalingFactorChanged, bool layoutSizeChanged) { for (LayoutObject* child = firstChild; child; child = child->nextSibling()) { bool forceChildLayout = forceLayout; if (screenScalingFactorChanged) { // If the screen scaling factor changed we need to update the text // metrics (note: this also happens for layoutSizeChanged=true). if (child->isSVGText()) toLayoutSVGText(child)->setNeedsTextMetricsUpdate(); forceChildLayout = true; } if (layoutSizeChanged) { // When selfNeedsLayout is false and the layout size changed, we have to // check whether this child uses relative lengths if (SVGElement* element = child->node()->isSVGElement() ? toSVGElement(child->node()) : 0) { if (element->hasRelativeLengths()) { // FIXME: this should be done on invalidation, not during layout. // When the layout size changed and when using relative values tell // the LayoutSVGShape to update its shape object if (child->isSVGShape()) { toLayoutSVGShape(child)->setNeedsShapeUpdate(); } else if (child->isSVGText()) { toLayoutSVGText(child)->setNeedsTextMetricsUpdate(); toLayoutSVGText(child)->setNeedsPositioningValuesUpdate(); } forceChildLayout = true; } } } // Resource containers are nasty: they can invalidate clients outside the // current SubtreeLayoutScope. // Since they only care about viewport size changes (to resolve their // relative lengths), we trigger their invalidation directly from // SVGSVGElement::svgAttributeChange() or at a higher SubtreeLayoutScope (in // LayoutView::layout()). We do not create a SubtreeLayoutScope for // resources because their ability to reference each other leads to circular // layout. We protect against that within the layout code for resources, but // it causes assertions if we use a SubTreeLayoutScope for them. if (child->isSVGResourceContainer()) { // Lay out any referenced resources before the child. layoutResourcesIfNeeded(child); child->layoutIfNeeded(); } else { SubtreeLayoutScope layoutScope(*child); if (forceChildLayout) layoutScope.setNeedsLayout(child, LayoutInvalidationReason::SvgChanged); // Lay out any referenced resources before the child. layoutResourcesIfNeeded(child); child->layoutIfNeeded(); } } }
void SVGLayoutSupport::layoutChildren(LayoutObject* start, bool selfNeedsLayout) { // When hasRelativeLengths() is false, no descendants have relative lengths // (hence no one is interested in viewport size changes). bool layoutSizeChanged = toSVGElement(start->node())->hasRelativeLengths() && layoutSizeOfNearestViewportChanged(start); bool transformChanged = transformToRootChanged(start); for (LayoutObject* child = start->slowFirstChild(); child; child = child->nextSibling()) { bool forceLayout = selfNeedsLayout; if (transformChanged) { // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true). if (child->isSVGText()) toLayoutSVGText(child)->setNeedsTextMetricsUpdate(); forceLayout = true; } if (layoutSizeChanged) { // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths if (SVGElement* element = child->node()->isSVGElement() ? toSVGElement(child->node()) : 0) { if (element->hasRelativeLengths()) { // FIXME: this should be done on invalidation, not during layout. // When the layout size changed and when using relative values tell the LayoutSVGShape to update its shape object if (child->isSVGShape()) { toLayoutSVGShape(child)->setNeedsShapeUpdate(); } else if (child->isSVGText()) { toLayoutSVGText(child)->setNeedsTextMetricsUpdate(); toLayoutSVGText(child)->setNeedsPositioningValuesUpdate(); } forceLayout = true; } } } SubtreeLayoutScope layoutScope(*child); // Resource containers are nasty: they can invalidate clients outside the current SubtreeLayoutScope. // Since they only care about viewport size changes (to resolve their relative lengths), we trigger // their invalidation directly from SVGSVGElement::svgAttributeChange() or at a higher // SubtreeLayoutScope (in LayoutView::layout()). if (forceLayout && !child->isSVGResourceContainer()) layoutScope.setNeedsLayout(child, LayoutInvalidationReason::SvgChanged); // Lay out any referenced resources before the child. layoutResourcesIfNeeded(child); child->layoutIfNeeded(); } }
void SVGLayoutSupport::computeContainerBoundingBoxes(const LayoutObject* container, FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, FloatRect& strokeBoundingBox, FloatRect& paintInvalidationBoundingBox) { objectBoundingBox = FloatRect(); objectBoundingBoxValid = false; strokeBoundingBox = FloatRect(); // When computing the strokeBoundingBox, we use the paintInvalidationRects of the container's children so that the container's stroke includes // the resources applied to the children (such as clips and filters). This allows filters applied to containers to correctly bound // the children, and also improves inlining of SVG content, as the stroke bound is used in that situation also. for (LayoutObject* current = container->slowFirstChild(); current; current = current->nextSibling()) { if (current->isSVGHiddenContainer()) continue; // Don't include elements in the union that do not layout. if (current->isSVGShape() && toLayoutSVGShape(current)->isShapeEmpty()) continue; if (current->isSVGText() && !toLayoutSVGText(current)->isObjectBoundingBoxValid()) continue; const AffineTransform& transform = current->localToSVGParentTransform(); updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, transform.mapRect(current->objectBoundingBox())); strokeBoundingBox.unite(transform.mapRect(current->paintInvalidationRectInLocalSVGCoordinates())); } paintInvalidationBoundingBox = strokeBoundingBox; }
const LayoutSVGText* LayoutSVGText::locateLayoutSVGTextAncestor(const LayoutObject* start) { ASSERT(start); while (start && !start->isSVGText()) start = start->parent(); if (!start || !start->isSVGText()) return nullptr; return toLayoutSVGText(start); }