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#if ENABLE(SVG)
28#include "SVGRenderSupport.h"
29
30#include "NodeRenderStyle.h"
31#include "RenderGeometryMap.h"
32#include "RenderLayer.h"
33#include "RenderSVGResource.h"
34#include "RenderSVGResourceClipper.h"
35#include "RenderSVGResourceFilter.h"
36#include "RenderSVGResourceMarker.h"
37#include "RenderSVGResourceMasker.h"
38#include "RenderSVGRoot.h"
39#include "RenderSVGText.h"
40#include "RenderSVGViewportContainer.h"
41#include "SVGResources.h"
42#include "SVGResourcesCache.h"
43#include "SVGStyledElement.h"
44#include "TransformState.h"
45
46namespace WebCore {
47
48FloatRect SVGRenderSupport::repaintRectForRendererInLocalCoordinatesExcludingSVGShadow(const RenderObject* object)
49{
50    // FIXME: Add support for RenderSVGBlock.
51
52    // FIXME: This should use a safer cast such as toRenderSVGModelObject().
53    if (object->isSVGShape() || object->isSVGImage() || object->isSVGContainer())
54        return static_cast<const RenderSVGModelObject*>(object)->repaintRectInLocalCoordinatesExcludingSVGShadow();
55
56    return object->repaintRectInLocalCoordinates();
57}
58
59LayoutRect SVGRenderSupport::clippedOverflowRectForRepaint(const RenderObject* object, const RenderLayerModelObject* repaintContainer)
60{
61    // Return early for any cases where we don't actually paint
62    if (object->style()->visibility() != VISIBLE && !object->enclosingLayer()->hasVisibleContent())
63        return LayoutRect();
64
65    // Pass our local paint rect to computeRectForRepaint() which will
66    // map to parent coords and recurse up the parent chain.
67    FloatRect repaintRect = repaintRectForRendererInLocalCoordinatesExcludingSVGShadow(object);
68    const SVGRenderStyle* svgStyle = object->style()->svgStyle();
69    if (const ShadowData* shadow = svgStyle->shadow())
70        shadow->adjustRectForShadow(repaintRect);
71    object->computeFloatRectForRepaint(repaintContainer, repaintRect);
72    return enclosingLayoutRect(repaintRect);
73}
74
75void SVGRenderSupport::computeFloatRectForRepaint(const RenderObject* object, const RenderLayerModelObject* repaintContainer, FloatRect& repaintRect, bool fixed)
76{
77    const SVGRenderStyle* svgStyle = object->style()->svgStyle();
78    if (const ShadowData* shadow = svgStyle->shadow())
79        shadow->adjustRectForShadow(repaintRect);
80    repaintRect.inflate(object->style()->outlineWidth());
81
82    // Translate to coords in our parent renderer, and then call computeFloatRectForRepaint() on our parent.
83    repaintRect = object->localToParentTransform().mapRect(repaintRect);
84    object->parent()->computeFloatRectForRepaint(repaintContainer, repaintRect, fixed);
85}
86
87void SVGRenderSupport::mapLocalToContainer(const RenderObject* object, const RenderLayerModelObject* repaintContainer, TransformState& transformState, bool* wasFixed)
88{
89    transformState.applyTransform(object->localToParentTransform());
90
91    RenderObject* parent = object->parent();
92
93    // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform
94    // to map an element from SVG viewport coordinates to CSS box coordinates.
95    // RenderSVGRoot's mapLocalToContainer method expects CSS box coordinates.
96    if (parent->isSVGRoot())
97        transformState.applyTransform(toRenderSVGRoot(parent)->localToBorderBoxTransform());
98
99    MapCoordinatesFlags mode = UseTransforms;
100    parent->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed);
101}
102
103const RenderObject* SVGRenderSupport::pushMappingToContainer(const RenderObject* object, const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap)
104{
105    ASSERT_UNUSED(ancestorToStopAt, ancestorToStopAt != object);
106
107    RenderObject* parent = object->parent();
108
109    // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform
110    // to map an element from SVG viewport coordinates to CSS box coordinates.
111    // RenderSVGRoot's mapLocalToContainer method expects CSS box coordinates.
112    if (parent->isSVGRoot()) {
113        TransformationMatrix matrix(object->localToParentTransform());
114        matrix.multiply(toRenderSVGRoot(parent)->localToBorderBoxTransform());
115        geometryMap.push(object, matrix);
116    } else
117        geometryMap.push(object, object->localToParentTransform());
118
119    return parent;
120}
121
122bool SVGRenderSupport::checkForSVGRepaintDuringLayout(RenderObject* object)
123{
124    if (!object->checkForRepaintDuringLayout())
125        return false;
126    // When a parent container is transformed in SVG, all children will be painted automatically
127    // so we are able to skip redundant repaint checks.
128    RenderObject* parent = object->parent();
129    return !(parent && parent->isSVGContainer() && toRenderSVGContainer(parent)->didTransformToRootUpdate());
130}
131
132// Update a bounding box taking into account the validity of the other bounding box.
133static inline void updateObjectBoundingBox(FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, RenderObject* other, FloatRect otherBoundingBox)
134{
135    bool otherValid = other->isSVGContainer() ? toRenderSVGContainer(other)->isObjectBoundingBoxValid() : true;
136    if (!otherValid)
137        return;
138
139    if (!objectBoundingBoxValid) {
140        objectBoundingBox = otherBoundingBox;
141        objectBoundingBoxValid = true;
142        return;
143    }
144
145    objectBoundingBox.uniteEvenIfEmpty(otherBoundingBox);
146}
147
148void SVGRenderSupport::computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox)
149{
150    objectBoundingBox = FloatRect();
151    objectBoundingBoxValid = false;
152    strokeBoundingBox = FloatRect();
153
154    // When computing the strokeBoundingBox, we use the repaintRects of the container's children so that the container's stroke includes
155    // the resources applied to the children (such as clips and filters). This allows filters applied to containers to correctly bound
156    // the children, and also improves inlining of SVG content, as the stroke bound is used in that situation also.
157    for (RenderObject* current = container->firstChild(); current; current = current->nextSibling()) {
158        if (current->isSVGHiddenContainer())
159            continue;
160
161        const AffineTransform& transform = current->localToParentTransform();
162        if (transform.isIdentity()) {
163            updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, current->objectBoundingBox());
164            strokeBoundingBox.unite(current->repaintRectInLocalCoordinates());
165        } else {
166            updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, transform.mapRect(current->objectBoundingBox()));
167            strokeBoundingBox.unite(transform.mapRect(current->repaintRectInLocalCoordinates()));
168        }
169    }
170
171    repaintBoundingBox = strokeBoundingBox;
172}
173
174bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo)
175{
176    if (localTransform.isIdentity())
177        return localRepaintRect.intersects(paintInfo.rect);
178
179    return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect);
180}
181
182const RenderSVGRoot* SVGRenderSupport::findTreeRootObject(const RenderObject* start)
183{
184    while (start && !start->isSVGRoot())
185        start = start->parent();
186
187    ASSERT(start);
188    ASSERT(start->isSVGRoot());
189    return toRenderSVGRoot(start);
190}
191
192static inline void invalidateResourcesOfChildren(RenderObject* start)
193{
194    ASSERT(!start->needsLayout());
195    if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(start))
196        resources->removeClientFromCache(start, false);
197
198    for (RenderObject* child = start->firstChild(); child; child = child->nextSibling())
199        invalidateResourcesOfChildren(child);
200}
201
202static inline bool layoutSizeOfNearestViewportChanged(const RenderObject* start)
203{
204    while (start && !start->isSVGRoot() && !start->isSVGViewportContainer())
205        start = start->parent();
206
207    ASSERT(start);
208    ASSERT(start->isSVGRoot() || start->isSVGViewportContainer());
209    if (start->isSVGViewportContainer())
210        return toRenderSVGViewportContainer(start)->isLayoutSizeChanged();
211
212    return toRenderSVGRoot(start)->isLayoutSizeChanged();
213}
214
215bool SVGRenderSupport::transformToRootChanged(RenderObject* ancestor)
216{
217    while (ancestor && !ancestor->isSVGRoot()) {
218        if (ancestor->isSVGTransformableContainer())
219            return toRenderSVGContainer(ancestor)->didTransformToRootUpdate();
220        if (ancestor->isSVGViewportContainer())
221            return toRenderSVGViewportContainer(ancestor)->didTransformToRootUpdate();
222        ancestor = ancestor->parent();
223    }
224
225    return false;
226}
227
228void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout)
229{
230    bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start);
231    bool transformChanged = transformToRootChanged(start);
232    bool hasSVGShadow = rendererHasSVGShadow(start);
233    bool needsBoundariesUpdate = start->needsBoundariesUpdate();
234    HashSet<RenderObject*> notlayoutedObjects;
235
236    for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
237        bool needsLayout = selfNeedsLayout;
238        bool childEverHadLayout = child->everHadLayout();
239
240        if (needsBoundariesUpdate && hasSVGShadow) {
241            // If we have a shadow, our shadow is baked into our children's cached boundaries,
242            // so they need to update.
243            child->setNeedsBoundariesUpdate();
244            needsLayout = true;
245        }
246
247        if (transformChanged) {
248            // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true).
249            if (child->isSVGText())
250                toRenderSVGText(child)->setNeedsTextMetricsUpdate();
251            needsLayout = true;
252        }
253
254        if (layoutSizeChanged) {
255            // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths
256            if (SVGElement* element = child->node()->isSVGElement() ? toSVGElement(child->node()) : 0) {
257                if (element->isSVGStyledElement() && toSVGStyledElement(element)->hasRelativeLengths()) {
258                    // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object
259                    if (child->isSVGShape())
260                        toRenderSVGShape(child)->setNeedsShapeUpdate();
261                    else if (child->isSVGText()) {
262                        toRenderSVGText(child)->setNeedsTextMetricsUpdate();
263                        toRenderSVGText(child)->setNeedsPositioningValuesUpdate();
264                    }
265
266                    needsLayout = true;
267                }
268            }
269        }
270
271        if (needsLayout)
272            child->setNeedsLayout(true, MarkOnlyThis);
273
274        if (child->needsLayout()) {
275            child->layout();
276            // Renderers are responsible for repainting themselves when changing, except
277            // for the initial paint to avoid potential double-painting caused by non-sensical "old" bounds.
278            // We could handle this in the individual objects, but for now it's easier to have
279            // parent containers call repaint().  (RenderBlock::layout* has similar logic.)
280            if (!childEverHadLayout)
281                child->repaint();
282        } else if (layoutSizeChanged)
283            notlayoutedObjects.add(child);
284
285        ASSERT(!child->needsLayout());
286    }
287
288    if (!layoutSizeChanged) {
289        ASSERT(notlayoutedObjects.isEmpty());
290        return;
291    }
292
293    // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path.
294    HashSet<RenderObject*>::iterator end = notlayoutedObjects.end();
295    for (HashSet<RenderObject*>::iterator it = notlayoutedObjects.begin(); it != end; ++it)
296        invalidateResourcesOfChildren(*it);
297}
298
299bool SVGRenderSupport::isOverflowHidden(const RenderObject* object)
300{
301    // SVG doesn't support independent x/y overflow
302    ASSERT(object->style()->overflowX() == object->style()->overflowY());
303
304    // OSCROLL is never set for SVG - see StyleResolver::adjustRenderStyle
305    ASSERT(object->style()->overflowX() != OSCROLL);
306
307    // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size.
308    ASSERT(!object->isRoot());
309
310    return object->style()->overflowX() == OHIDDEN;
311}
312
313bool SVGRenderSupport::rendererHasSVGShadow(const RenderObject* object)
314{
315    // FIXME: Add support for RenderSVGBlock.
316
317    // FIXME: This should use a safer cast such as toRenderSVGModelObject().
318    if (object->isSVGShape() || object->isSVGImage() || object->isSVGContainer())
319        return static_cast<const RenderSVGModelObject*>(object)->hasSVGShadow();
320
321    if (object->isSVGRoot())
322        return toRenderSVGRoot(object)->hasSVGShadow();
323
324    return false;
325}
326
327void SVGRenderSupport::setRendererHasSVGShadow(RenderObject* object, bool hasShadow)
328{
329    // FIXME: Add support for RenderSVGBlock.
330
331    // FIXME: This should use a safer cast such as toRenderSVGModelObject().
332    if (object->isSVGShape() || object->isSVGImage() || object->isSVGContainer())
333        return static_cast<RenderSVGModelObject*>(object)->setHasSVGShadow(hasShadow);
334
335    if (object->isSVGRoot())
336        return static_cast<RenderSVGRoot*>(object)->setHasSVGShadow(hasShadow);
337}
338
339void SVGRenderSupport::intersectRepaintRectWithShadows(const RenderObject* object, FloatRect& repaintRect)
340{
341    // Since -webkit-svg-shadow enables shadow drawing for its children, but its children
342    // don't inherit the shadow in their SVGRenderStyle, we need to search our parents for
343    // shadows in order to correctly compute our repaint rect.
344
345    ASSERT(object);
346
347    const RenderObject* currentObject = object;
348
349    AffineTransform localToRootTransform;
350
351    while (currentObject && rendererHasSVGShadow(currentObject)) {
352        RenderStyle* style = currentObject->style();
353        if (style) {
354            const SVGRenderStyle* svgStyle = style->svgStyle();
355            if (svgStyle) {
356                if (const ShadowData* shadow = svgStyle->shadow())
357                    shadow->adjustRectForShadow(repaintRect);
358            }
359        }
360
361        repaintRect = currentObject->localToParentTransform().mapRect(repaintRect);
362        localToRootTransform *= currentObject->localToParentTransform();
363
364        currentObject = currentObject->parent();
365    };
366
367    if (localToRootTransform.isIdentity())
368        return;
369
370    AffineTransform rootToLocalTransform = localToRootTransform.inverse();
371    repaintRect = rootToLocalTransform.mapRect(repaintRect);
372}
373
374void SVGRenderSupport::intersectRepaintRectWithResources(const RenderObject* object, FloatRect& repaintRect)
375{
376    ASSERT(object);
377
378    RenderObject* renderer = const_cast<RenderObject*>(object);
379    SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
380    if (!resources)
381        return;
382
383#if ENABLE(FILTERS)
384    if (RenderSVGResourceFilter* filter = resources->filter())
385        repaintRect = filter->resourceBoundingBox(renderer);
386#endif
387
388    if (RenderSVGResourceClipper* clipper = resources->clipper())
389        repaintRect.intersect(clipper->resourceBoundingBox(renderer));
390
391    if (RenderSVGResourceMasker* masker = resources->masker())
392        repaintRect.intersect(masker->resourceBoundingBox(renderer));
393}
394
395bool SVGRenderSupport::filtersForceContainerLayout(RenderObject* object)
396{
397    // If any of this container's children need to be laid out, and a filter is applied
398    // to the container, we need to repaint the entire container.
399    if (!object->normalChildNeedsLayout())
400        return false;
401
402    SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
403    if (!resources || !resources->filter())
404        return false;
405
406    return true;
407}
408
409bool SVGRenderSupport::pointInClippingArea(RenderObject* object, const FloatPoint& point)
410{
411    ASSERT(object);
412
413    // We just take clippers into account to determine if a point is on the node. The Specification may
414    // change later and we also need to check maskers.
415    SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
416    if (!resources)
417        return true;
418
419    if (RenderSVGResourceClipper* clipper = resources->clipper())
420        return clipper->hitTestClipContent(object->objectBoundingBox(), point);
421
422    return true;
423}
424
425void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object)
426{
427    ASSERT(context);
428    ASSERT(style);
429    ASSERT(object);
430    ASSERT(object->node());
431    ASSERT(object->node()->isSVGElement());
432
433    const SVGRenderStyle* svgStyle = style->svgStyle();
434    ASSERT(svgStyle);
435
436    SVGLengthContext lengthContext(toSVGElement(object->node()));
437    context->setStrokeThickness(svgStyle->strokeWidth().value(lengthContext));
438    context->setLineCap(svgStyle->capStyle());
439    context->setLineJoin(svgStyle->joinStyle());
440    if (svgStyle->joinStyle() == MiterJoin)
441        context->setMiterLimit(svgStyle->strokeMiterLimit());
442
443    const Vector<SVGLength>& dashes = svgStyle->strokeDashArray();
444    if (dashes.isEmpty())
445        context->setStrokeStyle(SolidStroke);
446    else {
447        DashArray dashArray;
448        const Vector<SVGLength>::const_iterator end = dashes.end();
449        for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it)
450            dashArray.append((*it).value(lengthContext));
451
452        context->setLineDash(dashArray, svgStyle->strokeDashOffset().value(lengthContext));
453    }
454}
455
456void SVGRenderSupport::childAdded(RenderObject* parent, RenderObject* child)
457{
458    SVGRenderSupport::setRendererHasSVGShadow(child, SVGRenderSupport::rendererHasSVGShadow(parent) || SVGRenderSupport::rendererHasSVGShadow(child));
459}
460
461void SVGRenderSupport::styleChanged(RenderObject* object)
462{
463    RenderObject* parent = object->parent();
464    SVGRenderSupport::setRendererHasSVGShadow(object, (parent && SVGRenderSupport::rendererHasSVGShadow(parent)) || (object->style()->svgStyle() && object->style()->svgStyle()->shadow()));
465}
466
467}
468
469#endif
470