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 "core/XLinkNames.h"
     26 #include "core/dom/Document.h"
     27 #include "core/rendering/RenderView.h"
     28 #include "core/rendering/svg/SVGResourcesCache.h"
     29 #include "core/svg/SVGFontFaceElement.h"
     30 #include "core/svg/SVGSVGElement.h"
     31 #include "core/svg/SVGViewSpec.h"
     32 #include "core/svg/SVGZoomAndPan.h"
     33 #include "core/svg/animation/SMILTimeContainer.h"
     34 #include "wtf/TemporaryChange.h"
     35 #include "wtf/text/AtomicString.h"
     36 
     37 namespace WebCore {
     38 
     39 SVGDocumentExtensions::SVGDocumentExtensions(Document* document)
     40     : m_document(document)
     41     , m_resourcesCache(adoptPtr(new SVGResourcesCache))
     42 #if ASSERT_ENABLED
     43     , m_inRelativeLengthSVGRootsInvalidation(false)
     44 #endif
     45 {
     46 }
     47 
     48 SVGDocumentExtensions::~SVGDocumentExtensions()
     49 {
     50 }
     51 
     52 void SVGDocumentExtensions::addTimeContainer(SVGSVGElement* element)
     53 {
     54     m_timeContainers.add(element);
     55 }
     56 
     57 void SVGDocumentExtensions::removeTimeContainer(SVGSVGElement* element)
     58 {
     59     m_timeContainers.remove(element);
     60 }
     61 
     62 void SVGDocumentExtensions::addResource(const AtomicString& id, RenderSVGResourceContainer* resource)
     63 {
     64     ASSERT(resource);
     65 
     66     if (id.isEmpty())
     67         return;
     68 
     69     // Replaces resource if already present, to handle potential id changes
     70     m_resources.set(id, resource);
     71 }
     72 
     73 void SVGDocumentExtensions::removeResource(const AtomicString& id)
     74 {
     75     if (id.isEmpty())
     76         return;
     77 
     78     m_resources.remove(id);
     79 }
     80 
     81 RenderSVGResourceContainer* SVGDocumentExtensions::resourceById(const AtomicString& id) const
     82 {
     83     if (id.isEmpty())
     84         return 0;
     85 
     86     return m_resources.get(id);
     87 }
     88 
     89 void SVGDocumentExtensions::serviceOnAnimationFrame(Document& document, double monotonicAnimationStartTime)
     90 {
     91     if (!document.svgExtensions())
     92         return;
     93     document.accessSVGExtensions().serviceAnimations(monotonicAnimationStartTime);
     94 }
     95 
     96 void SVGDocumentExtensions::serviceAnimations(double monotonicAnimationStartTime)
     97 {
     98     WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> > timeContainers;
     99     timeContainers.appendRange(m_timeContainers.begin(), m_timeContainers.end());
    100     WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> >::iterator end = timeContainers.end();
    101     for (WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> >::iterator itr = timeContainers.begin(); itr != end; ++itr)
    102         (*itr)->timeContainer()->serviceAnimations(monotonicAnimationStartTime);
    103 }
    104 
    105 void SVGDocumentExtensions::startAnimations()
    106 {
    107     // FIXME: Eventually every "Time Container" will need a way to latch on to some global timer
    108     // starting animations for a document will do this "latching"
    109     // FIXME: We hold a ref pointers to prevent a shadow tree from getting removed out from underneath us.
    110     // In the future we should refactor the use-element to avoid this. See https://webkit.org/b/53704
    111     WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> > timeContainers;
    112     timeContainers.appendRange(m_timeContainers.begin(), m_timeContainers.end());
    113     WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> >::iterator end = timeContainers.end();
    114     for (WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> >::iterator itr = timeContainers.begin(); itr != end; ++itr) {
    115         SMILTimeContainer* timeContainer = (*itr)->timeContainer();
    116         if (!timeContainer->isStarted())
    117             timeContainer->begin();
    118     }
    119 }
    120 
    121 void SVGDocumentExtensions::pauseAnimations()
    122 {
    123     WillBeHeapHashSet<RawPtrWillBeMember<SVGSVGElement> >::iterator end = m_timeContainers.end();
    124     for (WillBeHeapHashSet<RawPtrWillBeMember<SVGSVGElement> >::iterator itr = m_timeContainers.begin(); itr != end; ++itr)
    125         (*itr)->pauseAnimations();
    126 }
    127 
    128 void SVGDocumentExtensions::dispatchSVGLoadEventToOutermostSVGElements()
    129 {
    130     WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> > timeContainers;
    131     timeContainers.appendRange(m_timeContainers.begin(), m_timeContainers.end());
    132 
    133     WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> >::iterator end = timeContainers.end();
    134     for (WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> >::iterator it = timeContainers.begin(); it != end; ++it) {
    135         SVGSVGElement* outerSVG = it->get();
    136         if (!outerSVG->isOutermostSVGSVGElement())
    137             continue;
    138 
    139         // don't dispatch the load event document is not wellformed (for XML/standalone svg)
    140         if (outerSVG->document().wellFormed() || !outerSVG->document().isSVGDocument())
    141             outerSVG->sendSVGLoadEventIfPossible();
    142     }
    143 }
    144 
    145 static void reportMessage(Document* document, MessageLevel level, const String& message)
    146 {
    147     if (document->frame())
    148         document->addConsoleMessage(RenderingMessageSource, level, message);
    149 }
    150 
    151 void SVGDocumentExtensions::reportWarning(const String& message)
    152 {
    153     reportMessage(m_document, WarningMessageLevel, "Warning: " + message);
    154 }
    155 
    156 void SVGDocumentExtensions::reportError(const String& message)
    157 {
    158     reportMessage(m_document, ErrorMessageLevel, "Error: " + message);
    159 }
    160 
    161 void SVGDocumentExtensions::addPendingResource(const AtomicString& id, Element* element)
    162 {
    163     ASSERT(element);
    164     ASSERT(element->inDocument());
    165 
    166     if (id.isEmpty())
    167         return;
    168 
    169     HashMap<AtomicString, OwnPtr<SVGPendingElements> >::AddResult result = m_pendingResources.add(id, nullptr);
    170     if (result.isNewEntry)
    171         result.storedValue->value = adoptPtr(new SVGPendingElements);
    172     result.storedValue->value->add(element);
    173 
    174     element->setHasPendingResources();
    175 }
    176 
    177 bool SVGDocumentExtensions::hasPendingResource(const AtomicString& id) const
    178 {
    179     if (id.isEmpty())
    180         return false;
    181 
    182     return m_pendingResources.contains(id);
    183 }
    184 
    185 bool SVGDocumentExtensions::isElementPendingResources(Element* element) const
    186 {
    187     // This algorithm takes time proportional to the number of pending resources and need not.
    188     // If performance becomes an issue we can keep a counted set of elements and answer the question efficiently.
    189 
    190     ASSERT(element);
    191 
    192     HashMap<AtomicString, OwnPtr<SVGPendingElements> >::const_iterator end = m_pendingResources.end();
    193     for (HashMap<AtomicString, OwnPtr<SVGPendingElements> >::const_iterator it = m_pendingResources.begin(); it != end; ++it) {
    194         SVGPendingElements* elements = it->value.get();
    195         ASSERT(elements);
    196 
    197         if (elements->contains(element))
    198             return true;
    199     }
    200     return false;
    201 }
    202 
    203 bool SVGDocumentExtensions::isElementPendingResource(Element* element, const AtomicString& id) const
    204 {
    205     ASSERT(element);
    206 
    207     if (!hasPendingResource(id))
    208         return false;
    209 
    210     return m_pendingResources.get(id)->contains(element);
    211 }
    212 
    213 void SVGDocumentExtensions::clearHasPendingResourcesIfPossible(Element* element)
    214 {
    215     if (!isElementPendingResources(element))
    216         element->clearHasPendingResources();
    217 }
    218 
    219 void SVGDocumentExtensions::removeElementFromPendingResources(Element* element)
    220 {
    221     ASSERT(element);
    222 
    223     // Remove the element from pending resources.
    224     if (!m_pendingResources.isEmpty() && element->hasPendingResources()) {
    225         Vector<AtomicString> toBeRemoved;
    226         HashMap<AtomicString, OwnPtr<SVGPendingElements> >::iterator end = m_pendingResources.end();
    227         for (HashMap<AtomicString, OwnPtr<SVGPendingElements> >::iterator it = m_pendingResources.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         clearHasPendingResourcesIfPossible(element);
    238 
    239         // We use the removePendingResource function here because it deals with set lifetime correctly.
    240         Vector<AtomicString>::iterator vectorEnd = toBeRemoved.end();
    241         for (Vector<AtomicString>::iterator it = toBeRemoved.begin(); it != vectorEnd; ++it)
    242             removePendingResource(*it);
    243     }
    244 
    245     // Remove the element from pending resources that were scheduled for removal.
    246     if (!m_pendingResourcesForRemoval.isEmpty()) {
    247         Vector<AtomicString> toBeRemoved;
    248         HashMap<AtomicString, OwnPtr<SVGPendingElements> >::iterator end = m_pendingResourcesForRemoval.end();
    249         for (HashMap<AtomicString, OwnPtr<SVGPendingElements> >::iterator it = m_pendingResourcesForRemoval.begin(); it != end; ++it) {
    250             SVGPendingElements* elements = it->value.get();
    251             ASSERT(elements);
    252             ASSERT(!elements->isEmpty());
    253 
    254             elements->remove(element);
    255             if (elements->isEmpty())
    256                 toBeRemoved.append(it->key);
    257         }
    258 
    259         // We use the removePendingResourceForRemoval function here because it deals with set lifetime correctly.
    260         Vector<AtomicString>::iterator vectorEnd = toBeRemoved.end();
    261         for (Vector<AtomicString>::iterator it = toBeRemoved.begin(); it != vectorEnd; ++it)
    262             removePendingResourceForRemoval(*it);
    263     }
    264 }
    265 
    266 PassOwnPtr<SVGDocumentExtensions::SVGPendingElements> SVGDocumentExtensions::removePendingResource(const AtomicString& id)
    267 {
    268     ASSERT(m_pendingResources.contains(id));
    269     return m_pendingResources.take(id);
    270 }
    271 
    272 PassOwnPtr<SVGDocumentExtensions::SVGPendingElements> SVGDocumentExtensions::removePendingResourceForRemoval(const AtomicString& id)
    273 {
    274     ASSERT(m_pendingResourcesForRemoval.contains(id));
    275     return m_pendingResourcesForRemoval.take(id);
    276 }
    277 
    278 void SVGDocumentExtensions::markPendingResourcesForRemoval(const AtomicString& id)
    279 {
    280     if (id.isEmpty())
    281         return;
    282 
    283     ASSERT(!m_pendingResourcesForRemoval.contains(id));
    284 
    285     OwnPtr<SVGPendingElements> existing = m_pendingResources.take(id);
    286     if (existing && !existing->isEmpty())
    287         m_pendingResourcesForRemoval.add(id, existing.release());
    288 }
    289 
    290 Element* SVGDocumentExtensions::removeElementFromPendingResourcesForRemoval(const AtomicString& id)
    291 {
    292     if (id.isEmpty())
    293         return 0;
    294 
    295     SVGPendingElements* resourceSet = m_pendingResourcesForRemoval.get(id);
    296     if (!resourceSet || resourceSet->isEmpty())
    297         return 0;
    298 
    299     SVGPendingElements::iterator firstElement = resourceSet->begin();
    300     Element* element = *firstElement;
    301 
    302     resourceSet->remove(firstElement);
    303 
    304     if (resourceSet->isEmpty())
    305         removePendingResourceForRemoval(id);
    306 
    307     return element;
    308 }
    309 
    310 SVGElementSet* SVGDocumentExtensions::setOfElementsReferencingTarget(SVGElement* referencedElement) const
    311 {
    312     ASSERT(referencedElement);
    313     const ElementDependenciesMap::const_iterator it = m_elementDependencies.find(referencedElement);
    314     if (it == m_elementDependencies.end())
    315         return 0;
    316     return it->value.get();
    317 }
    318 
    319 void SVGDocumentExtensions::addElementReferencingTarget(SVGElement* referencingElement, SVGElement* referencedElement)
    320 {
    321     ASSERT(referencingElement);
    322     ASSERT(referencedElement);
    323 
    324     if (SVGElementSet* elements = m_elementDependencies.get(referencedElement)) {
    325         elements->add(referencingElement);
    326         return;
    327     }
    328 
    329     OwnPtrWillBeRawPtr<SVGElementSet> elements = adoptPtrWillBeNoop(new SVGElementSet);
    330     elements->add(referencingElement);
    331     m_elementDependencies.set(referencedElement, elements.release());
    332 }
    333 
    334 void SVGDocumentExtensions::removeAllTargetReferencesForElement(SVGElement* referencingElement)
    335 {
    336     WillBeHeapVector<RawPtrWillBeMember<SVGElement> > toBeRemoved;
    337 
    338     ElementDependenciesMap::iterator end = m_elementDependencies.end();
    339     for (ElementDependenciesMap::iterator it = m_elementDependencies.begin(); it != end; ++it) {
    340         SVGElement* referencedElement = it->key;
    341         SVGElementSet* referencingElements = it->value.get();
    342         SVGElementSet::iterator setIt = referencingElements->find(referencingElement);
    343         if (setIt == referencingElements->end())
    344             continue;
    345 
    346         referencingElements->remove(setIt);
    347         if (referencingElements->isEmpty())
    348             toBeRemoved.append(referencedElement);
    349     }
    350 
    351     m_elementDependencies.removeAll(toBeRemoved);
    352 }
    353 
    354 void SVGDocumentExtensions::rebuildAllElementReferencesForTarget(SVGElement* referencedElement)
    355 {
    356     ASSERT(referencedElement);
    357     ElementDependenciesMap::iterator it = m_elementDependencies.find(referencedElement);
    358     if (it == m_elementDependencies.end())
    359         return;
    360     ASSERT(it->key == referencedElement);
    361 
    362     WillBeHeapVector<RawPtrWillBeMember<SVGElement> > toBeNotified;
    363     SVGElementSet* referencingElements = it->value.get();
    364     copyToVector(*referencingElements, toBeNotified);
    365 
    366     // Force rebuilding the referencingElement so it knows about this change.
    367     WillBeHeapVector<RawPtrWillBeMember<SVGElement> >::iterator vectorEnd = toBeNotified.end();
    368     for (WillBeHeapVector<RawPtrWillBeMember<SVGElement> >::iterator vectorIt = toBeNotified.begin(); vectorIt != vectorEnd; ++vectorIt) {
    369         // Before rebuilding referencingElement ensure it was not removed from under us.
    370         if (SVGElementSet* referencingElements = setOfElementsReferencingTarget(referencedElement)) {
    371             if (referencingElements->contains(*vectorIt))
    372                 (*vectorIt)->svgAttributeChanged(XLinkNames::hrefAttr);
    373         }
    374     }
    375 }
    376 
    377 void SVGDocumentExtensions::removeAllElementReferencesForTarget(SVGElement* referencedElement)
    378 {
    379     ASSERT(referencedElement);
    380     ElementDependenciesMap::iterator it = m_elementDependencies.find(referencedElement);
    381     if (it == m_elementDependencies.end())
    382         return;
    383     ASSERT(it->key == referencedElement);
    384 
    385     m_elementDependencies.remove(it);
    386 }
    387 
    388 void SVGDocumentExtensions::addSVGRootWithRelativeLengthDescendents(SVGSVGElement* svgRoot)
    389 {
    390     ASSERT(!m_inRelativeLengthSVGRootsInvalidation);
    391     m_relativeLengthSVGRoots.add(svgRoot);
    392 }
    393 
    394 void SVGDocumentExtensions::removeSVGRootWithRelativeLengthDescendents(SVGSVGElement* svgRoot)
    395 {
    396     ASSERT(!m_inRelativeLengthSVGRootsInvalidation);
    397     m_relativeLengthSVGRoots.remove(svgRoot);
    398 }
    399 
    400 bool SVGDocumentExtensions::isSVGRootWithRelativeLengthDescendents(SVGSVGElement* svgRoot) const
    401 {
    402     return m_relativeLengthSVGRoots.contains(svgRoot);
    403 }
    404 
    405 void SVGDocumentExtensions::invalidateSVGRootsWithRelativeLengthDescendents(SubtreeLayoutScope* scope)
    406 {
    407     ASSERT(!m_inRelativeLengthSVGRootsInvalidation);
    408 #if ASSERT_ENABLED
    409     TemporaryChange<bool> inRelativeLengthSVGRootsChange(m_inRelativeLengthSVGRootsInvalidation, true);
    410 #endif
    411 
    412     WillBeHeapHashSet<RawPtrWillBeMember<SVGSVGElement> >::iterator end = m_relativeLengthSVGRoots.end();
    413     for (WillBeHeapHashSet<RawPtrWillBeMember<SVGSVGElement> >::iterator it = m_relativeLengthSVGRoots.begin(); it != end; ++it)
    414         (*it)->invalidateRelativeLengthClients(scope);
    415 }
    416 
    417 #if ENABLE(SVG_FONTS)
    418 void SVGDocumentExtensions::registerSVGFontFaceElement(SVGFontFaceElement* element)
    419 {
    420     m_svgFontFaceElements.add(element);
    421 }
    422 
    423 void SVGDocumentExtensions::unregisterSVGFontFaceElement(SVGFontFaceElement* element)
    424 {
    425     ASSERT(m_svgFontFaceElements.contains(element));
    426     m_svgFontFaceElements.remove(element);
    427 }
    428 
    429 void SVGDocumentExtensions::registerPendingSVGFontFaceElementsForRemoval(PassRefPtrWillBeRawPtr<SVGFontFaceElement> font)
    430 {
    431     m_pendingSVGFontFaceElementsForRemoval.add(font);
    432 }
    433 
    434 void SVGDocumentExtensions::removePendingSVGFontFaceElementsForRemoval()
    435 {
    436     m_pendingSVGFontFaceElementsForRemoval.clear();
    437 }
    438 
    439 #endif
    440 
    441 bool SVGDocumentExtensions::zoomAndPanEnabled() const
    442 {
    443     if (SVGSVGElement* svg = rootElement(*m_document)) {
    444         if (svg->useCurrentView()) {
    445             if (svg->currentView())
    446                 return svg->currentView()->zoomAndPan() == SVGZoomAndPanMagnify;
    447         } else {
    448             return svg->zoomAndPan() == SVGZoomAndPanMagnify;
    449         }
    450     }
    451 
    452     return false;
    453 }
    454 
    455 void SVGDocumentExtensions::startPan(const FloatPoint& start)
    456 {
    457     if (SVGSVGElement* svg = rootElement(*m_document))
    458         m_translate = FloatPoint(start.x() - svg->currentTranslate().x(), start.y() - svg->currentTranslate().y());
    459 }
    460 
    461 void SVGDocumentExtensions::updatePan(const FloatPoint& pos) const
    462 {
    463     if (SVGSVGElement* svg = rootElement(*m_document))
    464         svg->setCurrentTranslate(FloatPoint(pos.x() - m_translate.x(), pos.y() - m_translate.y()));
    465 }
    466 
    467 SVGSVGElement* SVGDocumentExtensions::rootElement(const Document& document)
    468 {
    469     Element* elem = document.documentElement();
    470     return isSVGSVGElement(elem) ? toSVGSVGElement(elem) : 0;
    471 }
    472 
    473 SVGSVGElement* SVGDocumentExtensions::rootElement() const
    474 {
    475     ASSERT(m_document);
    476     return rootElement(*m_document);
    477 }
    478 
    479 void SVGDocumentExtensions::trace(Visitor* visitor)
    480 {
    481     visitor->trace(m_document);
    482     visitor->trace(m_timeContainers);
    483 #if ENABLE(SVG_FONTS)
    484     visitor->trace(m_svgFontFaceElements);
    485     visitor->trace(m_pendingSVGFontFaceElementsForRemoval);
    486 #endif
    487     visitor->trace(m_elementDependencies);
    488     visitor->trace(m_relativeLengthSVGRoots);
    489 }
    490 
    491 }
    492