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
27 #include "core/rendering/svg/SVGRenderSupport.h"
28
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->repaintRectInLocalCoordinates();
55 object->computeFloatRectForRepaint(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 computeFloatRectForRepaint() on our parent.
64 repaintRect = object->localToParentTransform().mapRect(repaintRect);
65 object->parent()->computeFloatRectForRepaint(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
checkForSVGRepaintDuringLayout(RenderObject * object)103 bool SVGRenderSupport::checkForSVGRepaintDuringLayout(RenderObject* object)
104 {
105 if (!object->checkForRepaintDuringLayout())
106 return false;
107 // When a parent container is transformed in SVG, all children will be painted automatically
108 // so we are able to skip redundant repaint checks.
109 RenderObject* parent = object->parent();
110 return !(parent && parent->isSVGContainer() && toRenderSVGContainer(parent)->didTransformToRootUpdate());
111 }
112
113 // Update a bounding box taking into account the validity of the other bounding box.
updateObjectBoundingBox(FloatRect & objectBoundingBox,bool & objectBoundingBoxValid,RenderObject * other,FloatRect otherBoundingBox)114 static inline void updateObjectBoundingBox(FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, RenderObject* other, FloatRect otherBoundingBox)
115 {
116 bool otherValid = other->isSVGContainer() ? toRenderSVGContainer(other)->isObjectBoundingBoxValid() : true;
117 if (!otherValid)
118 return;
119
120 if (!objectBoundingBoxValid) {
121 objectBoundingBox = otherBoundingBox;
122 objectBoundingBoxValid = true;
123 return;
124 }
125
126 objectBoundingBox.uniteEvenIfEmpty(otherBoundingBox);
127 }
128
computeContainerBoundingBoxes(const RenderObject * container,FloatRect & objectBoundingBox,bool & objectBoundingBoxValid,FloatRect & strokeBoundingBox,FloatRect & repaintBoundingBox)129 void SVGRenderSupport::computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox)
130 {
131 objectBoundingBox = FloatRect();
132 objectBoundingBoxValid = false;
133 strokeBoundingBox = FloatRect();
134
135 // When computing the strokeBoundingBox, we use the repaintRects of the container's children so that the container's stroke includes
136 // the resources applied to the children (such as clips and filters). This allows filters applied to containers to correctly bound
137 // the children, and also improves inlining of SVG content, as the stroke bound is used in that situation also.
138 for (RenderObject* current = container->firstChild(); current; current = current->nextSibling()) {
139 if (current->isSVGHiddenContainer())
140 continue;
141
142 const AffineTransform& transform = current->localToParentTransform();
143 if (transform.isIdentity()) {
144 updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, current->objectBoundingBox());
145 strokeBoundingBox.unite(current->repaintRectInLocalCoordinates());
146 } else {
147 updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, transform.mapRect(current->objectBoundingBox()));
148 strokeBoundingBox.unite(transform.mapRect(current->repaintRectInLocalCoordinates()));
149 }
150 }
151
152 repaintBoundingBox = strokeBoundingBox;
153 }
154
paintInfoIntersectsRepaintRect(const FloatRect & localRepaintRect,const AffineTransform & localTransform,const PaintInfo & paintInfo)155 bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo)
156 {
157 if (localTransform.isIdentity())
158 return localRepaintRect.intersects(paintInfo.rect);
159
160 return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect);
161 }
162
findTreeRootObject(const RenderObject * start)163 const RenderSVGRoot* SVGRenderSupport::findTreeRootObject(const RenderObject* start)
164 {
165 while (start && !start->isSVGRoot())
166 start = start->parent();
167
168 ASSERT(start);
169 ASSERT(start->isSVGRoot());
170 return toRenderSVGRoot(start);
171 }
172
invalidateResourcesOfChildren(RenderObject * start)173 static inline void invalidateResourcesOfChildren(RenderObject* start)
174 {
175 ASSERT(!start->needsLayout());
176 if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(start))
177 resources->removeClientFromCache(start, false);
178
179 for (RenderObject* child = start->firstChild(); child; child = child->nextSibling())
180 invalidateResourcesOfChildren(child);
181 }
182
layoutSizeOfNearestViewportChanged(const RenderObject * start)183 static inline bool layoutSizeOfNearestViewportChanged(const RenderObject* start)
184 {
185 while (start && !start->isSVGRoot() && !start->isSVGViewportContainer())
186 start = start->parent();
187
188 ASSERT(start);
189 ASSERT(start->isSVGRoot() || start->isSVGViewportContainer());
190 if (start->isSVGViewportContainer())
191 return toRenderSVGViewportContainer(start)->isLayoutSizeChanged();
192
193 return toRenderSVGRoot(start)->isLayoutSizeChanged();
194 }
195
transformToRootChanged(RenderObject * ancestor)196 bool SVGRenderSupport::transformToRootChanged(RenderObject* ancestor)
197 {
198 while (ancestor && !ancestor->isSVGRoot()) {
199 if (ancestor->isSVGTransformableContainer())
200 return toRenderSVGContainer(ancestor)->didTransformToRootUpdate();
201 if (ancestor->isSVGViewportContainer())
202 return toRenderSVGViewportContainer(ancestor)->didTransformToRootUpdate();
203 ancestor = ancestor->parent();
204 }
205
206 return false;
207 }
208
layoutChildren(RenderObject * start,bool selfNeedsLayout)209 void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout)
210 {
211 bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start);
212 bool transformChanged = transformToRootChanged(start);
213 HashSet<RenderObject*> notlayoutedObjects;
214
215 for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
216 bool needsLayout = selfNeedsLayout;
217 bool childEverHadLayout = child->everHadLayout();
218
219 if (transformChanged) {
220 // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true).
221 if (child->isSVGText())
222 toRenderSVGText(child)->setNeedsTextMetricsUpdate();
223 needsLayout = true;
224 }
225
226 if (layoutSizeChanged) {
227 // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths
228 if (SVGElement* element = child->node()->isSVGElement() ? toSVGElement(child->node()) : 0) {
229 if (element->hasRelativeLengths()) {
230 // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object
231 if (child->isSVGShape())
232 toRenderSVGShape(child)->setNeedsShapeUpdate();
233 else if (child->isSVGText()) {
234 toRenderSVGText(child)->setNeedsTextMetricsUpdate();
235 toRenderSVGText(child)->setNeedsPositioningValuesUpdate();
236 }
237
238 needsLayout = true;
239 }
240 }
241 }
242
243 SubtreeLayoutScope layoutScope(child);
244 // Resource containers are nasty: they can invalidate clients outside the current SubtreeLayoutScope.
245 // Since they only care about viewport size changes (to resolve their relative lengths), we trigger
246 // their invalidation directly from SVGSVGElement::svgAttributeChange() or at a higher
247 // SubtreeLayoutScope (in RenderView::layout()).
248 if (needsLayout && !child->isSVGResourceContainer())
249 layoutScope.setNeedsLayout(child);
250
251 layoutResourcesIfNeeded(child);
252
253 if (child->needsLayout()) {
254 child->layout();
255 // Renderers are responsible for repainting themselves when changing, except
256 // for the initial paint to avoid potential double-painting caused by non-sensical "old" bounds.
257 // We could handle this in the individual objects, but for now it's easier to have
258 // parent containers call repaint(). (RenderBlock::layout* has similar logic.)
259 if (!childEverHadLayout && !RuntimeEnabledFeatures::repaintAfterLayoutEnabled())
260 child->repaint();
261 } else if (layoutSizeChanged)
262 notlayoutedObjects.add(child);
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 // SVG doesn't support independent x/y overflow
288 ASSERT(object->style()->overflowX() == object->style()->overflowY());
289
290 // OSCROLL is never set for SVG - see StyleResolver::adjustRenderStyle
291 ASSERT(object->style()->overflowX() != OSCROLL);
292
293 // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size.
294 ASSERT(!object->isRoot());
295
296 return object->style()->overflowX() == OHIDDEN;
297 }
298
intersectRepaintRectWithResources(const RenderObject * object,FloatRect & repaintRect)299 void SVGRenderSupport::intersectRepaintRectWithResources(const RenderObject* object, FloatRect& repaintRect)
300 {
301 ASSERT(object);
302
303 RenderObject* renderer = const_cast<RenderObject*>(object);
304 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
305 if (!resources)
306 return;
307
308 if (RenderSVGResourceFilter* filter = resources->filter())
309 repaintRect = filter->resourceBoundingBox(renderer);
310
311 if (RenderSVGResourceClipper* clipper = resources->clipper())
312 repaintRect.intersect(clipper->resourceBoundingBox(renderer));
313
314 if (RenderSVGResourceMasker* masker = resources->masker())
315 repaintRect.intersect(masker->resourceBoundingBox(renderer));
316 }
317
filtersForceContainerLayout(RenderObject * object)318 bool SVGRenderSupport::filtersForceContainerLayout(RenderObject* object)
319 {
320 // If any of this container's children need to be laid out, and a filter is applied
321 // to the container, we need to repaint the entire container.
322 if (!object->normalChildNeedsLayout())
323 return false;
324
325 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
326 if (!resources || !resources->filter())
327 return false;
328
329 return true;
330 }
331
pointInClippingArea(RenderObject * object,const FloatPoint & point)332 bool SVGRenderSupport::pointInClippingArea(RenderObject* object, const FloatPoint& point)
333 {
334 ASSERT(object);
335
336 // We just take clippers into account to determine if a point is on the node. The Specification may
337 // change later and we also need to check maskers.
338 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
339 if (!resources)
340 return true;
341
342 if (RenderSVGResourceClipper* clipper = resources->clipper())
343 return clipper->hitTestClipContent(object->objectBoundingBox(), point);
344
345 return true;
346 }
347
applyStrokeStyleToContext(GraphicsContext * context,const RenderStyle * style,const RenderObject * object)348 void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object)
349 {
350 ASSERT(context);
351 ASSERT(style);
352 ASSERT(object);
353 ASSERT(object->node());
354 ASSERT(object->node()->isSVGElement());
355
356 const SVGRenderStyle* svgStyle = style->svgStyle();
357 ASSERT(svgStyle);
358
359 SVGLengthContext lengthContext(toSVGElement(object->node()));
360 context->setStrokeThickness(svgStyle->strokeWidth().value(lengthContext));
361 context->setLineCap(svgStyle->capStyle());
362 context->setLineJoin(svgStyle->joinStyle());
363 context->setMiterLimit(svgStyle->strokeMiterLimit());
364
365 const Vector<SVGLength>& dashes = svgStyle->strokeDashArray();
366 if (dashes.isEmpty())
367 return;
368
369 DashArray dashArray;
370 const Vector<SVGLength>::const_iterator end = dashes.end();
371 for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it)
372 dashArray.append((*it).value(lengthContext));
373
374 context->setLineDash(dashArray, svgStyle->strokeDashOffset().value(lengthContext));
375 }
376
applyStrokeStyleToStrokeData(StrokeData * strokeData,const RenderStyle * style,const RenderObject * object)377 void SVGRenderSupport::applyStrokeStyleToStrokeData(StrokeData* strokeData, const RenderStyle* style, const RenderObject* object)
378 {
379 ASSERT(strokeData);
380 ASSERT(style);
381 ASSERT(object);
382 ASSERT(object->node());
383 ASSERT(object->node()->isSVGElement());
384
385 const SVGRenderStyle* svgStyle = style->svgStyle();
386 ASSERT(svgStyle);
387
388 SVGLengthContext lengthContext(toSVGElement(object->node()));
389 strokeData->setThickness(svgStyle->strokeWidth().value(lengthContext));
390 strokeData->setLineCap(svgStyle->capStyle());
391 strokeData->setLineJoin(svgStyle->joinStyle());
392 strokeData->setMiterLimit(svgStyle->strokeMiterLimit());
393
394 const Vector<SVGLength>& dashes = svgStyle->strokeDashArray();
395 if (dashes.isEmpty())
396 return;
397
398 DashArray dashArray;
399 const Vector<SVGLength>::const_iterator end = dashes.end();
400 for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it)
401 dashArray.append((*it).value(lengthContext));
402
403 strokeData->setLineDash(dashArray, svgStyle->strokeDashOffset().value(lengthContext));
404 }
405
isEmptySVGInlineText(const RenderObject * object)406 bool SVGRenderSupport::isEmptySVGInlineText(const RenderObject* object)
407 {
408 // RenderSVGInlineText performs whitespace filtering in order to support xml:space
409 // (http://www.w3.org/TR/SVG/struct.html#LangSpaceAttrs), and can end up with an empty string
410 // even when its original constructor argument is non-empty.
411 return object->isSVGInlineText() && toRenderSVGInlineText(object)->hasEmptyText();
412 }
413
414 }
415