• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007, 2008 Rob Buis <buis@kde.org>
3  * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
4  * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
5  * Copyright (C) 2009 Google, Inc.  All rights reserved.
6  * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
7  * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB.  If not, write to
21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  */
24 
25 #include "config.h"
26 #include "core/rendering/svg/SVGRenderSupport.h"
27 
28 #include "core/rendering/PaintInfo.h"
29 #include "core/rendering/RenderGeometryMap.h"
30 #include "core/rendering/RenderLayer.h"
31 #include "core/rendering/SubtreeLayoutScope.h"
32 #include "core/rendering/svg/RenderSVGInlineText.h"
33 #include "core/rendering/svg/RenderSVGResourceClipper.h"
34 #include "core/rendering/svg/RenderSVGResourceFilter.h"
35 #include "core/rendering/svg/RenderSVGResourceMasker.h"
36 #include "core/rendering/svg/RenderSVGRoot.h"
37 #include "core/rendering/svg/RenderSVGText.h"
38 #include "core/rendering/svg/RenderSVGViewportContainer.h"
39 #include "core/rendering/svg/SVGResources.h"
40 #include "core/rendering/svg/SVGResourcesCache.h"
41 #include "core/svg/SVGElement.h"
42 #include "platform/geometry/TransformState.h"
43 
44 namespace WebCore {
45 
clippedOverflowRectForRepaint(const RenderObject * object,const RenderLayerModelObject * repaintContainer)46 LayoutRect SVGRenderSupport::clippedOverflowRectForRepaint(const RenderObject* object, const RenderLayerModelObject* repaintContainer)
47 {
48     // Return early for any cases where we don't actually paint
49     if (object->style()->visibility() != VISIBLE && !object->enclosingLayer()->hasVisibleContent())
50         return LayoutRect();
51 
52     // Pass our local paint rect to computeRectForRepaint() which will
53     // map to parent coords and recurse up the parent chain.
54     FloatRect repaintRect = object->paintInvalidationRectInLocalCoordinates();
55     object->computeFloatRectForPaintInvalidation(repaintContainer, repaintRect);
56     return enclosingLayoutRect(repaintRect);
57 }
58 
computeFloatRectForRepaint(const RenderObject * object,const RenderLayerModelObject * repaintContainer,FloatRect & repaintRect,bool fixed)59 void SVGRenderSupport::computeFloatRectForRepaint(const RenderObject* object, const RenderLayerModelObject* repaintContainer, FloatRect& repaintRect, bool fixed)
60 {
61     repaintRect.inflate(object->style()->outlineWidth());
62 
63     // Translate to coords in our parent renderer, and then call computeFloatRectForPaintInvalidation() on our parent.
64     repaintRect = object->localToParentTransform().mapRect(repaintRect);
65     object->parent()->computeFloatRectForPaintInvalidation(repaintContainer, repaintRect, fixed);
66 }
67 
mapLocalToContainer(const RenderObject * object,const RenderLayerModelObject * repaintContainer,TransformState & transformState,bool * wasFixed)68 void SVGRenderSupport::mapLocalToContainer(const RenderObject* object, const RenderLayerModelObject* repaintContainer, TransformState& transformState, bool* wasFixed)
69 {
70     transformState.applyTransform(object->localToParentTransform());
71 
72     RenderObject* parent = object->parent();
73 
74     // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform
75     // to map an element from SVG viewport coordinates to CSS box coordinates.
76     // RenderSVGRoot's mapLocalToContainer method expects CSS box coordinates.
77     if (parent->isSVGRoot())
78         transformState.applyTransform(toRenderSVGRoot(parent)->localToBorderBoxTransform());
79 
80     MapCoordinatesFlags mode = UseTransforms;
81     parent->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed);
82 }
83 
pushMappingToContainer(const RenderObject * object,const RenderLayerModelObject * ancestorToStopAt,RenderGeometryMap & geometryMap)84 const RenderObject* SVGRenderSupport::pushMappingToContainer(const RenderObject* object, const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap)
85 {
86     ASSERT_UNUSED(ancestorToStopAt, ancestorToStopAt != object);
87 
88     RenderObject* parent = object->parent();
89 
90     // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform
91     // to map an element from SVG viewport coordinates to CSS box coordinates.
92     // RenderSVGRoot's mapLocalToContainer method expects CSS box coordinates.
93     if (parent->isSVGRoot()) {
94         TransformationMatrix matrix(object->localToParentTransform());
95         matrix.multiply(toRenderSVGRoot(parent)->localToBorderBoxTransform());
96         geometryMap.push(object, matrix);
97     } else
98         geometryMap.push(object, object->localToParentTransform());
99 
100     return parent;
101 }
102 
parentTransformDidChange(RenderObject * object)103 bool SVGRenderSupport::parentTransformDidChange(RenderObject* object)
104 {
105     // When a parent container is transformed in SVG, all children will be painted automatically
106     // so we are able to skip redundant repaint checks.
107     RenderObject* parent = object->parent();
108     return !(parent && parent->isSVGContainer() && toRenderSVGContainer(parent)->didTransformToRootUpdate());
109 }
110 
checkForSVGRepaintDuringLayout(RenderObject * object)111 bool SVGRenderSupport::checkForSVGRepaintDuringLayout(RenderObject* object)
112 {
113     if (!object->checkForPaintInvalidationDuringLayout())
114         return false;
115 
116     return parentTransformDidChange(object);
117 }
118 
119 // Update a bounding box taking into account the validity of the other bounding box.
updateObjectBoundingBox(FloatRect & objectBoundingBox,bool & objectBoundingBoxValid,RenderObject * other,FloatRect otherBoundingBox)120 inline void SVGRenderSupport::updateObjectBoundingBox(FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, RenderObject* other, FloatRect otherBoundingBox)
121 {
122     bool otherValid = other->isSVGContainer() ? toRenderSVGContainer(other)->isObjectBoundingBoxValid() : true;
123     if (!otherValid)
124         return;
125 
126     if (!objectBoundingBoxValid) {
127         objectBoundingBox = otherBoundingBox;
128         objectBoundingBoxValid = true;
129         return;
130     }
131 
132     objectBoundingBox.uniteEvenIfEmpty(otherBoundingBox);
133 }
134 
computeContainerBoundingBoxes(const RenderObject * container,FloatRect & objectBoundingBox,bool & objectBoundingBoxValid,FloatRect & strokeBoundingBox,FloatRect & repaintBoundingBox)135 void SVGRenderSupport::computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox)
136 {
137     objectBoundingBox = FloatRect();
138     objectBoundingBoxValid = false;
139     strokeBoundingBox = FloatRect();
140 
141     // When computing the strokeBoundingBox, we use the repaintRects of the container's children so that the container's stroke includes
142     // the resources applied to the children (such as clips and filters). This allows filters applied to containers to correctly bound
143     // the children, and also improves inlining of SVG content, as the stroke bound is used in that situation also.
144     for (RenderObject* current = container->slowFirstChild(); current; current = current->nextSibling()) {
145         if (current->isSVGHiddenContainer())
146             continue;
147 
148         const AffineTransform& transform = current->localToParentTransform();
149         updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current,
150             transform.mapRect(current->objectBoundingBox()));
151         strokeBoundingBox.unite(transform.mapRect(current->paintInvalidationRectInLocalCoordinates()));
152     }
153 
154     repaintBoundingBox = strokeBoundingBox;
155 }
156 
paintInfoIntersectsRepaintRect(const FloatRect & localRepaintRect,const AffineTransform & localTransform,const PaintInfo & paintInfo)157 bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo)
158 {
159     return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect);
160 }
161 
findTreeRootObject(const RenderObject * start)162 const RenderSVGRoot* SVGRenderSupport::findTreeRootObject(const RenderObject* start)
163 {
164     while (start && !start->isSVGRoot())
165         start = start->parent();
166 
167     ASSERT(start);
168     ASSERT(start->isSVGRoot());
169     return toRenderSVGRoot(start);
170 }
171 
invalidateResourcesOfChildren(RenderObject * start)172 inline void SVGRenderSupport::invalidateResourcesOfChildren(RenderObject* start)
173 {
174     ASSERT(!start->needsLayout());
175     if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(start))
176         resources->removeClientFromCache(start, false);
177 
178     for (RenderObject* child = start->slowFirstChild(); child; child = child->nextSibling())
179         invalidateResourcesOfChildren(child);
180 }
181 
layoutSizeOfNearestViewportChanged(const RenderObject * start)182 inline bool SVGRenderSupport::layoutSizeOfNearestViewportChanged(const RenderObject* start)
183 {
184     while (start && !start->isSVGRoot() && !start->isSVGViewportContainer())
185         start = start->parent();
186 
187     ASSERT(start);
188     ASSERT(start->isSVGRoot() || start->isSVGViewportContainer());
189     if (start->isSVGViewportContainer())
190         return toRenderSVGViewportContainer(start)->isLayoutSizeChanged();
191 
192     return toRenderSVGRoot(start)->isLayoutSizeChanged();
193 }
194 
transformToRootChanged(RenderObject * ancestor)195 bool SVGRenderSupport::transformToRootChanged(RenderObject* ancestor)
196 {
197     while (ancestor && !ancestor->isSVGRoot()) {
198         if (ancestor->isSVGTransformableContainer())
199             return toRenderSVGContainer(ancestor)->didTransformToRootUpdate();
200         if (ancestor->isSVGViewportContainer())
201             return toRenderSVGViewportContainer(ancestor)->didTransformToRootUpdate();
202         ancestor = ancestor->parent();
203     }
204 
205     return false;
206 }
207 
layoutChildren(RenderObject * start,bool selfNeedsLayout)208 void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout)
209 {
210     bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start);
211     bool transformChanged = transformToRootChanged(start);
212     HashSet<RenderObject*> notlayoutedObjects;
213 
214     for (RenderObject* child = start->slowFirstChild(); child; child = child->nextSibling()) {
215         bool needsLayout = selfNeedsLayout;
216         bool childEverHadLayout = child->everHadLayout();
217 
218         if (transformChanged) {
219             // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true).
220             if (child->isSVGText())
221                 toRenderSVGText(child)->setNeedsTextMetricsUpdate();
222             needsLayout = true;
223         }
224 
225         if (layoutSizeChanged) {
226             // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths
227             if (SVGElement* element = child->node()->isSVGElement() ? toSVGElement(child->node()) : 0) {
228                 if (element->hasRelativeLengths()) {
229                     // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object
230                     if (child->isSVGShape()) {
231                         toRenderSVGShape(child)->setNeedsShapeUpdate();
232                     } else if (child->isSVGText()) {
233                         toRenderSVGText(child)->setNeedsTextMetricsUpdate();
234                         toRenderSVGText(child)->setNeedsPositioningValuesUpdate();
235                     }
236 
237                     needsLayout = true;
238                 }
239             }
240         }
241 
242         SubtreeLayoutScope layoutScope(*child);
243         // Resource containers are nasty: they can invalidate clients outside the current SubtreeLayoutScope.
244         // Since they only care about viewport size changes (to resolve their relative lengths), we trigger
245         // their invalidation directly from SVGSVGElement::svgAttributeChange() or at a higher
246         // SubtreeLayoutScope (in RenderView::layout()).
247         if (needsLayout && !child->isSVGResourceContainer())
248             layoutScope.setNeedsLayout(child);
249 
250         layoutResourcesIfNeeded(child);
251 
252         if (child->needsLayout()) {
253             child->layout();
254             // Renderers are responsible for repainting themselves when changing, except
255             // for the initial paint to avoid potential double-painting caused by non-sensical "old" bounds.
256             // We could handle this in the individual objects, but for now it's easier to have
257             // parent containers call repaint().  (RenderBlock::layout* has similar logic.)
258             if (!childEverHadLayout && !RuntimeEnabledFeatures::repaintAfterLayoutEnabled())
259                 child->paintInvalidationForWholeRenderer();
260         } else if (layoutSizeChanged) {
261             notlayoutedObjects.add(child);
262         }
263     }
264 
265     if (!layoutSizeChanged) {
266         ASSERT(notlayoutedObjects.isEmpty());
267         return;
268     }
269 
270     // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path.
271     HashSet<RenderObject*>::iterator end = notlayoutedObjects.end();
272     for (HashSet<RenderObject*>::iterator it = notlayoutedObjects.begin(); it != end; ++it)
273         invalidateResourcesOfChildren(*it);
274 }
275 
layoutResourcesIfNeeded(const RenderObject * object)276 void SVGRenderSupport::layoutResourcesIfNeeded(const RenderObject* object)
277 {
278     ASSERT(object);
279 
280     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
281     if (resources)
282         resources->layoutIfNeeded();
283 }
284 
isOverflowHidden(const RenderObject * object)285 bool SVGRenderSupport::isOverflowHidden(const RenderObject* object)
286 {
287     // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size.
288     ASSERT(!object->isDocumentElement());
289 
290     return object->style()->overflowX() == OHIDDEN || object->style()->overflowX() == OSCROLL;
291 }
292 
intersectRepaintRectWithResources(const RenderObject * renderer,FloatRect & repaintRect)293 void SVGRenderSupport::intersectRepaintRectWithResources(const RenderObject* renderer, FloatRect& repaintRect)
294 {
295     ASSERT(renderer);
296 
297     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
298     if (!resources)
299         return;
300 
301     if (RenderSVGResourceFilter* filter = resources->filter())
302         repaintRect = filter->resourceBoundingBox(renderer);
303 
304     if (RenderSVGResourceClipper* clipper = resources->clipper())
305         repaintRect.intersect(clipper->resourceBoundingBox(renderer));
306 
307     if (RenderSVGResourceMasker* masker = resources->masker())
308         repaintRect.intersect(masker->resourceBoundingBox(renderer));
309 }
310 
filtersForceContainerLayout(RenderObject * object)311 bool SVGRenderSupport::filtersForceContainerLayout(RenderObject* object)
312 {
313     // If any of this container's children need to be laid out, and a filter is applied
314     // to the container, we need to repaint the entire container.
315     if (!object->normalChildNeedsLayout())
316         return false;
317 
318     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
319     if (!resources || !resources->filter())
320         return false;
321 
322     return true;
323 }
324 
pointInClippingArea(RenderObject * object,const FloatPoint & point)325 bool SVGRenderSupport::pointInClippingArea(RenderObject* object, const FloatPoint& point)
326 {
327     ASSERT(object);
328 
329     // We just take clippers into account to determine if a point is on the node. The Specification may
330     // change later and we also need to check maskers.
331     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
332     if (!resources)
333         return true;
334 
335     if (RenderSVGResourceClipper* clipper = resources->clipper())
336         return clipper->hitTestClipContent(object->objectBoundingBox(), point);
337 
338     return true;
339 }
340 
applyStrokeStyleToContext(GraphicsContext * context,const RenderStyle * style,const RenderObject * object)341 void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object)
342 {
343     ASSERT(context);
344     ASSERT(style);
345     ASSERT(object);
346     ASSERT(object->node());
347     ASSERT(object->node()->isSVGElement());
348 
349     const SVGRenderStyle* svgStyle = style->svgStyle();
350     ASSERT(svgStyle);
351 
352     SVGLengthContext lengthContext(toSVGElement(object->node()));
353     context->setStrokeThickness(svgStyle->strokeWidth()->value(lengthContext));
354     context->setLineCap(svgStyle->capStyle());
355     context->setLineJoin(svgStyle->joinStyle());
356     context->setMiterLimit(svgStyle->strokeMiterLimit());
357 
358     RefPtr<SVGLengthList> dashes = svgStyle->strokeDashArray();
359     if (dashes->isEmpty())
360         return;
361 
362     DashArray dashArray;
363     SVGLengthList::ConstIterator it = dashes->begin();
364     SVGLengthList::ConstIterator itEnd = dashes->end();
365     for (; it != itEnd; ++it)
366         dashArray.append(it->value(lengthContext));
367 
368     context->setLineDash(dashArray, svgStyle->strokeDashOffset()->value(lengthContext));
369 }
370 
applyStrokeStyleToStrokeData(StrokeData * strokeData,const RenderStyle * style,const RenderObject * object)371 void SVGRenderSupport::applyStrokeStyleToStrokeData(StrokeData* strokeData, const RenderStyle* style, const RenderObject* object)
372 {
373     ASSERT(strokeData);
374     ASSERT(style);
375     ASSERT(object);
376     ASSERT(object->node());
377     ASSERT(object->node()->isSVGElement());
378 
379     const SVGRenderStyle* svgStyle = style->svgStyle();
380     ASSERT(svgStyle);
381 
382     SVGLengthContext lengthContext(toSVGElement(object->node()));
383     strokeData->setThickness(svgStyle->strokeWidth()->value(lengthContext));
384     strokeData->setLineCap(svgStyle->capStyle());
385     strokeData->setLineJoin(svgStyle->joinStyle());
386     strokeData->setMiterLimit(svgStyle->strokeMiterLimit());
387 
388     RefPtr<SVGLengthList> dashes = svgStyle->strokeDashArray();
389     if (dashes->isEmpty())
390         return;
391 
392     DashArray dashArray;
393     size_t length = dashes->length();
394     for (size_t i = 0; i < length; ++i)
395         dashArray.append(dashes->at(i)->value(lengthContext));
396 
397     strokeData->setLineDash(dashArray, svgStyle->strokeDashOffset()->value(lengthContext));
398 }
399 
isRenderableTextNode(const RenderObject * object)400 bool SVGRenderSupport::isRenderableTextNode(const RenderObject* object)
401 {
402     ASSERT(object->isText());
403     // <br> is marked as text, but is not handled by the SVG rendering code-path.
404     return object->isSVGInlineText() && !toRenderSVGInlineText(object)->hasEmptyText();
405 }
406 
407 }
408