/* * Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann * Copyright (C) 2004, 2005, 2007, 2008 Rob Buis * Copyright (C) 2007 Eric Seidel * Copyright (C) 2009 Google, Inc. All rights reserved. * Copyright (C) 2009 Dirk Schulze * * 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 "config.h" #include "RenderSVGContainer.h" #include "GraphicsContext.h" #include "HitTestRequest.h" #include "LayoutRepainter.h" #include "RenderIterator.h" #include "RenderSVGResourceFilter.h" #include "RenderView.h" #include "SVGRenderingContext.h" #include "SVGResources.h" #include "SVGResourcesCache.h" #include namespace WebCore { RenderSVGContainer::RenderSVGContainer(SVGElement& element, Ref&& style) : RenderSVGModelObject(element, WTFMove(style)) , m_objectBoundingBoxValid(false) , m_needsBoundariesUpdate(true) { } RenderSVGContainer::~RenderSVGContainer() { } void RenderSVGContainer::layout() { StackStats::LayoutCheckPoint layoutCheckPoint; ASSERT(needsLayout()); // RenderSVGRoot disables layoutState for the SVG rendering tree. ASSERT(!view().layoutStateEnabled()); LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(*this) || selfWillPaint()); // Allow RenderSVGViewportContainer to update its viewport. calcViewport(); // Allow RenderSVGTransformableContainer to update its transform. bool updatedTransform = calculateLocalTransform(); // RenderSVGViewportContainer needs to set the 'layout size changed' flag. determineIfLayoutSizeChanged(); SVGRenderSupport::layoutChildren(*this, selfNeedsLayout() || SVGRenderSupport::filtersForceContainerLayout(*this)); // Invalidate all resources of this client if our layout changed. if (everHadLayout() && needsLayout()) SVGResourcesCache::clientLayoutChanged(*this); // At this point LayoutRepainter already grabbed the old bounds, // recalculate them now so repaintAfterLayout() uses the new bounds. if (m_needsBoundariesUpdate || updatedTransform) { updateCachedBoundaries(); m_needsBoundariesUpdate = false; // If our bounds changed, notify the parents. RenderSVGModelObject::setNeedsBoundariesUpdate(); } repainter.repaintAfterLayout(); clearNeedsLayout(); } void RenderSVGContainer::addChild(RenderObject* child, RenderObject* beforeChild) { RenderSVGModelObject::addChild(child, beforeChild); SVGResourcesCache::clientWasAddedToTree(*child); } void RenderSVGContainer::removeChild(RenderObject& child) { SVGResourcesCache::clientWillBeRemovedFromTree(child); RenderSVGModelObject::removeChild(child); } bool RenderSVGContainer::selfWillPaint() { auto* resources = SVGResourcesCache::cachedResourcesForRenderer(*this); return resources && resources->filter(); } void RenderSVGContainer::paint(PaintInfo& paintInfo, const LayoutPoint&) { if (paintInfo.context().paintingDisabled()) return; // Spec: groups w/o children still may render filter content. if (!firstChild() && !selfWillPaint()) return; FloatRect repaintRect = repaintRectInLocalCoordinates(); if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(repaintRect, localToParentTransform(), paintInfo)) return; PaintInfo childPaintInfo(paintInfo); { GraphicsContextStateSaver stateSaver(childPaintInfo.context()); // Let the RenderSVGViewportContainer subclass clip if necessary applyViewportClip(childPaintInfo); childPaintInfo.applyTransform(localToParentTransform()); SVGRenderingContext renderingContext; bool continueRendering = true; if (childPaintInfo.phase == PaintPhaseForeground) { renderingContext.prepareToRenderSVGContent(*this, childPaintInfo); continueRendering = renderingContext.isRenderingPrepared(); } if (continueRendering) { childPaintInfo.updateSubtreePaintRootForChildren(this); for (auto& child : childrenOfType(*this)) child.paint(childPaintInfo, IntPoint()); } } // FIXME: This really should be drawn from local coordinates, but currently we hack it // to avoid our clip killing our outline rect. Thus we translate our // outline rect into parent coords before drawing. // FIXME: This means our focus ring won't share our rotation like it should. // We should instead disable our clip during PaintPhaseOutline if (paintInfo.phase == PaintPhaseSelfOutline && style().outlineWidth() && style().visibility() == VISIBLE) { IntRect paintRectInParent = enclosingIntRect(localToParentTransform().mapRect(repaintRect)); paintOutline(paintInfo, paintRectInParent); } } // addFocusRingRects is called from paintOutline and needs to be in the same coordinates as the paintOuline call void RenderSVGContainer::addFocusRingRects(Vector& rects, const LayoutPoint&, const RenderLayerModelObject*) { LayoutRect paintRectInParent = LayoutRect(localToParentTransform().mapRect(repaintRectInLocalCoordinates())); if (!paintRectInParent.isEmpty()) rects.append(paintRectInParent); } void RenderSVGContainer::updateCachedBoundaries() { SVGRenderSupport::computeContainerBoundingBoxes(*this, m_objectBoundingBox, m_objectBoundingBoxValid, m_strokeBoundingBox, m_repaintBoundingBox); SVGRenderSupport::intersectRepaintRectWithResources(*this, m_repaintBoundingBox); } bool RenderSVGContainer::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction) { // Give RenderSVGViewportContainer a chance to apply its viewport clip if (!pointIsInsideViewportClip(pointInParent)) return false; FloatPoint localPoint = localToParentTransform().inverse().valueOr(AffineTransform()).mapPoint(pointInParent); if (!SVGRenderSupport::pointInClippingArea(*this, localPoint)) return false; for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { if (child->nodeAtFloatPoint(request, result, localPoint, hitTestAction)) { updateHitTestResult(result, LayoutPoint(localPoint)); return true; } } // Accessibility wants to return SVG containers, if appropriate. if (request.type() & HitTestRequest::AccessibilityHitTest && m_objectBoundingBox.contains(localPoint)) { updateHitTestResult(result, LayoutPoint(localPoint)); return true; } // Spec: Only graphical elements can be targeted by the mouse, period. // 16.4: "If there are no graphics elements whose relevant graphics content is under the pointer (i.e., there is no target element), the event is not dispatched." return false; } }