Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) 2006 Apple Inc. All rights reserved.
      3  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann (at) kde.org>
      4  * Copyright (C) 2007 Rob Buis <buis (at) kde.org>
      5  *
      6  * This library is free software; you can redistribute it and/or
      7  * modify it under the terms of the GNU Library General Public
      8  * License as published by the Free Software Foundation; either
      9  * version 2 of the License, or (at your option) any later version.
     10  *
     11  * This library is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14  * Library General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU Library General Public License
     17  * along with this library; see the file COPYING.LIB.  If not, write to
     18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19  * Boston, MA 02110-1301, USA.
     20  */
     21 
     22 #include "config.h"
     23 #include "core/svg/SVGDocumentExtensions.h"
     24 
     25 #include "XLinkNames.h"
     26 #include "core/dom/Document.h"
     27 #include "core/rendering/svg/SVGResourcesCache.h"
     28 #include "core/svg/SVGElement.h"
     29 #include "core/svg/SVGSVGElement.h"
     30 #include "core/svg/animation/SMILTimeContainer.h"
     31 #include "wtf/text/AtomicString.h"
     32 
     33 namespace WebCore {
     34 
     35 SVGDocumentExtensions::SVGDocumentExtensions(Document* document)
     36     : m_document(document)
     37     , m_resourcesCache(adoptPtr(new SVGResourcesCache))
     38 {
     39 }
     40 
     41 SVGDocumentExtensions::~SVGDocumentExtensions()
     42 {
     43 }
     44 
     45 void SVGDocumentExtensions::addTimeContainer(SVGSVGElement* element)
     46 {
     47     m_timeContainers.add(element);
     48 }
     49 
     50 void SVGDocumentExtensions::removeTimeContainer(SVGSVGElement* element)
     51 {
     52     m_timeContainers.remove(element);
     53 }
     54 
     55 void SVGDocumentExtensions::addResource(const AtomicString& id, RenderSVGResourceContainer* resource)
     56 {
     57     ASSERT(resource);
     58 
     59     if (id.isEmpty())
     60         return;
     61 
     62     // Replaces resource if already present, to handle potential id changes
     63     m_resources.set(id, resource);
     64 }
     65 
     66 void SVGDocumentExtensions::removeResource(const AtomicString& id)
     67 {
     68     if (id.isEmpty() || !m_resources.contains(id))
     69         return;
     70 
     71     m_resources.remove(id);
     72 }
     73 
     74 RenderSVGResourceContainer* SVGDocumentExtensions::resourceById(const AtomicString& id) const
     75 {
     76     if (id.isEmpty())
     77         return 0;
     78 
     79     return m_resources.get(id);
     80 }
     81 
     82 void SVGDocumentExtensions::startAnimations()
     83 {
     84     // FIXME: Eventually every "Time Container" will need a way to latch on to some global timer
     85     // starting animations for a document will do this "latching"
     86     // FIXME: We hold a ref pointers to prevent a shadow tree from getting removed out from underneath us.
     87     // In the future we should refactor the use-element to avoid this. See https://webkit.org/b/53704
     88     Vector<RefPtr<SVGSVGElement> > timeContainers;
     89     timeContainers.appendRange(m_timeContainers.begin(), m_timeContainers.end());
     90     Vector<RefPtr<SVGSVGElement> >::iterator end = timeContainers.end();
     91     for (Vector<RefPtr<SVGSVGElement> >::iterator itr = timeContainers.begin(); itr != end; ++itr)
     92         (*itr)->timeContainer()->begin();
     93 }
     94 
     95 void SVGDocumentExtensions::pauseAnimations()
     96 {
     97     HashSet<SVGSVGElement*>::iterator end = m_timeContainers.end();
     98     for (HashSet<SVGSVGElement*>::iterator itr = m_timeContainers.begin(); itr != end; ++itr)
     99         (*itr)->pauseAnimations();
    100 }
    101 
    102 void SVGDocumentExtensions::unpauseAnimations()
    103 {
    104     HashSet<SVGSVGElement*>::iterator end = m_timeContainers.end();
    105     for (HashSet<SVGSVGElement*>::iterator itr = m_timeContainers.begin(); itr != end; ++itr)
    106         (*itr)->unpauseAnimations();
    107 }
    108 
    109 void SVGDocumentExtensions::dispatchSVGLoadEventToOutermostSVGElements()
    110 {
    111     Vector<RefPtr<SVGSVGElement> > timeContainers;
    112     timeContainers.appendRange(m_timeContainers.begin(), m_timeContainers.end());
    113 
    114     Vector<RefPtr<SVGSVGElement> >::iterator end = timeContainers.end();
    115     for (Vector<RefPtr<SVGSVGElement> >::iterator it = timeContainers.begin(); it != end; ++it) {
    116         SVGSVGElement* outerSVG = (*it).get();
    117         if (!outerSVG->isOutermostSVGSVGElement())
    118             continue;
    119         outerSVG->sendSVGLoadEventIfPossible();
    120     }
    121 }
    122 
    123 static void reportMessage(Document* document, MessageLevel level, const String& message)
    124 {
    125     if (document->frame())
    126         document->addConsoleMessage(RenderingMessageSource, level, message);
    127 }
    128 
    129 void SVGDocumentExtensions::reportWarning(const String& message)
    130 {
    131     reportMessage(m_document, WarningMessageLevel, "Warning: " + message);
    132 }
    133 
    134 void SVGDocumentExtensions::reportError(const String& message)
    135 {
    136     reportMessage(m_document, ErrorMessageLevel, "Error: " + message);
    137 }
    138 
    139 void SVGDocumentExtensions::addPendingResource(const AtomicString& id, Element* element)
    140 {
    141     ASSERT(element);
    142     ASSERT(element->inDocument());
    143 
    144     if (id.isEmpty())
    145         return;
    146 
    147     HashMap<AtomicString, OwnPtr<SVGPendingElements> >::AddResult result = m_pendingResources.add(id, nullptr);
    148     if (result.isNewEntry)
    149         result.iterator->value = adoptPtr(new SVGPendingElements);
    150     result.iterator->value->add(element);
    151 
    152     element->setHasPendingResources();
    153 }
    154 
    155 bool SVGDocumentExtensions::hasPendingResource(const AtomicString& id) const
    156 {
    157     if (id.isEmpty())
    158         return false;
    159 
    160     return m_pendingResources.contains(id);
    161 }
    162 
    163 bool SVGDocumentExtensions::isElementPendingResources(Element* element) const
    164 {
    165     // This algorithm takes time proportional to the number of pending resources and need not.
    166     // If performance becomes an issue we can keep a counted set of elements and answer the question efficiently.
    167 
    168     ASSERT(element);
    169 
    170     HashMap<AtomicString, OwnPtr<SVGPendingElements> >::const_iterator end = m_pendingResources.end();
    171     for (HashMap<AtomicString, OwnPtr<SVGPendingElements> >::const_iterator it = m_pendingResources.begin(); it != end; ++it) {
    172         SVGPendingElements* elements = it->value.get();
    173         ASSERT(elements);
    174 
    175         if (elements->contains(element))
    176             return true;
    177     }
    178     return false;
    179 }
    180 
    181 bool SVGDocumentExtensions::isElementPendingResource(Element* element, const AtomicString& id) const
    182 {
    183     ASSERT(element);
    184 
    185     if (!hasPendingResource(id))
    186         return false;
    187 
    188     return m_pendingResources.get(id)->contains(element);
    189 }
    190 
    191 void SVGDocumentExtensions::clearHasPendingResourcesIfPossible(Element* element)
    192 {
    193     if (!isElementPendingResources(element))
    194         element->clearHasPendingResources();
    195 }
    196 
    197 void SVGDocumentExtensions::removeElementFromPendingResources(Element* element)
    198 {
    199     ASSERT(element);
    200 
    201     // Remove the element from pending resources.
    202     if (!m_pendingResources.isEmpty() && element->hasPendingResources()) {
    203         Vector<AtomicString> toBeRemoved;
    204         HashMap<AtomicString, OwnPtr<SVGPendingElements> >::iterator end = m_pendingResources.end();
    205         for (HashMap<AtomicString, OwnPtr<SVGPendingElements> >::iterator it = m_pendingResources.begin(); it != end; ++it) {
    206             SVGPendingElements* elements = it->value.get();
    207             ASSERT(elements);
    208             ASSERT(!elements->isEmpty());
    209 
    210             elements->remove(element);
    211             if (elements->isEmpty())
    212                 toBeRemoved.append(it->key);
    213         }
    214 
    215         clearHasPendingResourcesIfPossible(element);
    216 
    217         // We use the removePendingResource function here because it deals with set lifetime correctly.
    218         Vector<AtomicString>::iterator vectorEnd = toBeRemoved.end();
    219         for (Vector<AtomicString>::iterator it = toBeRemoved.begin(); it != vectorEnd; ++it)
    220             removePendingResource(*it);
    221     }
    222 
    223     // Remove the element from pending resources that were scheduled for removal.
    224     if (!m_pendingResourcesForRemoval.isEmpty()) {
    225         Vector<AtomicString> toBeRemoved;
    226         HashMap<AtomicString, OwnPtr<SVGPendingElements> >::iterator end = m_pendingResourcesForRemoval.end();
    227         for (HashMap<AtomicString, OwnPtr<SVGPendingElements> >::iterator it = m_pendingResourcesForRemoval.begin(); it != end; ++it) {
    228             SVGPendingElements* elements = it->value.get();
    229             ASSERT(elements);
    230             ASSERT(!elements->isEmpty());
    231 
    232             elements->remove(element);
    233             if (elements->isEmpty())
    234                 toBeRemoved.append(it->key);
    235         }
    236 
    237         // We use the removePendingResourceForRemoval function here because it deals with set lifetime correctly.
    238         Vector<AtomicString>::iterator vectorEnd = toBeRemoved.end();
    239         for (Vector<AtomicString>::iterator it = toBeRemoved.begin(); it != vectorEnd; ++it)
    240             removePendingResourceForRemoval(*it);
    241     }
    242 }
    243 
    244 PassOwnPtr<SVGDocumentExtensions::SVGPendingElements> SVGDocumentExtensions::removePendingResource(const AtomicString& id)
    245 {
    246     ASSERT(m_pendingResources.contains(id));
    247     return m_pendingResources.take(id);
    248 }
    249 
    250 PassOwnPtr<SVGDocumentExtensions::SVGPendingElements> SVGDocumentExtensions::removePendingResourceForRemoval(const AtomicString& id)
    251 {
    252     ASSERT(m_pendingResourcesForRemoval.contains(id));
    253     return m_pendingResourcesForRemoval.take(id);
    254 }
    255 
    256 void SVGDocumentExtensions::markPendingResourcesForRemoval(const AtomicString& id)
    257 {
    258     if (id.isEmpty())
    259         return;
    260 
    261     ASSERT(!m_pendingResourcesForRemoval.contains(id));
    262 
    263     OwnPtr<SVGPendingElements> existing = m_pendingResources.take(id);
    264     if (existing && !existing->isEmpty())
    265         m_pendingResourcesForRemoval.add(id, existing.release());
    266 }
    267 
    268 Element* SVGDocumentExtensions::removeElementFromPendingResourcesForRemoval(const AtomicString& id)
    269 {
    270     if (id.isEmpty())
    271         return 0;
    272 
    273     SVGPendingElements* resourceSet = m_pendingResourcesForRemoval.get(id);
    274     if (!resourceSet || resourceSet->isEmpty())
    275         return 0;
    276 
    277     SVGPendingElements::iterator firstElement = resourceSet->begin();
    278     Element* element = *firstElement;
    279 
    280     resourceSet->remove(firstElement);
    281 
    282     if (resourceSet->isEmpty())
    283         removePendingResourceForRemoval(id);
    284 
    285     return element;
    286 }
    287 
    288 HashSet<SVGElement*>* SVGDocumentExtensions::setOfElementsReferencingTarget(SVGElement* referencedElement) const
    289 {
    290     ASSERT(referencedElement);
    291     const HashMap<SVGElement*, OwnPtr<HashSet<SVGElement*> > >::const_iterator it = m_elementDependencies.find(referencedElement);
    292     if (it == m_elementDependencies.end())
    293         return 0;
    294     return it->value.get();
    295 }
    296 
    297 void SVGDocumentExtensions::addElementReferencingTarget(SVGElement* referencingElement, SVGElement* referencedElement)
    298 {
    299     ASSERT(referencingElement);
    300     ASSERT(referencedElement);
    301 
    302     if (HashSet<SVGElement*>* elements = m_elementDependencies.get(referencedElement)) {
    303         elements->add(referencingElement);
    304         return;
    305     }
    306 
    307     OwnPtr<HashSet<SVGElement*> > elements = adoptPtr(new HashSet<SVGElement*>);
    308     elements->add(referencingElement);
    309     m_elementDependencies.set(referencedElement, elements.release());
    310 }
    311 
    312 void SVGDocumentExtensions::removeAllTargetReferencesForElement(SVGElement* referencingElement)
    313 {
    314     Vector<SVGElement*> toBeRemoved;
    315 
    316     HashMap<SVGElement*, OwnPtr<HashSet<SVGElement*> > >::iterator end = m_elementDependencies.end();
    317     for (HashMap<SVGElement*, OwnPtr<HashSet<SVGElement*> > >::iterator it = m_elementDependencies.begin(); it != end; ++it) {
    318         SVGElement* referencedElement = it->key;
    319         HashSet<SVGElement*>* referencingElements = it->value.get();
    320         HashSet<SVGElement*>::iterator setIt = referencingElements->find(referencingElement);
    321         if (setIt == referencingElements->end())
    322             continue;
    323 
    324         referencingElements->remove(setIt);
    325         if (referencingElements->isEmpty())
    326             toBeRemoved.append(referencedElement);
    327     }
    328 
    329     Vector<SVGElement*>::iterator vectorEnd = toBeRemoved.end();
    330     for (Vector<SVGElement*>::iterator it = toBeRemoved.begin(); it != vectorEnd; ++it)
    331         m_elementDependencies.remove(*it);
    332 }
    333 
    334 void SVGDocumentExtensions::rebuildAllElementReferencesForTarget(SVGElement* referencedElement)
    335 {
    336     ASSERT(referencedElement);
    337     HashMap<SVGElement*, OwnPtr<HashSet<SVGElement*> > >::iterator it = m_elementDependencies.find(referencedElement);
    338     if (it == m_elementDependencies.end())
    339         return;
    340     ASSERT(it->key == referencedElement);
    341     Vector<SVGElement*> toBeNotified;
    342 
    343     HashSet<SVGElement*>* referencingElements = it->value.get();
    344     HashSet<SVGElement*>::iterator setEnd = referencingElements->end();
    345     for (HashSet<SVGElement*>::iterator setIt = referencingElements->begin(); setIt != setEnd; ++setIt)
    346         toBeNotified.append(*setIt);
    347 
    348     // Force rebuilding the referencingElement so it knows about this change.
    349     Vector<SVGElement*>::iterator vectorEnd = toBeNotified.end();
    350     for (Vector<SVGElement*>::iterator vectorIt = toBeNotified.begin(); vectorIt != vectorEnd; ++vectorIt) {
    351         // Before rebuilding referencingElement ensure it was not removed from under us.
    352         if (HashSet<SVGElement*>* referencingElements = setOfElementsReferencingTarget(referencedElement)) {
    353             if (referencingElements->contains(*vectorIt))
    354                 (*vectorIt)->svgAttributeChanged(XLinkNames::hrefAttr);
    355         }
    356     }
    357 }
    358 
    359 void SVGDocumentExtensions::removeAllElementReferencesForTarget(SVGElement* referencedElement)
    360 {
    361     ASSERT(referencedElement);
    362     HashMap<SVGElement*, OwnPtr<HashSet<SVGElement*> > >::iterator it = m_elementDependencies.find(referencedElement);
    363     if (it == m_elementDependencies.end())
    364         return;
    365     ASSERT(it->key == referencedElement);
    366 
    367     m_elementDependencies.remove(it);
    368 }
    369 
    370 #if ENABLE(SVG_FONTS)
    371 void SVGDocumentExtensions::registerSVGFontFaceElement(SVGFontFaceElement* element)
    372 {
    373     m_svgFontFaceElements.add(element);
    374 }
    375 
    376 void SVGDocumentExtensions::unregisterSVGFontFaceElement(SVGFontFaceElement* element)
    377 {
    378     ASSERT(m_svgFontFaceElements.contains(element));
    379     m_svgFontFaceElements.remove(element);
    380 }
    381 #endif
    382 
    383 }
    384