void SVGRenderSupport::computeContainerBoundingBoxes(const RenderElement& container, FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox) { objectBoundingBox = FloatRect(); objectBoundingBoxValid = false; strokeBoundingBox = FloatRect(); // When computing the strokeBoundingBox, we use the repaintRects 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.firstChild(); current; current = current->nextSibling()) { if (current->isSVGHiddenContainer()) continue; const AffineTransform& transform = current->localToParentTransform(); if (transform.isIdentity()) { updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, current->objectBoundingBox()); strokeBoundingBox.unite(current->repaintRectInLocalCoordinates()); } else { updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, transform.mapRect(current->objectBoundingBox())); strokeBoundingBox.unite(transform.mapRect(current->repaintRectInLocalCoordinates())); } } repaintBoundingBox = strokeBoundingBox; }
void RenderListItem::insertOrMoveMarkerRendererIfNeeded() { // Sanity check the location of our marker. if (!m_marker) return; RenderElement* currentParent = m_marker->parent(); RenderBlock* newParent = getParentOfFirstLineBox(this, m_marker); if (!newParent) { // If the marker is currently contained inside an anonymous box, // then we are the only item in that anonymous box (since no line box // parent was found). It's ok to just leave the marker where it is // in this case. if (currentParent && currentParent->isAnonymousBlock()) return; newParent = this; } if (newParent != currentParent) { // Removing and adding the marker can trigger repainting in // containers other than ourselves, so we need to disable LayoutState. LayoutStateDisabler layoutStateDisabler(&view()); m_marker->removeFromParent(); newParent->addChild(m_marker, firstNonMarkerChild(newParent)); m_marker->updateMarginsAndContent(); // If current parent is an anonymous block that has lost all its children, destroy it. if (currentParent && currentParent->isAnonymousBlock() && !currentParent->firstChild() && !toRenderBlock(currentParent)->continuation()) currentParent->destroy(); } }
void RenderListItem::insertOrMoveMarkerRendererIfNeeded() { // Sanity check the location of our marker. if (!m_marker) return; // FIXME: Do not even try to reposition the marker when we are not in layout // until after we fixed webkit.org/b/163789. if (!view().frameView().isInRenderTreeLayout()) return; RenderElement* currentParent = m_marker->parent(); RenderBlock* newParent = getParentOfFirstLineBox(*this, *m_marker); if (!newParent) { // If the marker is currently contained inside an anonymous box, // then we are the only item in that anonymous box (since no line box // parent was found). It's ok to just leave the marker where it is // in this case. if (currentParent && currentParent->isAnonymousBlock()) return; if (multiColumnFlowThread()) newParent = multiColumnFlowThread(); else newParent = this; } if (newParent != currentParent) { // Removing and adding the marker can trigger repainting in // containers other than ourselves, so we need to disable LayoutState. LayoutStateDisabler layoutStateDisabler(view()); // Mark the parent dirty so that when the marker gets inserted into the tree // and dirties ancestors, it stops at the parent. newParent->setChildNeedsLayout(MarkOnlyThis); m_marker->setNeedsLayout(MarkOnlyThis); m_marker->removeFromParent(); newParent->addChild(m_marker, firstNonMarkerChild(*newParent)); m_marker->updateMarginsAndContent(); // If current parent is an anonymous block that has lost all its children, destroy it. if (currentParent && currentParent->isAnonymousBlock() && !currentParent->firstChild() && !downcast<RenderBlock>(*currentParent).continuation()) currentParent->destroy(); } }
void SVGRenderSupport::layoutChildren(RenderElement& 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.firstChild(); 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. for (auto child : notlayoutedObjects) invalidateResourcesOfChildren(*child); }
bool TextAutoSizingValue::adjustNodeSizes() { bool objectsRemoved = false; // Remove stale nodes. Nodes may have had their renderers detached. We'll // also need to remove the style from the documents m_textAutoSizedNodes // collection. Return true indicates we need to do that removal. Vector<RefPtr<Node> > nodesForRemoval; HashSet<RefPtr<Node> >::iterator end = m_autoSizedNodes.end(); for (HashSet<RefPtr<Node> >::iterator i = m_autoSizedNodes.begin(); i != end; ++i) { RefPtr<Node> autoSizingNode = *i; RenderText* text = static_cast<RenderText*>(autoSizingNode->renderer()); if (!text || !text->style().textSizeAdjust().isAuto() || !text->candidateComputedTextSize()) { // remove node. nodesForRemoval.append(autoSizingNode); objectsRemoved = true; } } unsigned count = nodesForRemoval.size(); for (unsigned i = 0; i < count; i++) m_autoSizedNodes.remove(nodesForRemoval[i]); // If we only have one piece of text with the style on the page don't // adjust it's size. if (m_autoSizedNodes.size() <= 1) return objectsRemoved; // Compute average size float cumulativeSize = 0; end = m_autoSizedNodes.end(); for (HashSet<RefPtr<Node> >::iterator i = m_autoSizedNodes.begin(); i != end; ++i) { RefPtr<Node> autoSizingNode = *i; RenderText* renderText = static_cast<RenderText*>(autoSizingNode->renderer()); cumulativeSize += renderText->candidateComputedTextSize(); } float averageSize = roundf(cumulativeSize / m_autoSizedNodes.size()); // Adjust sizes bool firstPass = true; end = m_autoSizedNodes.end(); for (HashSet<RefPtr<Node> >::iterator i = m_autoSizedNodes.begin(); i != end; ++i) { const RefPtr<Node>& autoSizingNode = *i; RenderText* text = static_cast<RenderText*>(autoSizingNode->renderer()); if (text && text->style().fontDescription().computedSize() != averageSize) { float specifiedSize = text->style().fontDescription().specifiedSize(); float scaleChange = averageSize / specifiedSize; if (scaleChange > MAX_SCALE_INCREASE && firstPass) { firstPass = false; averageSize = roundf(specifiedSize * MAX_SCALE_INCREASE); scaleChange = averageSize / specifiedSize; } RefPtr<RenderStyle> style = cloneRenderStyleWithState(text->style()); FontDescription fontDescription = style->fontDescription(); fontDescription.setComputedSize(averageSize); style->setFontDescription(fontDescription); style->font().update(autoSizingNode->document().ensureStyleResolver().fontSelector()); text->parent()->setStyle(style.releaseNonNull()); RenderElement* parentRenderer = text->parent(); if (parentRenderer->isAnonymousBlock()) parentRenderer = parentRenderer->parent(); // If we have a list we should resize ListMarkers separately. RenderObject* listMarkerRenderer = parentRenderer->firstChild(); if (listMarkerRenderer->isListMarker()) { RefPtr<RenderStyle> style = cloneRenderStyleWithState(listMarkerRenderer->style()); style->setFontDescription(fontDescription); style->font().update(autoSizingNode->document().ensureStyleResolver().fontSelector()); toRenderListMarker(*listMarkerRenderer).setStyle(style.releaseNonNull()); } // Resize the line height of the parent. const RenderStyle& parentStyle = parentRenderer->style(); Length lineHeightLength = parentStyle.specifiedLineHeight(); int specifiedLineHeight = 0; if (lineHeightLength.isPercent()) specifiedLineHeight = minimumValueForLength(lineHeightLength, fontDescription.specifiedSize()); else specifiedLineHeight = lineHeightLength.value(); int lineHeight = specifiedLineHeight * scaleChange; if (!lineHeightLength.isFixed() || lineHeightLength.value() != lineHeight) { RefPtr<RenderStyle> newParentStyle = cloneRenderStyleWithState(parentStyle); newParentStyle->setLineHeight(Length(lineHeight, Fixed)); newParentStyle->setSpecifiedLineHeight(lineHeightLength); newParentStyle->setFontDescription(fontDescription); newParentStyle->font().update(autoSizingNode->document().ensureStyleResolver().fontSelector()); parentRenderer->setStyle(newParentStyle.releaseNonNull()); } } } return objectsRemoved; }