void SVGRectElement::svgAttributeChanged(const QualifiedName& attrName) { if (!isSupportedAttribute(attrName)) { SVGGeometryElement::svgAttributeChanged(attrName); return; } SVGElement::InvalidationGuard invalidationGuard(this); bool isLengthAttribute = attrName == SVGNames::xAttr || attrName == SVGNames::yAttr || attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr || attrName == SVGNames::rxAttr || attrName == SVGNames::ryAttr; if (isLengthAttribute) updateRelativeLengthsInformation(); RenderSVGShape* renderer = toRenderSVGShape(this->renderer()); if (!renderer) return; if (isLengthAttribute) { renderer->setNeedsShapeUpdate(); RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); return; } ASSERT_NOT_REACHED(); }
void SVGRenderSupport::computeContainerBoundingBoxes(const RenderObject* 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 (RenderObject* current = container->slowFirstChild(); current; current = current->nextSibling()) { if (current->isSVGHiddenContainer()) continue; // Don't include elements in the union that do not render. if (current->isSVGShape() && toRenderSVGShape(current)->isShapeEmpty()) continue; const AffineTransform& transform = current->localToParentTransform(); updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, transform.mapRect(current->objectBoundingBox())); strokeBoundingBox.unite(transform.mapRect(current->paintInvalidationRectInLocalCoordinates())); } paintInvalidationBoundingBox = strokeBoundingBox; }
void SVGPolyElement::svgAttributeChanged(const QualifiedName& attrName) { if (!isSupportedAttribute(attrName)) { SVGGeometryElement::svgAttributeChanged(attrName); return; } SVGElementInstance::InvalidationGuard invalidationGuard(this); RenderSVGShape* renderer = toRenderSVGShape(this->renderer()); if (!renderer) return; if (attrName == SVGNames::pointsAttr) { renderer->setNeedsShapeUpdate(); RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); return; } if (SVGLangSpace::isKnownAttribute(attrName) || SVGExternalResourcesRequired::isKnownAttribute(attrName)) { RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); return; } ASSERT_NOT_REACHED(); }
void SVGEllipseElement::svgAttributeChanged(const QualifiedName& attrName) { if (!isSupportedAttribute(attrName)) { SVGGraphicsElement::svgAttributeChanged(attrName); return; } SVGElementInstance::InvalidationGuard invalidationGuard(this); if (attrName == SVGNames::cxAttr || attrName == SVGNames::cyAttr || attrName == SVGNames::rxAttr || attrName == SVGNames::ryAttr) { invalidateSVGPresentationAttributeStyle(); return; } RenderSVGShape* renderer = toRenderSVGShape(this->renderer()); if (!renderer) return; if (SVGLangSpace::isKnownAttribute(attrName) || SVGExternalResourcesRequired::isKnownAttribute(attrName)) { RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer); return; } ASSERT_NOT_REACHED(); }
void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout) { bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start); bool transformChanged = transformToRootChanged(start); HashSet<RenderObject*> notlayoutedObjects; for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { bool needsLayout = selfNeedsLayout; if (transformChanged) { // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true). if (child->isSVGText()) toRenderSVGText(child)->setNeedsTextMetricsUpdate(); needsLayout = 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() ? static_cast<SVGElement*>(child->node()) : 0) { if (element->isStyled() && static_cast<SVGStyledElement*>(element)->hasRelativeLengths()) { // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object if (child->isSVGShape()) toRenderSVGShape(child)->setNeedsShapeUpdate(); needsLayout = true; } } } if (needsLayout) { child->setNeedsLayout(true, false); child->layout(); } else { if (child->needsLayout()) child->layout(); else if (layoutSizeChanged) notlayoutedObjects.add(child); } ASSERT(!child->needsLayout()); } if (!layoutSizeChanged) { ASSERT(notlayoutedObjects.isEmpty()); return; } // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path. HashSet<RenderObject*>::iterator end = notlayoutedObjects.end(); for (HashSet<RenderObject*>::iterator it = notlayoutedObjects.begin(); it != end; ++it) invalidateResourcesOfChildren(*it); }
bool SVGGeometryElement::isPointInStroke(PassRefPtr<SVGPointTearOff> point) const { document().updateLayoutIgnorePendingStylesheets(); // FIXME: Eventually we should support isPointInStroke for display:none elements. if (!renderer() || !renderer()->isSVGShape()) return false; HitTestRequest request(HitTestRequest::ReadOnly); PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_GEOMETRY_HITTESTING, request, renderer()->style()->pointerEvents()); hitRules.canHitFill = false; return toRenderSVGShape(renderer())->nodeAtFloatPointInternal(request, point->target()->value(), hitRules); }
void SVGRenderSupport::layoutChildren(RenderObject* 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 (RenderObject* 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()) toRenderSVGText(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 RenderSVGShape to update its shape object if (child->isSVGShape()) { toRenderSVGShape(child)->setNeedsShapeUpdate(); } else if (child->isSVGText()) { toRenderSVGText(child)->setNeedsTextMetricsUpdate(); toRenderSVGText(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 RenderView::layout()). if (forceLayout && !child->isSVGResourceContainer()) layoutScope.setNeedsLayout(child); // Lay out any referenced resources before the child. layoutResourcesIfNeeded(child); child->layoutIfNeeded(); } }
void SVGRectElement::svgAttributeChanged(const QualifiedName& attrName) { if (!isSupportedAttribute(attrName)) { SVGGraphicsElement::svgAttributeChanged(attrName); return; } SVGElementInstance::InvalidationGuard invalidationGuard(this); if (attrName == SVGNames::xAttr || attrName == SVGNames::yAttr || attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr) { invalidateSVGPresentationAttributeStyle(); return; } bool isLengthAttribute = attrName == SVGNames::rxAttr || attrName == SVGNames::ryAttr; if (isLengthAttribute) updateRelativeLengthsInformation(); RenderSVGShape* renderer = toRenderSVGShape(this->renderer()); if (!renderer) return; if (isLengthAttribute) { renderer->setNeedsShapeUpdate(); RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer); return; } if (SVGLangSpace::isKnownAttribute(attrName) || SVGExternalResourcesRequired::isKnownAttribute(attrName)) { RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer); return; } ASSERT_NOT_REACHED(); }
void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout) { bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start); bool transformChanged = transformToRootChanged(start); HashSet<RenderObject*> notlayoutedObjects; for (RenderObject* child = start->slowFirstChild(); child; child = child->nextSibling()) { bool needsLayout = selfNeedsLayout; bool childEverHadLayout = child->everHadLayout(); if (transformChanged) { // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true). if (child->isSVGText()) toRenderSVGText(child)->setNeedsTextMetricsUpdate(); needsLayout = 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()) { // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object if (child->isSVGShape()) { toRenderSVGShape(child)->setNeedsShapeUpdate(); } else if (child->isSVGText()) { toRenderSVGText(child)->setNeedsTextMetricsUpdate(); toRenderSVGText(child)->setNeedsPositioningValuesUpdate(); } needsLayout = 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 RenderView::layout()). if (needsLayout && !child->isSVGResourceContainer()) layoutScope.setNeedsLayout(child); layoutResourcesIfNeeded(child); if (child->needsLayout()) { child->layout(); // Renderers are responsible for repainting themselves when changing, except // for the initial paint to avoid potential double-painting caused by non-sensical "old" bounds. // We could handle this in the individual objects, but for now it's easier to have // parent containers call repaint(). (RenderBlock::layout* has similar logic.) if (!childEverHadLayout && !RuntimeEnabledFeatures::repaintAfterLayoutEnabled()) child->paintInvalidationForWholeRenderer(); } else if (layoutSizeChanged) { notlayoutedObjects.add(child); } } if (!layoutSizeChanged) { ASSERT(notlayoutedObjects.isEmpty()); return; } // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path. HashSet<RenderObject*>::iterator end = notlayoutedObjects.end(); for (HashSet<RenderObject*>::iterator it = notlayoutedObjects.begin(); it != end; ++it) invalidateResourcesOfChildren(*it); }
void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout) { bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start); bool transformChanged = transformToRootChanged(start); bool hasSVGShadow = rendererHasSVGShadow(start); bool needsBoundariesUpdate = start->needsBoundariesUpdate(); HashSet<RenderObject*> notlayoutedObjects; for (RenderObject* child = start->firstChildSlow(); child; child = child->nextSibling()) { bool needsLayout = selfNeedsLayout; bool childEverHadLayout = child->everHadLayout(); if (needsBoundariesUpdate && hasSVGShadow) { // If we have a shadow, our shadow is baked into our children's cached boundaries, // so they need to update. child->setNeedsBoundariesUpdate(); needsLayout = true; } if (transformChanged) { // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true). if (child->isSVGText()) toRenderSVGText(child)->setNeedsTextMetricsUpdate(); needsLayout = 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()) { // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object if (child->isSVGShape()) toRenderSVGShape(child)->setNeedsShapeUpdate(); else if (child->isSVGText()) { toRenderSVGText(child)->setNeedsTextMetricsUpdate(); toRenderSVGText(child)->setNeedsPositioningValuesUpdate(); } needsLayout = true; } } } if (needsLayout) child->setNeedsLayout(MarkOnlyThis); if (child->needsLayout()) { toRenderElement(child)->layout(); // Renderers are responsible for repainting themselves when changing, except // for the initial paint to avoid potential double-painting caused by non-sensical "old" bounds. // We could handle this in the individual objects, but for now it's easier to have // parent containers call repaint(). (RenderBlock::layout* has similar logic.) if (!childEverHadLayout) child->repaint(); } else if (layoutSizeChanged) notlayoutedObjects.add(child); ASSERT(!child->needsLayout()); } if (!layoutSizeChanged) { ASSERT(notlayoutedObjects.isEmpty()); return; } // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path. HashSet<RenderObject*>::iterator end = notlayoutedObjects.end(); for (HashSet<RenderObject*>::iterator it = notlayoutedObjects.begin(); it != end; ++it) invalidateResourcesOfChildren(*it); }