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/platform/graphics/transforms/TransformState.h"
30#include "core/rendering/RenderGeometryMap.h"
31#include "core/rendering/RenderLayer.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 "wtf/UnusedParam.h"
43
44namespace WebCore {
45
46LayoutRect 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
59void 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
68void 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
84const 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
103bool 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.
114static 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
129void 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
155bool 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
163const 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
173static 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
183static 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
196bool 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
209void 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        if (needsLayout)
244            child->setNeedsLayout(MarkOnlyThis);
245
246        if (child->needsLayout()) {
247            child->layout();
248            // Renderers are responsible for repainting themselves when changing, except
249            // for the initial paint to avoid potential double-painting caused by non-sensical "old" bounds.
250            // We could handle this in the individual objects, but for now it's easier to have
251            // parent containers call repaint().  (RenderBlock::layout* has similar logic.)
252            if (!childEverHadLayout)
253                child->repaint();
254        } else if (layoutSizeChanged)
255            notlayoutedObjects.add(child);
256
257        ASSERT(!child->needsLayout());
258    }
259
260    if (!layoutSizeChanged) {
261        ASSERT(notlayoutedObjects.isEmpty());
262        return;
263    }
264
265    // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path.
266    HashSet<RenderObject*>::iterator end = notlayoutedObjects.end();
267    for (HashSet<RenderObject*>::iterator it = notlayoutedObjects.begin(); it != end; ++it)
268        invalidateResourcesOfChildren(*it);
269}
270
271bool SVGRenderSupport::isOverflowHidden(const RenderObject* object)
272{
273    // SVG doesn't support independent x/y overflow
274    ASSERT(object->style()->overflowX() == object->style()->overflowY());
275
276    // OSCROLL is never set for SVG - see StyleResolver::adjustRenderStyle
277    ASSERT(object->style()->overflowX() != OSCROLL);
278
279    // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size.
280    ASSERT(!object->isRoot());
281
282    return object->style()->overflowX() == OHIDDEN;
283}
284
285void SVGRenderSupport::intersectRepaintRectWithResources(const RenderObject* object, FloatRect& repaintRect)
286{
287    ASSERT(object);
288
289    RenderObject* renderer = const_cast<RenderObject*>(object);
290    SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
291    if (!resources)
292        return;
293
294    if (RenderSVGResourceFilter* filter = resources->filter())
295        repaintRect = filter->resourceBoundingBox(renderer);
296
297    if (RenderSVGResourceClipper* clipper = resources->clipper())
298        repaintRect.intersect(clipper->resourceBoundingBox(renderer));
299
300    if (RenderSVGResourceMasker* masker = resources->masker())
301        repaintRect.intersect(masker->resourceBoundingBox(renderer));
302}
303
304bool SVGRenderSupport::filtersForceContainerLayout(RenderObject* object)
305{
306    // If any of this container's children need to be laid out, and a filter is applied
307    // to the container, we need to repaint the entire container.
308    if (!object->normalChildNeedsLayout())
309        return false;
310
311    SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
312    if (!resources || !resources->filter())
313        return false;
314
315    return true;
316}
317
318bool SVGRenderSupport::pointInClippingArea(RenderObject* object, const FloatPoint& point)
319{
320    ASSERT(object);
321
322    // We just take clippers into account to determine if a point is on the node. The Specification may
323    // change later and we also need to check maskers.
324    SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
325    if (!resources)
326        return true;
327
328    if (RenderSVGResourceClipper* clipper = resources->clipper())
329        return clipper->hitTestClipContent(object->objectBoundingBox(), point);
330
331    return true;
332}
333
334void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object)
335{
336    ASSERT(context);
337    ASSERT(style);
338    ASSERT(object);
339    ASSERT(object->node());
340    ASSERT(object->node()->isSVGElement());
341
342    const SVGRenderStyle* svgStyle = style->svgStyle();
343    ASSERT(svgStyle);
344
345    SVGLengthContext lengthContext(toSVGElement(object->node()));
346    context->setStrokeThickness(svgStyle->strokeWidth().value(lengthContext));
347    context->setLineCap(svgStyle->capStyle());
348    context->setLineJoin(svgStyle->joinStyle());
349    context->setMiterLimit(svgStyle->strokeMiterLimit());
350
351    const Vector<SVGLength>& dashes = svgStyle->strokeDashArray();
352    if (dashes.isEmpty())
353        return;
354
355    DashArray dashArray;
356    const Vector<SVGLength>::const_iterator end = dashes.end();
357    for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it)
358        dashArray.append((*it).value(lengthContext));
359
360    context->setLineDash(dashArray, svgStyle->strokeDashOffset().value(lengthContext));
361}
362
363void SVGRenderSupport::applyStrokeStyleToStrokeData(StrokeData* strokeData, const RenderStyle* style, const RenderObject* object)
364{
365    ASSERT(strokeData);
366    ASSERT(style);
367    ASSERT(object);
368    ASSERT(object->node());
369    ASSERT(object->node()->isSVGElement());
370
371    const SVGRenderStyle* svgStyle = style->svgStyle();
372    ASSERT(svgStyle);
373
374    SVGLengthContext lengthContext(toSVGElement(object->node()));
375    strokeData->setThickness(svgStyle->strokeWidth().value(lengthContext));
376    strokeData->setLineCap(svgStyle->capStyle());
377    strokeData->setLineJoin(svgStyle->joinStyle());
378    strokeData->setMiterLimit(svgStyle->strokeMiterLimit());
379
380    const Vector<SVGLength>& dashes = svgStyle->strokeDashArray();
381    if (dashes.isEmpty())
382        return;
383
384    DashArray dashArray;
385    const Vector<SVGLength>::const_iterator end = dashes.end();
386    for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it)
387        dashArray.append((*it).value(lengthContext));
388
389    strokeData->setLineDash(dashArray, svgStyle->strokeDashOffset().value(lengthContext));
390}
391
392bool SVGRenderSupport::isEmptySVGInlineText(const RenderObject* object)
393{
394    // RenderSVGInlineText performs whitespace filtering in order to support xml:space
395    // (http://www.w3.org/TR/SVG/struct.html#LangSpaceAttrs), and can end up with an empty string
396    // even when its original constructor argument is non-empty.
397    return object->isSVGInlineText() && toRenderSVGInlineText(object)->hasEmptyText();
398}
399
400}
401