| /* |
| * Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org> |
| * Copyright (C) 2004, 2005, 2006, 2008 Rob Buis <buis@kde.org> |
| * Copyright (C) 2008 Apple Inc. All rights reserved. |
| * Copyright (C) 2008 Alp Toker <alp@atoker.com> |
| * Copyright (C) 2009 Cameron McCormack <cam@mcc.id.au> |
| * |
| * 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. |
| */ |
| |
| #include "config.h" |
| |
| #include "core/svg/SVGElement.h" |
| |
| #include "bindings/core/v8/ScriptEventListener.h" |
| #include "core/HTMLNames.h" |
| #include "core/SVGNames.h" |
| #include "core/XLinkNames.h" |
| #include "core/XMLNames.h" |
| #include "core/css/CSSCursorImageValue.h" |
| #include "core/css/parser/BisonCSSParser.h" |
| #include "core/css/resolver/StyleResolver.h" |
| #include "core/dom/Document.h" |
| #include "core/dom/ElementTraversal.h" |
| #include "core/dom/shadow/ShadowRoot.h" |
| #include "core/events/Event.h" |
| #include "core/frame/Settings.h" |
| #include "core/html/HTMLElement.h" |
| #include "core/rendering/RenderObject.h" |
| #include "core/rendering/svg/RenderSVGResourceContainer.h" |
| #include "core/svg/SVGCursorElement.h" |
| #include "core/svg/SVGDocumentExtensions.h" |
| #include "core/svg/SVGElementRareData.h" |
| #include "core/svg/SVGGraphicsElement.h" |
| #include "core/svg/SVGSVGElement.h" |
| #include "core/svg/SVGTitleElement.h" |
| #include "core/svg/SVGUseElement.h" |
| |
| #include "wtf/TemporaryChange.h" |
| |
| namespace blink { |
| |
| using namespace HTMLNames; |
| using namespace SVGNames; |
| |
| SVGElement::SVGElement(const QualifiedName& tagName, Document& document, ConstructionType constructionType) |
| : Element(tagName, &document, constructionType) |
| #if ENABLE(ASSERT) |
| , m_inRelativeLengthClientsInvalidation(false) |
| #endif |
| // |m_isContextElement| must be initialized before |m_className|, as SVGAnimatedString tear-off c-tor currently set this to true. |
| , m_isContextElement(false) |
| , m_SVGRareData(nullptr) |
| , m_className(SVGAnimatedString::create(this, HTMLNames::classAttr, SVGString::create())) |
| { |
| ScriptWrappable::init(this); |
| addToPropertyMap(m_className); |
| setHasCustomStyleCallbacks(); |
| } |
| |
| SVGElement::~SVGElement() |
| { |
| ASSERT(inDocument() || !hasRelativeLengths()); |
| |
| // The below teardown is all handled by weak pointer processing in oilpan. |
| #if !ENABLE(OILPAN) |
| if (hasSVGRareData()) { |
| if (SVGCursorElement* cursorElement = svgRareData()->cursorElement()) |
| cursorElement->removeReferencedElement(this); |
| if (CSSCursorImageValue* cursorImageValue = svgRareData()->cursorImageValue()) |
| cursorImageValue->removeReferencedElement(this); |
| |
| // With Oilpan, either removedFrom has been called or the document is dead |
| // as well and there is no reason to clear out the references. |
| rebuildAllIncomingReferences(); |
| removeAllIncomingReferences(); |
| } |
| #endif |
| } |
| |
| void SVGElement::detach(const AttachContext& context) |
| { |
| Element::detach(context); |
| if (SVGElement* element = correspondingElement()) |
| element->removeInstanceMapping(this); |
| } |
| |
| void SVGElement::attach(const AttachContext& context) |
| { |
| Element::attach(context); |
| if (SVGElement* element = correspondingElement()) |
| element->mapInstanceToElement(this); |
| } |
| |
| short SVGElement::tabIndex() const |
| { |
| if (supportsFocus()) |
| return Element::tabIndex(); |
| return -1; |
| } |
| |
| void SVGElement::willRecalcStyle(StyleRecalcChange change) |
| { |
| if (!hasSVGRareData()) |
| return; |
| // If the style changes because of a regular property change (not induced by SMIL animations themselves) |
| // reset the "computed style without SMIL style properties", so the base value change gets reflected. |
| if (change > NoChange || needsStyleRecalc()) |
| svgRareData()->setNeedsOverrideComputedStyleUpdate(); |
| } |
| |
| void SVGElement::buildPendingResourcesIfNeeded() |
| { |
| Document& document = this->document(); |
| if (!needsPendingResourceHandling() || !inDocument() || inUseShadowTree()) |
| return; |
| |
| SVGDocumentExtensions& extensions = document.accessSVGExtensions(); |
| AtomicString resourceId = getIdAttribute(); |
| if (!extensions.hasPendingResource(resourceId)) |
| return; |
| |
| // Mark pending resources as pending for removal. |
| extensions.markPendingResourcesForRemoval(resourceId); |
| |
| // Rebuild pending resources for each client of a pending resource that is being removed. |
| while (Element* clientElement = extensions.removeElementFromPendingResourcesForRemoval(resourceId)) { |
| ASSERT(clientElement->hasPendingResources()); |
| if (clientElement->hasPendingResources()) { |
| // FIXME: Ideally we'd always resolve pending resources async instead of inside |
| // insertedInto and svgAttributeChanged. For now we only do it for <use> since |
| // that would stamp out DOM. |
| if (isSVGUseElement(clientElement)) |
| toSVGUseElement(clientElement)->invalidateShadowTree(); |
| else |
| clientElement->buildPendingResource(); |
| extensions.clearHasPendingResourcesIfPossible(clientElement); |
| } |
| } |
| } |
| |
| SVGElementRareData* SVGElement::ensureSVGRareData() |
| { |
| if (hasSVGRareData()) |
| return svgRareData(); |
| |
| m_SVGRareData = adoptPtrWillBeNoop(new SVGElementRareData(this)); |
| return m_SVGRareData.get(); |
| } |
| |
| bool SVGElement::isOutermostSVGSVGElement() const |
| { |
| if (!isSVGSVGElement(*this)) |
| return false; |
| |
| // Element may not be in the document, pretend we're outermost for viewport(), getCTM(), etc. |
| if (!parentNode()) |
| return true; |
| |
| // We act like an outermost SVG element, if we're a direct child of a <foreignObject> element. |
| if (isSVGForeignObjectElement(*parentNode())) |
| return true; |
| |
| // If we're living in a shadow tree, we're a <svg> element that got created as replacement |
| // for a <symbol> element or a cloned <svg> element in the referenced tree. In that case |
| // we're always an inner <svg> element. |
| if (inUseShadowTree() && parentOrShadowHostElement() && parentOrShadowHostElement()->isSVGElement()) |
| return false; |
| |
| // This is true whenever this is the outermost SVG, even if there are HTML elements outside it |
| return !parentNode()->isSVGElement(); |
| } |
| |
| void SVGElement::reportAttributeParsingError(SVGParsingError error, const QualifiedName& name, const AtomicString& value) |
| { |
| if (error == NoError) |
| return; |
| |
| String errorString = "<" + tagName() + "> attribute " + name.toString() + "=\"" + value + "\""; |
| SVGDocumentExtensions& extensions = document().accessSVGExtensions(); |
| |
| if (error == NegativeValueForbiddenError) { |
| extensions.reportError("Invalid negative value for " + errorString); |
| return; |
| } |
| |
| if (error == ParsingAttributeFailedError) { |
| extensions.reportError("Invalid value for " + errorString); |
| return; |
| } |
| |
| ASSERT_NOT_REACHED(); |
| } |
| |
| String SVGElement::title() const |
| { |
| // According to spec, we should not return titles when hovering over root <svg> elements (those |
| // <title> elements are the title of the document, not a tooltip) so we instantly return. |
| if (isOutermostSVGSVGElement()) |
| return String(); |
| |
| if (inUseShadowTree()) { |
| String useTitle(shadowHost()->title()); |
| if (!useTitle.isEmpty()) |
| return useTitle; |
| } |
| |
| // If we aren't an instance in a <use> or the <use> title was not found, then find the first |
| // <title> child of this element. |
| // If a title child was found, return the text contents. |
| if (Element* titleElement = Traversal<SVGTitleElement>::firstChild(*this)) |
| return titleElement->innerText(); |
| |
| // Otherwise return a null/empty string. |
| return String(); |
| } |
| |
| bool SVGElement::instanceUpdatesBlocked() const |
| { |
| return hasSVGRareData() && svgRareData()->instanceUpdatesBlocked(); |
| } |
| |
| void SVGElement::setInstanceUpdatesBlocked(bool value) |
| { |
| if (hasSVGRareData()) |
| svgRareData()->setInstanceUpdatesBlocked(value); |
| } |
| |
| AffineTransform SVGElement::localCoordinateSpaceTransform(CTMScope) const |
| { |
| // To be overriden by SVGGraphicsElement (or as special case SVGTextElement and SVGPatternElement) |
| return AffineTransform(); |
| } |
| |
| Node::InsertionNotificationRequest SVGElement::insertedInto(ContainerNode* rootParent) |
| { |
| Element::insertedInto(rootParent); |
| updateRelativeLengthsInformation(); |
| buildPendingResourcesIfNeeded(); |
| return InsertionDone; |
| } |
| |
| void SVGElement::removedFrom(ContainerNode* rootParent) |
| { |
| bool wasInDocument = rootParent->inDocument(); |
| |
| if (wasInDocument && hasRelativeLengths()) { |
| // The root of the subtree being removed should take itself out from its parent's relative |
| // length set. For the other nodes in the subtree we don't need to do anything: they will |
| // get their own removedFrom() notification and just clear their sets. |
| if (rootParent->isSVGElement() && !parentNode()) { |
| ASSERT(toSVGElement(rootParent)->m_elementsWithRelativeLengths.contains(this)); |
| toSVGElement(rootParent)->updateRelativeLengthsInformation(false, this); |
| } |
| |
| m_elementsWithRelativeLengths.clear(); |
| } |
| |
| ASSERT_WITH_SECURITY_IMPLICATION(!rootParent->isSVGElement() || !toSVGElement(rootParent)->m_elementsWithRelativeLengths.contains(this)); |
| |
| Element::removedFrom(rootParent); |
| |
| if (wasInDocument) { |
| rebuildAllIncomingReferences(); |
| removeAllIncomingReferences(); |
| } |
| |
| invalidateInstances(); |
| } |
| |
| void SVGElement::childrenChanged(const ChildrenChange& change) |
| { |
| Element::childrenChanged(change); |
| |
| // Invalidate all instances associated with us. |
| if (!change.byParser) |
| invalidateInstances(); |
| } |
| |
| void mapAttributeToCSSProperty(HashMap<StringImpl*, CSSPropertyID>* propertyNameToIdMap, const QualifiedName& attrName) |
| { |
| CSSPropertyID propertyId = cssPropertyID(attrName.localName()); |
| ASSERT(propertyId > 0); |
| propertyNameToIdMap->set(attrName.localName().impl(), propertyId); |
| } |
| |
| CSSPropertyID SVGElement::cssPropertyIdForSVGAttributeName(const QualifiedName& attrName) |
| { |
| if (!attrName.namespaceURI().isNull()) |
| return CSSPropertyInvalid; |
| |
| static HashMap<StringImpl*, CSSPropertyID>* propertyNameToIdMap = 0; |
| if (!propertyNameToIdMap) { |
| propertyNameToIdMap = new HashMap<StringImpl*, CSSPropertyID>; |
| // This is a list of all base CSS and SVG CSS properties which are exposed as SVG XML attributes |
| mapAttributeToCSSProperty(propertyNameToIdMap, alignment_baselineAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, baseline_shiftAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, buffered_renderingAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, clipAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, clip_pathAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, clip_ruleAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, SVGNames::colorAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, color_interpolationAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, color_interpolation_filtersAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, color_renderingAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, cursorAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, SVGNames::directionAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, displayAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, dominant_baselineAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, enable_backgroundAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, fillAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, fill_opacityAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, fill_ruleAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, filterAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, flood_colorAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, flood_opacityAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, font_familyAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, font_sizeAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, font_stretchAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, font_styleAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, font_variantAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, font_weightAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, glyph_orientation_horizontalAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, glyph_orientation_verticalAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, image_renderingAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, letter_spacingAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, lighting_colorAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, marker_endAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, marker_midAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, marker_startAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, maskAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, mask_typeAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, opacityAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, overflowAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, paint_orderAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, pointer_eventsAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, shape_renderingAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, stop_colorAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, stop_opacityAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, strokeAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, stroke_dasharrayAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, stroke_dashoffsetAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, stroke_linecapAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, stroke_linejoinAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, stroke_miterlimitAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, stroke_opacityAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, stroke_widthAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, text_anchorAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, text_decorationAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, text_renderingAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, transform_originAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, unicode_bidiAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, vector_effectAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, visibilityAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, word_spacingAttr); |
| mapAttributeToCSSProperty(propertyNameToIdMap, writing_modeAttr); |
| } |
| |
| return propertyNameToIdMap->get(attrName.localName().impl()); |
| } |
| |
| void SVGElement::updateRelativeLengthsInformation(bool clientHasRelativeLengths, SVGElement* clientElement) |
| { |
| ASSERT(clientElement); |
| |
| // If we're not yet in a document, this function will be called again from insertedInto(). Do nothing now. |
| if (!inDocument()) |
| return; |
| |
| // An element wants to notify us that its own relative lengths state changed. |
| // Register it in the relative length map, and register us in the parent relative length map. |
| // Register the parent in the grandparents map, etc. Repeat procedure until the root of the SVG tree. |
| for (ContainerNode* currentNode = this; currentNode && currentNode->isSVGElement(); currentNode = currentNode->parentNode()) { |
| SVGElement* currentElement = toSVGElement(currentNode); |
| ASSERT(!currentElement->m_inRelativeLengthClientsInvalidation); |
| |
| bool hadRelativeLengths = currentElement->hasRelativeLengths(); |
| if (clientHasRelativeLengths) |
| currentElement->m_elementsWithRelativeLengths.add(clientElement); |
| else |
| currentElement->m_elementsWithRelativeLengths.remove(clientElement); |
| |
| // If the relative length state hasn't changed, we can stop propagating the notification. |
| if (hadRelativeLengths == currentElement->hasRelativeLengths()) |
| return; |
| |
| clientElement = currentElement; |
| clientHasRelativeLengths = clientElement->hasRelativeLengths(); |
| } |
| |
| // Register root SVG elements for top level viewport change notifications. |
| if (isSVGSVGElement(*clientElement)) { |
| SVGDocumentExtensions& svgExtensions = accessDocumentSVGExtensions(); |
| if (clientElement->hasRelativeLengths()) |
| svgExtensions.addSVGRootWithRelativeLengthDescendents(toSVGSVGElement(clientElement)); |
| else |
| svgExtensions.removeSVGRootWithRelativeLengthDescendents(toSVGSVGElement(clientElement)); |
| } |
| } |
| |
| void SVGElement::invalidateRelativeLengthClients(SubtreeLayoutScope* layoutScope) |
| { |
| if (!inDocument()) |
| return; |
| |
| ASSERT(!m_inRelativeLengthClientsInvalidation); |
| #if ENABLE(ASSERT) |
| TemporaryChange<bool> inRelativeLengthClientsInvalidationChange(m_inRelativeLengthClientsInvalidation, true); |
| #endif |
| |
| RenderObject* renderer = this->renderer(); |
| if (renderer && selfHasRelativeLengths()) { |
| if (renderer->isSVGResourceContainer()) |
| toRenderSVGResourceContainer(renderer)->invalidateCacheAndMarkForLayout(layoutScope); |
| else |
| renderer->setNeedsLayoutAndFullPaintInvalidation(MarkContainingBlockChain, layoutScope); |
| } |
| |
| WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::iterator end = m_elementsWithRelativeLengths.end(); |
| for (WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::iterator it = m_elementsWithRelativeLengths.begin(); it != end; ++it) { |
| if (*it != this) |
| (*it)->invalidateRelativeLengthClients(layoutScope); |
| } |
| } |
| |
| SVGSVGElement* SVGElement::ownerSVGElement() const |
| { |
| ContainerNode* n = parentOrShadowHostNode(); |
| while (n) { |
| if (isSVGSVGElement(*n)) |
| return toSVGSVGElement(n); |
| |
| n = n->parentOrShadowHostNode(); |
| } |
| |
| return 0; |
| } |
| |
| SVGElement* SVGElement::viewportElement() const |
| { |
| // This function needs shadow tree support - as RenderSVGContainer uses this function |
| // to determine the "overflow" property. <use> on <symbol> wouldn't work otherwhise. |
| ContainerNode* n = parentOrShadowHostNode(); |
| while (n) { |
| if (isSVGSVGElement(*n) || isSVGImageElement(*n) || isSVGSymbolElement(*n)) |
| return toSVGElement(n); |
| |
| n = n->parentOrShadowHostNode(); |
| } |
| |
| return 0; |
| } |
| |
| SVGDocumentExtensions& SVGElement::accessDocumentSVGExtensions() |
| { |
| // This function is provided for use by SVGAnimatedProperty to avoid |
| // global inclusion of core/dom/Document.h in SVG code. |
| return document().accessSVGExtensions(); |
| } |
| |
| void SVGElement::mapInstanceToElement(SVGElement* instance) |
| { |
| ASSERT(instance); |
| ASSERT(instance->inUseShadowTree()); |
| |
| WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& instances = ensureSVGRareData()->elementInstances(); |
| ASSERT(!instances.contains(instance)); |
| |
| instances.add(instance); |
| } |
| |
| void SVGElement::removeInstanceMapping(SVGElement* instance) |
| { |
| ASSERT(instance); |
| ASSERT(instance->inUseShadowTree()); |
| |
| if (!hasSVGRareData()) |
| return; |
| |
| WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& instances = svgRareData()->elementInstances(); |
| |
| instances.remove(instance); |
| } |
| |
| static WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& emptyInstances() |
| { |
| DEFINE_STATIC_LOCAL(OwnPtrWillBePersistent<WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> > >, emptyInstances, (adoptPtrWillBeNoop(new WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >()))); |
| return *emptyInstances; |
| } |
| |
| const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& SVGElement::instancesForElement() const |
| { |
| if (!hasSVGRareData()) |
| return emptyInstances(); |
| return svgRareData()->elementInstances(); |
| } |
| |
| bool SVGElement::getBoundingBox(FloatRect& rect) |
| { |
| if (!isSVGGraphicsElement()) |
| return false; |
| |
| rect = toSVGGraphicsElement(this)->getBBox(); |
| return true; |
| } |
| |
| void SVGElement::setCursorElement(SVGCursorElement* cursorElement) |
| { |
| SVGElementRareData* rareData = ensureSVGRareData(); |
| if (SVGCursorElement* oldCursorElement = rareData->cursorElement()) { |
| if (cursorElement == oldCursorElement) |
| return; |
| oldCursorElement->removeReferencedElement(this); |
| } |
| rareData->setCursorElement(cursorElement); |
| } |
| |
| #if !ENABLE(OILPAN) |
| void SVGElement::cursorElementRemoved() |
| { |
| svgRareData()->setCursorElement(0); |
| } |
| #endif |
| |
| void SVGElement::setCursorImageValue(CSSCursorImageValue* cursorImageValue) |
| { |
| SVGElementRareData* rareData = ensureSVGRareData(); |
| #if !ENABLE(OILPAN) |
| if (CSSCursorImageValue* oldCursorImageValue = rareData->cursorImageValue()) { |
| if (cursorImageValue == oldCursorImageValue) |
| return; |
| oldCursorImageValue->removeReferencedElement(this); |
| } |
| #endif |
| rareData->setCursorImageValue(cursorImageValue); |
| } |
| |
| #if !ENABLE(OILPAN) |
| void SVGElement::cursorImageValueRemoved() |
| { |
| svgRareData()->setCursorImageValue(0); |
| } |
| #endif |
| |
| SVGElement* SVGElement::correspondingElement() |
| { |
| ASSERT(!hasSVGRareData() || !svgRareData()->correspondingElement() || containingShadowRoot()); |
| return hasSVGRareData() ? svgRareData()->correspondingElement() : 0; |
| } |
| |
| SVGUseElement* SVGElement::correspondingUseElement() const |
| { |
| if (ShadowRoot* root = containingShadowRoot()) { |
| if (isSVGUseElement(root->host()) && (root->type() == ShadowRoot::UserAgentShadowRoot)) |
| return toSVGUseElement(root->host()); |
| } |
| return 0; |
| } |
| |
| void SVGElement::setCorrespondingElement(SVGElement* correspondingElement) |
| { |
| ensureSVGRareData()->setCorrespondingElement(correspondingElement); |
| } |
| |
| bool SVGElement::inUseShadowTree() const |
| { |
| return correspondingUseElement(); |
| } |
| |
| void SVGElement::parseAttribute(const QualifiedName& name, const AtomicString& value) |
| { |
| if (name == HTMLNames::classAttr) { |
| // SVG animation has currently requires special storage of values so we set |
| // the className here. svgAttributeChanged actually causes the resulting |
| // style updates (instead of Element::parseAttribute). We don't |
| // tell Element about the change to avoid parsing the class list twice |
| SVGParsingError parseError = NoError; |
| m_className->setBaseValueAsString(value, parseError); |
| reportAttributeParsingError(parseError, name, value); |
| } else if (name.matches(XMLNames::langAttr) || name.matches(XMLNames::spaceAttr)) { |
| } else if (name == tabindexAttr) { |
| Element::parseAttribute(name, value); |
| } else { |
| // standard events |
| const AtomicString& eventName = HTMLElement::eventNameForAttributeName(name); |
| if (!eventName.isNull()) |
| setAttributeEventListener(eventName, createAttributeEventListener(this, name, value, eventParameterName())); |
| else |
| Element::parseAttribute(name, value); |
| } |
| } |
| |
| void SVGElement::parseAttributeNew(const QualifiedName& name, const AtomicString& value) |
| { |
| RefPtr<SVGAnimatedPropertyBase> property = propertyFromAttribute(name); |
| if (property) { |
| SVGParsingError parseError = NoError; |
| property->setBaseValueAsString(value, parseError); |
| reportAttributeParsingError(parseError, name, value); |
| |
| return; |
| } |
| |
| SVGElement::parseAttribute(name, value); |
| } |
| |
| typedef HashMap<QualifiedName, AnimatedPropertyType> AttributeToPropertyTypeMap; |
| AnimatedPropertyType SVGElement::animatedPropertyTypeForCSSAttribute(const QualifiedName& attributeName) |
| { |
| DEFINE_STATIC_LOCAL(AttributeToPropertyTypeMap, cssPropertyMap, ()); |
| |
| if (cssPropertyMap.isEmpty()) { |
| // Fill the map for the first use. |
| cssPropertyMap.set(alignment_baselineAttr, AnimatedString); |
| cssPropertyMap.set(baseline_shiftAttr, AnimatedString); |
| cssPropertyMap.set(buffered_renderingAttr, AnimatedString); |
| cssPropertyMap.set(clipAttr, AnimatedRect); |
| cssPropertyMap.set(clip_pathAttr, AnimatedString); |
| cssPropertyMap.set(clip_ruleAttr, AnimatedString); |
| cssPropertyMap.set(SVGNames::colorAttr, AnimatedColor); |
| cssPropertyMap.set(color_interpolationAttr, AnimatedString); |
| cssPropertyMap.set(color_interpolation_filtersAttr, AnimatedString); |
| cssPropertyMap.set(color_renderingAttr, AnimatedString); |
| cssPropertyMap.set(cursorAttr, AnimatedString); |
| cssPropertyMap.set(displayAttr, AnimatedString); |
| cssPropertyMap.set(dominant_baselineAttr, AnimatedString); |
| cssPropertyMap.set(fillAttr, AnimatedColor); |
| cssPropertyMap.set(fill_opacityAttr, AnimatedNumber); |
| cssPropertyMap.set(fill_ruleAttr, AnimatedString); |
| cssPropertyMap.set(filterAttr, AnimatedString); |
| cssPropertyMap.set(flood_colorAttr, AnimatedColor); |
| cssPropertyMap.set(flood_opacityAttr, AnimatedNumber); |
| cssPropertyMap.set(font_familyAttr, AnimatedString); |
| cssPropertyMap.set(font_sizeAttr, AnimatedLength); |
| cssPropertyMap.set(font_stretchAttr, AnimatedString); |
| cssPropertyMap.set(font_styleAttr, AnimatedString); |
| cssPropertyMap.set(font_variantAttr, AnimatedString); |
| cssPropertyMap.set(font_weightAttr, AnimatedString); |
| cssPropertyMap.set(image_renderingAttr, AnimatedString); |
| cssPropertyMap.set(letter_spacingAttr, AnimatedLength); |
| cssPropertyMap.set(lighting_colorAttr, AnimatedColor); |
| cssPropertyMap.set(marker_endAttr, AnimatedString); |
| cssPropertyMap.set(marker_midAttr, AnimatedString); |
| cssPropertyMap.set(marker_startAttr, AnimatedString); |
| cssPropertyMap.set(maskAttr, AnimatedString); |
| cssPropertyMap.set(mask_typeAttr, AnimatedString); |
| cssPropertyMap.set(opacityAttr, AnimatedNumber); |
| cssPropertyMap.set(overflowAttr, AnimatedString); |
| cssPropertyMap.set(paint_orderAttr, AnimatedString); |
| cssPropertyMap.set(pointer_eventsAttr, AnimatedString); |
| cssPropertyMap.set(shape_renderingAttr, AnimatedString); |
| cssPropertyMap.set(stop_colorAttr, AnimatedColor); |
| cssPropertyMap.set(stop_opacityAttr, AnimatedNumber); |
| cssPropertyMap.set(strokeAttr, AnimatedColor); |
| cssPropertyMap.set(stroke_dasharrayAttr, AnimatedLengthList); |
| cssPropertyMap.set(stroke_dashoffsetAttr, AnimatedLength); |
| cssPropertyMap.set(stroke_linecapAttr, AnimatedString); |
| cssPropertyMap.set(stroke_linejoinAttr, AnimatedString); |
| cssPropertyMap.set(stroke_miterlimitAttr, AnimatedNumber); |
| cssPropertyMap.set(stroke_opacityAttr, AnimatedNumber); |
| cssPropertyMap.set(stroke_widthAttr, AnimatedLength); |
| cssPropertyMap.set(text_anchorAttr, AnimatedString); |
| cssPropertyMap.set(text_decorationAttr, AnimatedString); |
| cssPropertyMap.set(text_renderingAttr, AnimatedString); |
| cssPropertyMap.set(vector_effectAttr, AnimatedString); |
| cssPropertyMap.set(visibilityAttr, AnimatedString); |
| cssPropertyMap.set(word_spacingAttr, AnimatedLength); |
| } |
| |
| if (cssPropertyMap.contains(attributeName)) |
| return cssPropertyMap.get(attributeName); |
| |
| return AnimatedUnknown; |
| } |
| |
| void SVGElement::addToPropertyMap(PassRefPtr<SVGAnimatedPropertyBase> passProperty) |
| { |
| RefPtr<SVGAnimatedPropertyBase> property(passProperty); |
| QualifiedName attributeName = property->attributeName(); |
| m_attributeToPropertyMap.set(attributeName, property.release()); |
| } |
| |
| PassRefPtr<SVGAnimatedPropertyBase> SVGElement::propertyFromAttribute(const QualifiedName& attributeName) |
| { |
| AttributeToPropertyMap::iterator it = m_attributeToPropertyMap.find<SVGAttributeHashTranslator>(attributeName); |
| if (it == m_attributeToPropertyMap.end()) |
| return nullptr; |
| |
| return it->value; |
| } |
| |
| bool SVGElement::isAnimatableCSSProperty(const QualifiedName& attrName) |
| { |
| return animatedPropertyTypeForCSSAttribute(attrName) != AnimatedUnknown; |
| } |
| |
| bool SVGElement::isPresentationAttribute(const QualifiedName& name) const |
| { |
| return cssPropertyIdForSVGAttributeName(name) > 0; |
| } |
| |
| void SVGElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style) |
| { |
| CSSPropertyID propertyID = cssPropertyIdForSVGAttributeName(name); |
| if (propertyID > 0) |
| addPropertyToPresentationAttributeStyle(style, propertyID, value); |
| } |
| |
| bool SVGElement::haveLoadedRequiredResources() |
| { |
| for (SVGElement* child = Traversal<SVGElement>::firstChild(*this); child; child = Traversal<SVGElement>::nextSibling(*child)) { |
| if (!child->haveLoadedRequiredResources()) |
| return false; |
| } |
| return true; |
| } |
| |
| static inline void collectInstancesForSVGElement(SVGElement* element, WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& instances) |
| { |
| ASSERT(element); |
| if (element->containingShadowRoot()) |
| return; |
| |
| ASSERT(!element->instanceUpdatesBlocked()); |
| |
| instances = element->instancesForElement(); |
| } |
| |
| bool SVGElement::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> prpListener, bool useCapture) |
| { |
| RefPtr<EventListener> listener = prpListener; |
| |
| // Add event listener to regular DOM element |
| if (!Node::addEventListener(eventType, listener, useCapture)) |
| return false; |
| |
| // Add event listener to all shadow tree DOM element instances |
| WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> > instances; |
| collectInstancesForSVGElement(this, instances); |
| const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator end = instances.end(); |
| for (WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator it = instances.begin(); it != end; ++it) { |
| bool result = (*it)->Node::addEventListener(eventType, listener, useCapture); |
| ASSERT_UNUSED(result, result); |
| } |
| |
| return true; |
| } |
| |
| bool SVGElement::removeEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture) |
| { |
| WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> > instances; |
| collectInstancesForSVGElement(this, instances); |
| if (instances.isEmpty()) |
| return Node::removeEventListener(eventType, listener.get(), useCapture); |
| |
| // EventTarget::removeEventListener creates a PassRefPtr around the given EventListener |
| // object when creating a temporary RegisteredEventListener object used to look up the |
| // event listener in a cache. If we want to be able to call removeEventListener() multiple |
| // times on different nodes, we have to delay its immediate destruction, which would happen |
| // after the first call below. |
| // XXX is that true? |
| RefPtr<EventListener> protector(listener); |
| |
| // Remove event listener from regular DOM element |
| if (!Node::removeEventListener(eventType, listener.get(), useCapture)) |
| return false; |
| |
| // Remove event listener from all shadow tree DOM element instances |
| const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator end = instances.end(); |
| for (WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator it = instances.begin(); it != end; ++it) { |
| SVGElement* shadowTreeElement = *it; |
| ASSERT(shadowTreeElement); |
| |
| shadowTreeElement->Node::removeEventListener(eventType, listener.get(), useCapture); |
| } |
| |
| return true; |
| } |
| |
| static bool hasLoadListener(Element* element) |
| { |
| if (element->hasEventListeners(EventTypeNames::load)) |
| return true; |
| |
| for (element = element->parentOrShadowHostElement(); element; element = element->parentOrShadowHostElement()) { |
| const EventListenerVector& entry = element->getEventListeners(EventTypeNames::load); |
| for (size_t i = 0; i < entry.size(); ++i) { |
| if (entry[i].useCapture) |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool SVGElement::sendSVGLoadEventIfPossible() |
| { |
| if (!haveLoadedRequiredResources()) |
| return false; |
| if ((isStructurallyExternal() || isSVGSVGElement(*this)) && hasLoadListener(this)) |
| dispatchEvent(Event::create(EventTypeNames::load)); |
| return true; |
| } |
| |
| void SVGElement::sendSVGLoadEventToSelfAndAncestorChainIfPossible() |
| { |
| // Let Document::implicitClose() dispatch the 'load' to the outermost SVG root. |
| if (isOutermostSVGSVGElement()) |
| return; |
| |
| // Save the next parent to dispatch to in case dispatching the event mutates the tree. |
| RefPtrWillBeRawPtr<Element> parent = parentOrShadowHostElement(); |
| if (!sendSVGLoadEventIfPossible()) |
| return; |
| |
| // If document/window 'load' has been sent already, then only deliver to |
| // the element in question. |
| if (document().loadEventFinished()) |
| return; |
| |
| if (!parent || !parent->isSVGElement()) |
| return; |
| |
| toSVGElement(parent)->sendSVGLoadEventToSelfAndAncestorChainIfPossible(); |
| } |
| |
| void SVGElement::sendSVGLoadEventIfPossibleAsynchronously() |
| { |
| svgLoadEventTimer()->startOneShot(0, FROM_HERE); |
| } |
| |
| void SVGElement::svgLoadEventTimerFired(Timer<SVGElement>*) |
| { |
| sendSVGLoadEventIfPossible(); |
| } |
| |
| Timer<SVGElement>* SVGElement::svgLoadEventTimer() |
| { |
| ASSERT_NOT_REACHED(); |
| return 0; |
| } |
| |
| void SVGElement::attributeChanged(const QualifiedName& name, const AtomicString& newValue, AttributeModificationReason) |
| { |
| Element::attributeChanged(name, newValue); |
| |
| if (isIdAttributeName(name)) |
| rebuildAllIncomingReferences(); |
| |
| // Changes to the style attribute are processed lazily (see Element::getAttribute() and related methods), |
| // so we don't want changes to the style attribute to result in extra work here. |
| if (name != HTMLNames::styleAttr) |
| svgAttributeChanged(name); |
| } |
| |
| void SVGElement::svgAttributeChanged(const QualifiedName& attrName) |
| { |
| CSSPropertyID propId = SVGElement::cssPropertyIdForSVGAttributeName(attrName); |
| if (propId > 0) { |
| invalidateInstances(); |
| return; |
| } |
| |
| if (attrName == HTMLNames::classAttr) { |
| classAttributeChanged(AtomicString(m_className->currentValue()->value())); |
| invalidateInstances(); |
| return; |
| } |
| |
| if (isIdAttributeName(attrName)) { |
| RenderObject* object = renderer(); |
| // Notify resources about id changes, this is important as we cache resources by id in SVGDocumentExtensions |
| if (object && object->isSVGResourceContainer()) |
| toRenderSVGResourceContainer(object)->idChanged(); |
| if (inDocument()) |
| buildPendingResourcesIfNeeded(); |
| invalidateInstances(); |
| return; |
| } |
| } |
| |
| void SVGElement::synchronizeAnimatedSVGAttribute(const QualifiedName& name) const |
| { |
| if (!elementData() || !elementData()->m_animatedSVGAttributesAreDirty) |
| return; |
| |
| if (name == anyQName()) { |
| AttributeToPropertyMap::const_iterator::Values it = m_attributeToPropertyMap.values().begin(); |
| AttributeToPropertyMap::const_iterator::Values end = m_attributeToPropertyMap.values().end(); |
| for (; it != end; ++it) { |
| if ((*it)->needsSynchronizeAttribute()) |
| (*it)->synchronizeAttribute(); |
| } |
| |
| elementData()->m_animatedSVGAttributesAreDirty = false; |
| } else { |
| RefPtr<SVGAnimatedPropertyBase> property = m_attributeToPropertyMap.get(name); |
| if (property && property->needsSynchronizeAttribute()) |
| property->synchronizeAttribute(); |
| } |
| } |
| |
| PassRefPtr<RenderStyle> SVGElement::customStyleForRenderer() |
| { |
| if (!correspondingElement()) |
| return document().ensureStyleResolver().styleForElement(this); |
| |
| RenderStyle* style = 0; |
| if (Element* parent = parentOrShadowHostElement()) { |
| if (RenderObject* renderer = parent->renderer()) |
| style = renderer->style(); |
| } |
| |
| return document().ensureStyleResolver().styleForElement(correspondingElement(), style, DisallowStyleSharing); |
| } |
| |
| MutableStylePropertySet* SVGElement::animatedSMILStyleProperties() const |
| { |
| if (hasSVGRareData()) |
| return svgRareData()->animatedSMILStyleProperties(); |
| return 0; |
| } |
| |
| MutableStylePropertySet* SVGElement::ensureAnimatedSMILStyleProperties() |
| { |
| return ensureSVGRareData()->ensureAnimatedSMILStyleProperties(); |
| } |
| |
| void SVGElement::setUseOverrideComputedStyle(bool value) |
| { |
| if (hasSVGRareData()) |
| svgRareData()->setUseOverrideComputedStyle(value); |
| } |
| |
| RenderStyle* SVGElement::computedStyle(PseudoId pseudoElementSpecifier) |
| { |
| if (!hasSVGRareData() || !svgRareData()->useOverrideComputedStyle()) |
| return Element::computedStyle(pseudoElementSpecifier); |
| |
| RenderStyle* parentStyle = 0; |
| if (Element* parent = parentOrShadowHostElement()) { |
| if (RenderObject* renderer = parent->renderer()) |
| parentStyle = renderer->style(); |
| } |
| |
| return svgRareData()->overrideComputedStyle(this, parentStyle); |
| } |
| |
| bool SVGElement::hasFocusEventListeners() const |
| { |
| return hasEventListeners(EventTypeNames::focusin) || hasEventListeners(EventTypeNames::focusout) |
| || hasEventListeners(EventTypeNames::focus) || hasEventListeners(EventTypeNames::blur); |
| } |
| |
| void SVGElement::invalidateInstances() |
| { |
| if (!inDocument()) |
| return; |
| |
| if (instanceUpdatesBlocked()) |
| return; |
| |
| const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& set = instancesForElement(); |
| if (set.isEmpty()) |
| return; |
| |
| // Mark all use elements referencing 'element' for rebuilding |
| const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator end = set.end(); |
| for (WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator it = set.begin(); it != end; ++it) { |
| (*it)->setCorrespondingElement(0); |
| |
| if (SVGUseElement* element = (*it)->correspondingUseElement()) { |
| ASSERT(element->inDocument()); |
| element->invalidateShadowTree(); |
| } |
| } |
| |
| svgRareData()->elementInstances().clear(); |
| |
| document().updateRenderTreeIfNeeded(); |
| } |
| |
| SVGElement::InstanceUpdateBlocker::InstanceUpdateBlocker(SVGElement* targetElement) |
| : m_targetElement(targetElement) |
| { |
| if (m_targetElement) |
| m_targetElement->setInstanceUpdatesBlocked(true); |
| } |
| |
| SVGElement::InstanceUpdateBlocker::~InstanceUpdateBlocker() |
| { |
| if (m_targetElement) |
| m_targetElement->setInstanceUpdatesBlocked(false); |
| } |
| |
| #if ENABLE(ASSERT) |
| bool SVGElement::isAnimatableAttribute(const QualifiedName& name) const |
| { |
| DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, animatableAttributes, ()); |
| |
| if (animatableAttributes.isEmpty()) { |
| animatableAttributes.add(XLinkNames::hrefAttr); |
| animatableAttributes.add(SVGNames::amplitudeAttr); |
| animatableAttributes.add(SVGNames::azimuthAttr); |
| animatableAttributes.add(SVGNames::baseFrequencyAttr); |
| animatableAttributes.add(SVGNames::biasAttr); |
| animatableAttributes.add(SVGNames::clipPathUnitsAttr); |
| animatableAttributes.add(SVGNames::cxAttr); |
| animatableAttributes.add(SVGNames::cyAttr); |
| animatableAttributes.add(SVGNames::diffuseConstantAttr); |
| animatableAttributes.add(SVGNames::divisorAttr); |
| animatableAttributes.add(SVGNames::dxAttr); |
| animatableAttributes.add(SVGNames::dyAttr); |
| animatableAttributes.add(SVGNames::edgeModeAttr); |
| animatableAttributes.add(SVGNames::elevationAttr); |
| animatableAttributes.add(SVGNames::exponentAttr); |
| animatableAttributes.add(SVGNames::filterResAttr); |
| animatableAttributes.add(SVGNames::filterUnitsAttr); |
| animatableAttributes.add(SVGNames::fxAttr); |
| animatableAttributes.add(SVGNames::fyAttr); |
| animatableAttributes.add(SVGNames::gradientTransformAttr); |
| animatableAttributes.add(SVGNames::gradientUnitsAttr); |
| animatableAttributes.add(SVGNames::heightAttr); |
| animatableAttributes.add(SVGNames::in2Attr); |
| animatableAttributes.add(SVGNames::inAttr); |
| animatableAttributes.add(SVGNames::interceptAttr); |
| animatableAttributes.add(SVGNames::k1Attr); |
| animatableAttributes.add(SVGNames::k2Attr); |
| animatableAttributes.add(SVGNames::k3Attr); |
| animatableAttributes.add(SVGNames::k4Attr); |
| animatableAttributes.add(SVGNames::kernelMatrixAttr); |
| animatableAttributes.add(SVGNames::kernelUnitLengthAttr); |
| animatableAttributes.add(SVGNames::lengthAdjustAttr); |
| animatableAttributes.add(SVGNames::limitingConeAngleAttr); |
| animatableAttributes.add(SVGNames::markerHeightAttr); |
| animatableAttributes.add(SVGNames::markerUnitsAttr); |
| animatableAttributes.add(SVGNames::markerWidthAttr); |
| animatableAttributes.add(SVGNames::maskContentUnitsAttr); |
| animatableAttributes.add(SVGNames::maskUnitsAttr); |
| animatableAttributes.add(SVGNames::methodAttr); |
| animatableAttributes.add(SVGNames::modeAttr); |
| animatableAttributes.add(SVGNames::numOctavesAttr); |
| animatableAttributes.add(SVGNames::offsetAttr); |
| animatableAttributes.add(SVGNames::operatorAttr); |
| animatableAttributes.add(SVGNames::orderAttr); |
| animatableAttributes.add(SVGNames::orientAttr); |
| animatableAttributes.add(SVGNames::pathLengthAttr); |
| animatableAttributes.add(SVGNames::patternContentUnitsAttr); |
| animatableAttributes.add(SVGNames::patternTransformAttr); |
| animatableAttributes.add(SVGNames::patternUnitsAttr); |
| animatableAttributes.add(SVGNames::pointsAtXAttr); |
| animatableAttributes.add(SVGNames::pointsAtYAttr); |
| animatableAttributes.add(SVGNames::pointsAtZAttr); |
| animatableAttributes.add(SVGNames::preserveAlphaAttr); |
| animatableAttributes.add(SVGNames::preserveAspectRatioAttr); |
| animatableAttributes.add(SVGNames::primitiveUnitsAttr); |
| animatableAttributes.add(SVGNames::radiusAttr); |
| animatableAttributes.add(SVGNames::rAttr); |
| animatableAttributes.add(SVGNames::refXAttr); |
| animatableAttributes.add(SVGNames::refYAttr); |
| animatableAttributes.add(SVGNames::resultAttr); |
| animatableAttributes.add(SVGNames::rotateAttr); |
| animatableAttributes.add(SVGNames::rxAttr); |
| animatableAttributes.add(SVGNames::ryAttr); |
| animatableAttributes.add(SVGNames::scaleAttr); |
| animatableAttributes.add(SVGNames::seedAttr); |
| animatableAttributes.add(SVGNames::slopeAttr); |
| animatableAttributes.add(SVGNames::spacingAttr); |
| animatableAttributes.add(SVGNames::specularConstantAttr); |
| animatableAttributes.add(SVGNames::specularExponentAttr); |
| animatableAttributes.add(SVGNames::spreadMethodAttr); |
| animatableAttributes.add(SVGNames::startOffsetAttr); |
| animatableAttributes.add(SVGNames::stdDeviationAttr); |
| animatableAttributes.add(SVGNames::stitchTilesAttr); |
| animatableAttributes.add(SVGNames::surfaceScaleAttr); |
| animatableAttributes.add(SVGNames::tableValuesAttr); |
| animatableAttributes.add(SVGNames::targetAttr); |
| animatableAttributes.add(SVGNames::targetXAttr); |
| animatableAttributes.add(SVGNames::targetYAttr); |
| animatableAttributes.add(SVGNames::transformAttr); |
| animatableAttributes.add(SVGNames::typeAttr); |
| animatableAttributes.add(SVGNames::valuesAttr); |
| animatableAttributes.add(SVGNames::viewBoxAttr); |
| animatableAttributes.add(SVGNames::widthAttr); |
| animatableAttributes.add(SVGNames::x1Attr); |
| animatableAttributes.add(SVGNames::x2Attr); |
| animatableAttributes.add(SVGNames::xAttr); |
| animatableAttributes.add(SVGNames::xChannelSelectorAttr); |
| animatableAttributes.add(SVGNames::y1Attr); |
| animatableAttributes.add(SVGNames::y2Attr); |
| animatableAttributes.add(SVGNames::yAttr); |
| animatableAttributes.add(SVGNames::yChannelSelectorAttr); |
| animatableAttributes.add(SVGNames::zAttr); |
| } |
| |
| if (name == classAttr) |
| return true; |
| |
| return animatableAttributes.contains(name); |
| } |
| #endif |
| |
| SVGElementSet* SVGElement::setOfIncomingReferences() const |
| { |
| if (!hasSVGRareData()) |
| return 0; |
| return &svgRareData()->incomingReferences(); |
| } |
| |
| void SVGElement::addReferenceTo(SVGElement* targetElement) |
| { |
| ASSERT(targetElement); |
| |
| ensureSVGRareData()->outgoingReferences().add(targetElement); |
| targetElement->ensureSVGRareData()->incomingReferences().add(this); |
| } |
| |
| void SVGElement::rebuildAllIncomingReferences() |
| { |
| if (!hasSVGRareData()) |
| return; |
| |
| const SVGElementSet& incomingReferences = svgRareData()->incomingReferences(); |
| |
| // Iterate on a snapshot as |incomingReferences| may be altered inside loop. |
| WillBeHeapVector<RawPtrWillBeMember<SVGElement> > incomingReferencesSnapshot; |
| copyToVector(incomingReferences, incomingReferencesSnapshot); |
| |
| // Force rebuilding the |sourceElement| so it knows about this change. |
| for (WillBeHeapVector<RawPtrWillBeMember<SVGElement> >::iterator it = incomingReferencesSnapshot.begin(), itEnd = incomingReferencesSnapshot.end(); it != itEnd; ++it) { |
| SVGElement* sourceElement = *it; |
| |
| // Before rebuilding |sourceElement| ensure it was not removed from under us. |
| if (incomingReferences.contains(sourceElement)) |
| sourceElement->svgAttributeChanged(XLinkNames::hrefAttr); |
| } |
| } |
| |
| void SVGElement::removeAllIncomingReferences() |
| { |
| if (!hasSVGRareData()) |
| return; |
| |
| SVGElementSet& incomingReferences = svgRareData()->incomingReferences(); |
| for (SVGElementSet::iterator it = incomingReferences.begin(), itEnd = incomingReferences.end(); it != itEnd; ++it) { |
| SVGElement* sourceElement = *it; |
| ASSERT(sourceElement->hasSVGRareData()); |
| sourceElement->ensureSVGRareData()->outgoingReferences().remove(this); |
| } |
| incomingReferences.clear(); |
| } |
| |
| void SVGElement::removeAllOutgoingReferences() |
| { |
| if (!hasSVGRareData()) |
| return; |
| |
| SVGElementSet& outgoingReferences = svgRareData()->outgoingReferences(); |
| for (SVGElementSet::iterator it = outgoingReferences.begin(), itEnd = outgoingReferences.end(); it != itEnd; ++it) { |
| SVGElement* targetElement = *it; |
| ASSERT(targetElement->hasSVGRareData()); |
| targetElement->ensureSVGRareData()->incomingReferences().remove(this); |
| } |
| outgoingReferences.clear(); |
| } |
| |
| void SVGElement::trace(Visitor* visitor) |
| { |
| #if ENABLE(OILPAN) |
| visitor->trace(m_elementsWithRelativeLengths); |
| visitor->trace(m_SVGRareData); |
| #endif |
| Element::trace(visitor); |
| } |
| |
| const AtomicString& SVGElement::eventParameterName() |
| { |
| DEFINE_STATIC_LOCAL(const AtomicString, evtString, ("evt", AtomicString::ConstructFromLiteral)); |
| return evtString; |
| } |
| |
| } |