SVGDocumentExtensions.cpp 9.24 KB
Newer Older
1
/*
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 * Copyright (C) 2006 Apple Inc. All rights reserved.
 * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
 * Copyright (C) 2007 Rob Buis <buis@kde.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */
21 22

#include "config.h"
zimmermann's avatar
zimmermann committed
23

mjs's avatar
mjs committed
24
#if ENABLE(SVG)
25
#include "SVGDocumentExtensions.h"
darin's avatar
darin committed
26

27 28
#include "Console.h"
#include "DOMWindow.h"
darin's avatar
darin committed
29
#include "Document.h"
darin's avatar
darin committed
30
#include "EventListener.h"
31
#include "Frame.h"
rwlbuis's avatar
rwlbuis committed
32 33
#include "FrameLoader.h"
#include "Page.h"
34
#include "SMILTimeContainer.h"
35
#include "SVGElement.h"
36
#include "SVGSMILElement.h"
rwlbuis's avatar
rwlbuis committed
37
#include "SVGSVGElement.h"
38
#include "ScriptableDocumentParser.h"
39
#include <wtf/text/AtomicString.h>
40 41 42

namespace WebCore {

43 44
SVGDocumentExtensions::SVGDocumentExtensions(Document* document)
    : m_document(document)
45
    , m_resourcesCache(adoptPtr(new SVGResourcesCache))
46 47 48 49 50
{
}

SVGDocumentExtensions::~SVGDocumentExtensions()
{
51
    deleteAllValues(m_animatedElements);
bdash's avatar
bdash committed
52
    deleteAllValues(m_pendingResources);
53 54
}

55
void SVGDocumentExtensions::addTimeContainer(SVGSVGElement* element)
56
{
57
    m_timeContainers.add(element);
58 59
}

60
void SVGDocumentExtensions::removeTimeContainer(SVGSVGElement* element)
61
{
62
    m_timeContainers.remove(element);
63 64
}

65
void SVGDocumentExtensions::addResource(const AtomicString& id, RenderSVGResourceContainer* resource)
66 67 68 69 70 71 72 73 74 75
{
    ASSERT(resource);

    if (id.isEmpty())
        return;

    // Replaces resource if already present, to handle potential id changes
    m_resources.set(id, resource);
}

76
void SVGDocumentExtensions::removeResource(const AtomicString& id)
77
{
78
    if (id.isEmpty() || !m_resources.contains(id))
79 80 81 82 83
        return;

    m_resources.remove(id);
}

84
RenderSVGResourceContainer* SVGDocumentExtensions::resourceById(const AtomicString& id) const
85 86 87 88 89 90 91
{
    if (id.isEmpty())
        return 0;

    return m_resources.get(id);
}

92
void SVGDocumentExtensions::startAnimations()
93
{
94 95
    // FIXME: Eventually every "Time Container" will need a way to latch on to some global timer
    // starting animations for a document will do this "latching"
96
#if ENABLE(SVG_ANIMATION)    
97 98 99 100 101 102
    // FIXME: We hold a ref pointers to prevent a shadow tree from getting removed out from underneath us.
    // In the future we should refactor the use-element to avoid this. See https://webkit.org/b/53704
    Vector<RefPtr<SVGSVGElement> > timeContainers;
    timeContainers.appendRange(m_timeContainers.begin(), m_timeContainers.end());
    Vector<RefPtr<SVGSVGElement> >::iterator end = timeContainers.end();
    for (Vector<RefPtr<SVGSVGElement> >::iterator itr = timeContainers.begin(); itr != end; ++itr)
103
        (*itr)->timeContainer()->begin();
104
#endif
105
}
106 107
    
void SVGDocumentExtensions::pauseAnimations()
108
{
109
    HashSet<SVGSVGElement*>::iterator end = m_timeContainers.end();
110 111
    for (HashSet<SVGSVGElement*>::iterator itr = m_timeContainers.begin(); itr != end; ++itr)
        (*itr)->pauseAnimations();
112 113
}

114
void SVGDocumentExtensions::unpauseAnimations()
115
{
116
    HashSet<SVGSVGElement*>::iterator end = m_timeContainers.end();
117 118
    for (HashSet<SVGSVGElement*>::iterator itr = m_timeContainers.begin(); itr != end; ++itr)
        (*itr)->unpauseAnimations();
119 120
}

121 122
bool SVGDocumentExtensions::sampleAnimationAtTime(const String& elementId, SVGSMILElement* element, double time)
{
123 124 125 126 127 128
#if !ENABLE(SVG_ANIMATION)
    UNUSED_PARAM(elementId);
    UNUSED_PARAM(element);
    UNUSED_PARAM(time);
    return false;
#else
129 130 131 132 133 134 135
    ASSERT(element);
    SMILTimeContainer* container = element->timeContainer();
    if (!container || container->isPaused())
        return false;

    container->sampleAnimationAtTime(elementId, time);
    return true;
136
#endif
137 138
}

139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
void SVGDocumentExtensions::addAnimationElementToTarget(SVGSMILElement* animationElement, SVGElement* targetElement)
{
    ASSERT(targetElement);
    ASSERT(animationElement);

    if (HashSet<SVGSMILElement*>* animationElementsForTarget = m_animatedElements.get(targetElement)) {
        animationElementsForTarget->add(animationElement);
        return;
    }

    HashSet<SVGSMILElement*>* animationElementsForTarget = new HashSet<SVGSMILElement*>;
    animationElementsForTarget->add(animationElement);
    m_animatedElements.set(targetElement, animationElementsForTarget);
}

void SVGDocumentExtensions::removeAnimationElementFromTarget(SVGSMILElement* animationElement, SVGElement* targetElement)
{
    ASSERT(targetElement);
    ASSERT(animationElement);

    HashMap<SVGElement*, HashSet<SVGSMILElement*>* >::iterator it = m_animatedElements.find(targetElement);
    ASSERT(it != m_animatedElements.end());
    
    HashSet<SVGSMILElement*>* animationElementsForTarget = it->second;
    ASSERT(!animationElementsForTarget->isEmpty());

    animationElementsForTarget->remove(animationElement);
    if (animationElementsForTarget->isEmpty()) {
        m_animatedElements.remove(it);
        delete animationElementsForTarget;
    }
}

void SVGDocumentExtensions::removeAllAnimationElementsFromTarget(SVGElement* targetElement)
{
    ASSERT(targetElement);
    HashSet<SVGSMILElement*>* animationElementsForTarget = m_animatedElements.take(targetElement);
    if (!animationElementsForTarget)
        return;
178
#if ENABLE(SVG_ANIMATION)
179 180 181 182 183
    HashSet<SVGSMILElement*>::iterator it = animationElementsForTarget->begin();
    HashSet<SVGSMILElement*>::iterator end = animationElementsForTarget->end();
    for (; it != end; ++it)
        (*it)->resetTargetElement();
    delete animationElementsForTarget;
184 185 186
#else
    ASSERT_NOT_REACHED();
#endif
187 188
}

189 190 191 192 193 194 195 196 197 198 199 200
// FIXME: Callers should probably use ScriptController::eventHandlerLineNumber()
static int parserLineNumber(Document* document)
{
    ScriptableDocumentParser* parser = document->scriptableDocumentParser();
    if (!parser)
        return 1;
    return parser->lineNumber();
}

static void reportMessage(Document* document, MessageLevel level, const String& message)
{
    if (Frame* frame = document->frame())
201
        frame->domWindow()->console()->addMessage(JSMessageSource, LogMessageType, level, message, parserLineNumber(document), document->documentURI());
202 203
}

rwlbuis's avatar
rwlbuis committed
204 205
void SVGDocumentExtensions::reportWarning(const String& message)
{
206
    reportMessage(m_document, WarningMessageLevel, "Warning: " + message);
rwlbuis's avatar
rwlbuis committed
207 208 209 210
}

void SVGDocumentExtensions::reportError(const String& message)
{
211
    reportMessage(m_document, ErrorMessageLevel, "Error: " + message);
rwlbuis's avatar
rwlbuis committed
212 213
}

214
void SVGDocumentExtensions::addPendingResource(const AtomicString& id, SVGStyledElement* element)
zimmermann's avatar
zimmermann committed
215
{
216
    ASSERT(element);
zimmermann's avatar
zimmermann committed
217

zimmermann's avatar
zimmermann committed
218 219 220
    if (id.isEmpty())
        return;

zimmermann's avatar
zimmermann committed
221
    if (m_pendingResources.contains(id))
222
        m_pendingResources.get(id)->add(element);
zimmermann's avatar
zimmermann committed
223
    else {
224
        SVGPendingElements* set = new SVGPendingElements;
225
        set->add(element);
zimmermann's avatar
zimmermann committed
226 227 228

        m_pendingResources.add(id, set);
    }
229

230
    element->setHasPendingResources();
zimmermann's avatar
zimmermann committed
231 232
}

233
bool SVGDocumentExtensions::hasPendingResources(const AtomicString& id) const
zimmermann's avatar
zimmermann committed
234
{
zimmermann's avatar
zimmermann committed
235 236 237
    if (id.isEmpty())
        return false;

zimmermann's avatar
zimmermann committed
238 239 240
    return m_pendingResources.contains(id);
}

241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
bool SVGDocumentExtensions::isElementInPendingResources(SVGStyledElement* element) const
{
    ASSERT(element);

    if (m_pendingResources.isEmpty())
        return false;

    HashMap<AtomicString, SVGPendingElements*>::const_iterator end = m_pendingResources.end();
    for (HashMap<AtomicString, SVGPendingElements*>::const_iterator it = m_pendingResources.begin(); it != end; ++it) {
        SVGPendingElements* elements = it->second;
        ASSERT(elements);

        if (elements->contains(element))
            return true;
    }
    return false;
}

259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
void SVGDocumentExtensions::removeElementFromPendingResources(SVGStyledElement* element)
{
    ASSERT(element);

    if (m_pendingResources.isEmpty() || !element->hasPendingResources())
        return;

    Vector<AtomicString> toBeRemoved;
    HashMap<AtomicString, SVGPendingElements*>::iterator end = m_pendingResources.end();
    for (HashMap<AtomicString, SVGPendingElements*>::iterator it = m_pendingResources.begin(); it != end; ++it) {
        SVGPendingElements* elements = it->second;
        ASSERT(elements);
        ASSERT(!elements->isEmpty());

        elements->remove(element);
        if (elements->isEmpty())
            toBeRemoved.append(it->first);
    }

278 279
    element->clearHasPendingResourcesIfPossible();

280 281 282 283 284 285 286 287 288
    if (toBeRemoved.isEmpty())
        return;

    Vector<AtomicString>::iterator endVector = toBeRemoved.end();
    for (Vector<AtomicString>::iterator it = toBeRemoved.begin(); it != endVector; ++it)
        m_pendingResources.remove(*it);
}

PassOwnPtr<SVGDocumentExtensions::SVGPendingElements> SVGDocumentExtensions::removePendingResource(const AtomicString& id)
zimmermann's avatar
zimmermann committed
289 290 291
{
    ASSERT(m_pendingResources.contains(id));

292
    OwnPtr<SVGPendingElements> set = adoptPtr(m_pendingResources.get(id));
zimmermann's avatar
zimmermann committed
293
    m_pendingResources.remove(id);
294
    return set.release();
zimmermann's avatar
zimmermann committed
295 296
}

297 298 299
}

#endif