1/*
2 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB.  If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20#include "config.h"
21
22#if ENABLE(SVG)
23#include "RenderSVGResourceContainer.h"
24
25#include "RenderSVGShadowTreeRootContainer.h"
26#include "SVGStyledTransformableElement.h"
27
28namespace WebCore {
29
30static inline SVGDocumentExtensions* svgExtensionsFromNode(Node* node)
31{
32    ASSERT(node);
33    ASSERT(node->document());
34    return node->document()->accessSVGExtensions();
35}
36
37RenderSVGResourceContainer::RenderSVGResourceContainer(SVGStyledElement* node)
38    : RenderSVGHiddenContainer(node)
39    , m_id(node->hasID() ? node->getIdAttribute() : nullAtom)
40    , m_registered(false)
41{
42}
43
44RenderSVGResourceContainer::~RenderSVGResourceContainer()
45{
46    if (m_registered)
47        svgExtensionsFromNode(node())->removeResource(m_id);
48}
49
50void RenderSVGResourceContainer::layout()
51{
52    // Invalidate all resources if our layout changed.
53    if (m_everHadLayout && selfNeedsLayout())
54        removeAllClientsFromCache();
55
56    RenderSVGHiddenContainer::layout();
57}
58
59void RenderSVGResourceContainer::destroy()
60{
61    SVGResourcesCache::resourceDestroyed(this);
62    RenderSVGHiddenContainer::destroy();
63}
64
65void RenderSVGResourceContainer::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
66{
67    RenderSVGHiddenContainer::styleDidChange(diff, oldStyle);
68
69    if (!m_registered) {
70        m_registered = true;
71        registerResource();
72    }
73}
74
75void RenderSVGResourceContainer::idChanged()
76{
77    // Invalidate all our current clients.
78    removeAllClientsFromCache();
79
80    // Remove old id, that is guaranteed to be present in cache.
81    SVGDocumentExtensions* extensions = svgExtensionsFromNode(node());
82    extensions->removeResource(m_id);
83    m_id = static_cast<Element*>(node())->getIdAttribute();
84
85    registerResource();
86}
87
88void RenderSVGResourceContainer::markAllClientsForInvalidation(InvalidationMode mode)
89{
90    if (m_clients.isEmpty())
91        return;
92
93    bool needsLayout = mode == LayoutAndBoundariesInvalidation;
94    bool markForInvalidation = mode != ParentOnlyInvalidation;
95
96    HashSet<RenderObject*>::iterator end = m_clients.end();
97    for (HashSet<RenderObject*>::iterator it = m_clients.begin(); it != end; ++it) {
98        RenderObject* client = *it;
99        if (client->isSVGResourceContainer()) {
100            client->toRenderSVGResourceContainer()->removeAllClientsFromCache(markForInvalidation);
101            continue;
102        }
103
104        if (markForInvalidation)
105            markClientForInvalidation(client, mode);
106
107        if (needsLayout)
108            client->setNeedsLayout(true);
109
110        // Invalidate resources in ancestor chain, if needed.
111        RenderObject* current = client->parent();
112        while (current) {
113            if (current->isSVGResourceContainer()) {
114                current->toRenderSVGResourceContainer()->removeAllClientsFromCache(markForInvalidation);
115                break;
116            }
117
118            current = current->parent();
119        }
120    }
121}
122
123void RenderSVGResourceContainer::markClientForInvalidation(RenderObject* client, InvalidationMode mode)
124{
125    ASSERT(client);
126    ASSERT(!m_clients.isEmpty());
127
128    switch (mode) {
129    case LayoutAndBoundariesInvalidation:
130    case BoundariesInvalidation:
131        client->setNeedsBoundariesUpdate();
132        break;
133    case RepaintInvalidation:
134        if (client->view())
135            client->repaint();
136        break;
137    case ParentOnlyInvalidation:
138        break;
139    }
140}
141
142void RenderSVGResourceContainer::addClient(RenderObject* client)
143{
144    ASSERT(client);
145    m_clients.add(client);
146}
147
148void RenderSVGResourceContainer::removeClient(RenderObject* client)
149{
150    ASSERT(client);
151    m_clients.remove(client);
152}
153
154void RenderSVGResourceContainer::registerResource()
155{
156    SVGDocumentExtensions* extensions = svgExtensionsFromNode(node());
157    if (!extensions->isPendingResource(m_id)) {
158        extensions->addResource(m_id, this);
159        return;
160    }
161
162    OwnPtr<SVGDocumentExtensions::SVGPendingElements> clients(extensions->removePendingResource(m_id));
163
164    // Cache us with the new id.
165    extensions->addResource(m_id, this);
166
167    // Update cached resources of pending clients.
168    const SVGDocumentExtensions::SVGPendingElements::const_iterator end = clients->end();
169    for (SVGDocumentExtensions::SVGPendingElements::const_iterator it = clients->begin(); it != end; ++it) {
170        RenderObject* renderer = (*it)->renderer();
171        if (!renderer)
172            continue;
173        SVGResourcesCache::clientUpdatedFromElement(renderer, renderer->style());
174        renderer->setNeedsLayout(true);
175    }
176}
177
178// FIXME: This does not belong here.
179AffineTransform RenderSVGResourceContainer::transformOnNonScalingStroke(RenderObject* object, const AffineTransform& resourceTransform)
180{
181    if (!object->isSVGPath())
182        return resourceTransform;
183
184    SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(object->node());
185    AffineTransform transform = element->getScreenCTM(SVGLocatable::DisallowStyleUpdate);
186    transform *= resourceTransform;
187    return transform;
188}
189
190}
191
192#endif
193