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
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->paintInvalidationRectInLocalCoordinates();
55    object->computeFloatRectForPaintInvalidation(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 computeFloatRectForPaintInvalidation() on our parent.
64    repaintRect = object->localToParentTransform().mapRect(repaintRect);
65    object->parent()->computeFloatRectForPaintInvalidation(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::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
111bool 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.
120inline 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
135void 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
157bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo)
158{
159    return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect);
160}
161
162const 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
172inline 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
182inline 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
195bool 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
208void 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
276void SVGRenderSupport::layoutResourcesIfNeeded(const RenderObject* object)
277{
278    ASSERT(object);
279
280    SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
281    if (resources)
282        resources->layoutIfNeeded();
283}
284
285bool 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
293void 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
311bool 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
325bool 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
341void 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
371void 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
400bool 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