Example #1
0
static void updateContainerOffset(SVGElementInstance* targetInstance)
{
    // Depth-first used to write the method in early exit style, no particular other reason.
    for (SVGElementInstance* instance = targetInstance->firstChild(); instance; instance = instance->nextSibling())
        updateContainerOffset(instance);

    SVGElement* correspondingElement = targetInstance->correspondingElement();
    ASSERT(correspondingElement);

    if (!correspondingElement->hasTagName(SVGNames::useTag))
        return;

    SVGElement* shadowTreeElement = targetInstance->shadowTreeElement();
    ASSERT(shadowTreeElement);
    ASSERT(shadowTreeElement->hasTagName(SVGNames::gTag));

    if (!static_cast<SVGGElement*>(shadowTreeElement)->isShadowTreeContainerElement())
        return;

    // Spec: An additional transformation translate(x,y) is appended to the end
    // (i.e., right-side) of the transform attribute on the generated 'g', where x
    // and y represent the values of the x and y attributes on the 'use' element. 
    SVGUseElement* useElement = static_cast<SVGUseElement*>(correspondingElement);
    SVGShadowTreeContainerElement* containerElement = static_cast<SVGShadowTreeContainerElement*>(shadowTreeElement);
    containerElement->setContainerOffset(useElement->x(), useElement->y());
}
Example #2
0
void SVGAnimateTransformElement::applyResultsToTarget()
{
    if (!hasValidTarget())
        return;
    // We accumulate to the target element transform list so there is not much to do here.
    SVGElement* targetElement = this->targetElement();
    if (!targetElement)
        return;

    if (RenderObject* renderer = targetElement->renderer()) {
        renderer->setNeedsTransformUpdate();
        RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
    }

    // ...except in case where we have additional instances in <use> trees.
    const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement();
    RefPtr<SVGTransformList> transformList = transformListFor(targetElement);
    const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
    for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
        SVGElement* shadowTreeElement = (*it)->shadowTreeElement();
        ASSERT(shadowTreeElement);
        if (shadowTreeElement->isStyledTransformable())
            static_cast<SVGStyledTransformableElement*>(shadowTreeElement)->setTransformBaseValue(transformList.get());
        else if (shadowTreeElement->hasTagName(SVGNames::textTag))
            static_cast<SVGTextElement*>(shadowTreeElement)->setTransformBaseValue(transformList.get());
        else if (shadowTreeElement->hasTagName(SVGNames::linearGradientTag) || shadowTreeElement->hasTagName(SVGNames::radialGradientTag))
            static_cast<SVGGradientElement*>(shadowTreeElement)->setGradientTransformBaseValue(transformList.get());
        if (RenderObject* renderer = shadowTreeElement->renderer()) {
            renderer->setNeedsTransformUpdate();
            RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
        }
    }
}
void RenderSVGContainer::calcViewport()
{
    SVGElement* svgelem = static_cast<SVGElement*>(element());
    if (svgelem->hasTagName(SVGNames::svgTag)) {
        SVGSVGElement* svg = static_cast<SVGSVGElement*>(element());

        if (!selfNeedsLayout() && !svg->hasRelativeValues())
            return;

        float x = 0.0f;
        float y = 0.0f;
        if (parent()->isSVGContainer()) {
            x = svg->x().value();
            y = svg->y().value();
        }
        float w = svg->width().value();
        float h = svg->height().value();
        m_viewport = FloatRect(x, y, w, h);
    } else if (svgelem->hasTagName(SVGNames::markerTag)) {
        if (!selfNeedsLayout())
            return;

        SVGMarkerElement* svg = static_cast<SVGMarkerElement*>(element());
        float w = svg->markerWidth().value();
        float h = svg->markerHeight().value();
        m_viewport = FloatRect(0.0f, 0.0f, w, h);
    }
}
Example #4
0
static void updateContainerSize(SVGUseElement* useElement, SVGElementInstance* targetInstance)
{
    // Depth-first used to write the method in early exit style, no particular other reason.
    for (SVGElementInstance* instance = targetInstance->firstChild(); instance; instance = instance->nextSibling())
        updateContainerSize(useElement, instance);

    SVGElement* correspondingElement = targetInstance->correspondingElement();
    ASSERT(correspondingElement);

    bool isSymbolTag = correspondingElement->hasTagName(SVGNames::symbolTag);
    if (!correspondingElement->hasTagName(SVGNames::svgTag) && !isSymbolTag)
        return;

    SVGElement* shadowTreeElement = targetInstance->shadowTreeElement();
    ASSERT(shadowTreeElement);
    ASSERT(shadowTreeElement->hasTagName(SVGNames::svgTag));

    // Spec (<use> on <symbol>): This generated 'svg' will always have explicit values for attributes width and height.
    // If attributes width and/or height are provided on the 'use' element, then these attributes
    // will be transferred to the generated 'svg'. If attributes width and/or height are not specified,
    // the generated 'svg' element will use values of 100% for these attributes.
    
    // Spec (<use> on <svg>): If attributes width and/or height are provided on the 'use' element, then these
    // values will override the corresponding attributes on the 'svg' in the generated tree.

    if (useElement->hasAttribute(SVGNames::widthAttr))
        shadowTreeElement->setAttribute(SVGNames::widthAttr, useElement->getAttribute(SVGNames::widthAttr));
    else if (isSymbolTag && shadowTreeElement->hasAttribute(SVGNames::widthAttr))
        shadowTreeElement->setAttribute(SVGNames::widthAttr, "100%");

    if (useElement->hasAttribute(SVGNames::heightAttr))
        shadowTreeElement->setAttribute(SVGNames::heightAttr, useElement->getAttribute(SVGNames::heightAttr));
    else if (isSymbolTag && shadowTreeElement->hasAttribute(SVGNames::heightAttr))
        shadowTreeElement->setAttribute(SVGNames::heightAttr, "100%");
}   
Example #5
0
bool SVGAnimateTransformElement::hasValidTarget() const
{
    SVGElement* targetElement = this->targetElement();
    return SVGAnimationElement::hasValidTarget()
        && (targetElement->isStyledTransformable()
        || targetElement->hasTagName(SVGNames::textTag)
        || targetElement->hasTagName(SVGNames::linearGradientTag)
        || targetElement->hasTagName(SVGNames::radialGradientTag));
}
Example #6
0
static bool isDirectReference(const SVGElement& element)
{
    return element.hasTagName(SVGNames::pathTag)
        || element.hasTagName(SVGNames::rectTag)
        || element.hasTagName(SVGNames::circleTag)
        || element.hasTagName(SVGNames::ellipseTag)
        || element.hasTagName(SVGNames::polygonTag)
        || element.hasTagName(SVGNames::polylineTag)
        || element.hasTagName(SVGNames::textTag);
}
bool RenderSVGTransformableContainer::calculateLocalTransform()
{
    SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());

    // If we're either the renderer for a <use> element, or for any <g> element inside the shadow
    // tree, that was created during the use/symbol/svg expansion in SVGUseElement. These containers
    // need to respect the translations induced by their corresponding use elements x/y attributes.
    SVGUseElement* useElement = 0;
    if (element->hasTagName(SVGNames::useTag))
        useElement = static_cast<SVGUseElement*>(element);
    else if (element->isInShadowTree() && element->hasTagName(SVGNames::gTag)) {
        SVGElement* correspondingElement = element->correspondingElement();
        if (correspondingElement && correspondingElement->hasTagName(SVGNames::useTag))
            useElement = static_cast<SVGUseElement*>(correspondingElement);
    }

    if (useElement) {
        SVGLengthContext lengthContext(useElement);
        FloatSize translation(useElement->x().value(lengthContext), useElement->y().value(lengthContext));
        if (translation != m_lastTranslation)
            m_needsTransformUpdate = true;
        m_lastTranslation = translation;
    }

    m_didTransformToRootUpdate = m_needsTransformUpdate || SVGRenderSupport::transformToRootChanged(parent());
    if (!m_needsTransformUpdate)
        return false;

    m_localTransform = element->animatedLocalTransform();
    m_localTransform.translate(m_lastTranslation.width(), m_lastTranslation.height());
    m_needsTransformUpdate = false;
    return true;
}
Example #8
0
void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* anchorNode)
{
    bool hadUseCurrentView = m_useCurrentView;
    if (fragmentIdentifier.startsWith("xpointer(")) {
        // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491)
        setUseCurrentView(false);
    } else if (fragmentIdentifier.startsWith("svgView(")) {
        if (currentView()->parseViewSpec(fragmentIdentifier))
            setUseCurrentView(true);
    } else if (anchorNode && anchorNode->hasTagName(SVGNames::viewTag)) {
        if (SVGViewElement* viewElement = anchorNode->hasTagName(SVGNames::viewTag) ? static_cast<SVGViewElement*>(anchorNode) : 0) {
            SVGElement* element = SVGLocatable::nearestViewportElement(viewElement);
            if (element->hasTagName(SVGNames::svgTag)) {
                SVGSVGElement* svg = static_cast<SVGSVGElement*>(element);
                svg->inheritViewAttributes(viewElement);
                setUseCurrentView(true);
            }
        }
    }

    if (!hadUseCurrentView) {
        if (!m_useCurrentView)
            return;
    } else if (!m_useCurrentView)
        currentView()->setTransform(emptyString());

    // Force a layout, otherwise RenderSVGRoots localToBorderBoxTransform won't be rebuild.
    if (RenderObject* object = renderer())
        RenderSVGResource::markForLayoutAndParentResourceInvalidation(object);

    // FIXME: We need to decide which <svg> to focus on, and zoom to it.
    // FIXME: We need to actually "highlight" the viewTarget(s).
}
void RenderSVGViewportContainer::calcViewport()
{
    SVGSVGElement& svg = svgSVGElement();
    FloatRect oldViewport = m_viewport;

    SVGLengthContext lengthContext(&svg);
    m_viewport = FloatRect(svg.x().value(lengthContext), svg.y().value(lengthContext), svg.width().value(lengthContext), svg.height().value(lengthContext));

    SVGElement* correspondingElement = svg.correspondingElement();
    if (correspondingElement && svg.isInShadowTree()) {
        const HashSet<SVGElementInstance*>& instances = correspondingElement->instancesForElement();
        ASSERT(!instances.isEmpty());

        SVGUseElement* useElement = 0;
        const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
        for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
            const SVGElementInstance* instance = (*it);
            ASSERT(instance->correspondingElement()->hasTagName(SVGNames::svgTag) || instance->correspondingElement()->hasTagName(SVGNames::symbolTag));
            if (instance->shadowTreeElement() == &svg) {
                ASSERT(correspondingElement == instance->correspondingElement());
                useElement = instance->directUseElement();
                if (!useElement)
                    useElement = instance->correspondingUseElement();
                break;
            }
        }

        ASSERT(useElement);
        bool isSymbolElement = correspondingElement->hasTagName(SVGNames::symbolTag);

        // Spec (<use> on <symbol>): This generated 'svg' will always have explicit values for attributes width and height.
        // If attributes width and/or height are provided on the 'use' element, then these attributes
        // will be transferred to the generated 'svg'. If attributes width and/or height are not specified,
        // the generated 'svg' element will use values of 100% for these attributes.

        // Spec (<use> on <svg>): If attributes width and/or height are provided on the 'use' element, then these
        // values will override the corresponding attributes on the 'svg' in the generated tree.

        SVGLengthContext lengthContext(&svg);
        if (useElement->hasAttribute(SVGNames::widthAttr))
            m_viewport.setWidth(useElement->width().value(lengthContext));
        else if (isSymbolElement && svg.hasAttribute(SVGNames::widthAttr)) {
            SVGLength containerWidth(LengthModeWidth, "100%");
            m_viewport.setWidth(containerWidth.value(lengthContext));
        }

        if (useElement->hasAttribute(SVGNames::heightAttr))
            m_viewport.setHeight(useElement->height().value(lengthContext));
        else if (isSymbolElement && svg.hasAttribute(SVGNames::heightAttr)) {
            SVGLength containerHeight(LengthModeHeight, "100%");
            m_viewport.setHeight(containerHeight.value(lengthContext));
        }
    }

    if (oldViewport != m_viewport) {
        setNeedsBoundariesUpdate();
        setNeedsTransformUpdate();
    }
}
Example #10
0
static TextStream& operator<<(TextStream& ts, const RenderSVGShape& shape)
{
    writePositionAndStyle(ts, shape);

    SVGElement* svgElement = shape.element();
    SVGLengthContext lengthContext(svgElement);

    if (isSVGRectElement(svgElement)) {
        SVGRectElement* element = toSVGRectElement(svgElement);
        writeNameValuePair(ts, "x", element->x().value(lengthContext));
        writeNameValuePair(ts, "y", element->y().value(lengthContext));
        writeNameValuePair(ts, "width", element->width().value(lengthContext));
        writeNameValuePair(ts, "height", element->height().value(lengthContext));
    } else if (isSVGLineElement(svgElement)) {
        SVGLineElement* element = toSVGLineElement(svgElement);
        writeNameValuePair(ts, "x1", element->x1().value(lengthContext));
        writeNameValuePair(ts, "y1", element->y1().value(lengthContext));
        writeNameValuePair(ts, "x2", element->x2().value(lengthContext));
        writeNameValuePair(ts, "y2", element->y2().value(lengthContext));
    } else if (isSVGEllipseElement(svgElement)) {
        SVGEllipseElement* element = toSVGEllipseElement(svgElement);
        writeNameValuePair(ts, "cx", element->cx().value(lengthContext));
        writeNameValuePair(ts, "cy", element->cy().value(lengthContext));
        writeNameValuePair(ts, "rx", element->rx().value(lengthContext));
        writeNameValuePair(ts, "ry", element->ry().value(lengthContext));
    } else if (isSVGCircleElement(svgElement)) {
        SVGCircleElement* element = toSVGCircleElement(svgElement);
        writeNameValuePair(ts, "cx", element->cx().value(lengthContext));
        writeNameValuePair(ts, "cy", element->cy().value(lengthContext));
        writeNameValuePair(ts, "r", element->r().value(lengthContext));
    } else if (svgElement->hasTagName(SVGNames::polygonTag) || svgElement->hasTagName(SVGNames::polylineTag)) {
        SVGPolyElement* element = toSVGPolyElement(svgElement);
        writeNameAndQuotedValue(ts, "points", element->pointList().valueAsString());
    } else if (isSVGPathElement(svgElement)) {
        SVGPathElement* element = toSVGPathElement(svgElement);
        String pathString;
        // FIXME: We should switch to UnalteredParsing here - this will affect the path dumping output of dozens of tests.
        buildStringFromByteStream(element->pathByteStream(), pathString, NormalizedParsing);
        writeNameAndQuotedValue(ts, "data", pathString);
    } else
        ASSERT_NOT_REACHED();
    return ts;
}
Example #11
0
void SVGUseElement::associateInstancesWithShadowTreeElements(Node* target, SVGElementInstance* targetInstance)
{
    if (!target || !targetInstance)
        return;

    SVGElement* originalElement = targetInstance->correspondingElement();

    if (originalElement->hasTagName(SVGNames::useTag)) {
#if ENABLE(SVG) && ENABLE(SVG_USE)
        // <use> gets replaced by <g>
        ASSERT(target->nodeName() == SVGNames::gTag);
#else 
        ASSERT(target->nodeName() == SVGNames::gTag || target->nodeName() == SVGNames::useTag);
#endif
    } else if (originalElement->hasTagName(SVGNames::symbolTag)) {
        // <symbol> gets replaced by <svg>
#if ENABLE(SVG) && ENABLE(SVG_USE) && ENABLE(SVG_FOREIGN_OBJECT)
        ASSERT(target->nodeName() == SVGNames::svgTag);
#endif
    } else
        ASSERT(target->nodeName() == originalElement->nodeName());

    SVGElement* element = 0;
    if (target->isSVGElement())
        element = static_cast<SVGElement*>(target);

    ASSERT(!targetInstance->shadowTreeElement());
    targetInstance->setShadowTreeElement(element);

    Node* node = target->firstChild();
    for (SVGElementInstance* instance = targetInstance->firstChild(); node && instance; instance = instance->nextSibling()) {
        // Skip any non-svg elements in shadow tree
        while (node && !node->isSVGElement())
           node = node->nextSibling();

        if (!node)
            break;

        associateInstancesWithShadowTreeElements(node, instance);
        node = node->nextSibling();
    }
}
Example #12
0
void RenderSVGViewportContainer::calcViewport()
{
     SVGElement* element = static_cast<SVGElement*>(node());
     if (element->hasTagName(SVGNames::svgTag)) {
         SVGSVGElement* svg = static_cast<SVGSVGElement*>(element);
         m_viewport = FloatRect(svg->x().value(svg)
                                , svg->y().value(svg)
                                , svg->width().value(svg)
                                , svg->height().value(svg));
    }
}
Example #13
0
void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* anchorNode)
{
    RenderObject* renderer = this->renderer();
    SVGViewSpec* view = m_viewSpec.get();
    if (view)
        view->reset();

    bool hadUseCurrentView = m_useCurrentView;
    m_useCurrentView = false;

    if (fragmentIdentifier.startsWith("xpointer(")) {
        // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491)
        if (renderer && hadUseCurrentView)
            RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
        return;
    }

    if (fragmentIdentifier.startsWith("svgView(")) {
        if (!view)
            view = currentView(); // Create the SVGViewSpec.

        if (view->parseViewSpec(fragmentIdentifier))
            m_useCurrentView = true;
        else
            view->reset();

        if (renderer && (hadUseCurrentView || m_useCurrentView))
            RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
        return;
    }

    // Spec: If the SVG fragment identifier addresses a ‘view’ element within an SVG document (e.g., MyDrawing.svg#MyView
    // or MyDrawing.svg#xpointer(id('MyView'))) then the closest ancestor ‘svg’ element is displayed in the viewport.
    // Any view specification attributes included on the given ‘view’ element override the corresponding view specification
    // attributes on the closest ancestor ‘svg’ element.
    if (anchorNode && anchorNode->hasTagName(SVGNames::viewTag)) {
        if (SVGViewElement* viewElement = anchorNode->hasTagName(SVGNames::viewTag) ? static_cast<SVGViewElement*>(anchorNode) : 0) {
            SVGElement* element = SVGLocatable::nearestViewportElement(viewElement);
            if (element->hasTagName(SVGNames::svgTag)) {
                SVGSVGElement* svg = static_cast<SVGSVGElement*>(element);
                svg->inheritViewAttributes(viewElement);

                if (RenderObject* renderer = svg->renderer())
                    RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
            }
        }
        return;
    }

    // FIXME: We need to decide which <svg> to focus on, and zoom to it.
    // FIXME: We need to actually "highlight" the viewTarget(s).
}
Example #14
0
static void dumpInstanceTree(unsigned int& depth, String& text, SVGElementInstance* targetInstance)
{
    SVGElement* element = targetInstance->correspondingElement();
    ASSERT(element);

    if (element->hasTagName(SVGNames::useTag)) {
        if (toSVGUseElement(element)->cachedDocumentIsStillLoading())
            return;
    }

    SVGElement* shadowTreeElement = targetInstance->shadowTreeElement();
    ASSERT(shadowTreeElement);

    SVGUseElement* directUseElement = targetInstance->directUseElement();
    String directUseElementName = directUseElement ? directUseElement->nodeName() : "null";

    String elementId = element->getIdAttribute();
    String elementNodeName = element->nodeName();
    String shadowTreeElementNodeName = shadowTreeElement->nodeName();
    String parentNodeName = element->parentNode() ? element->parentNode()->nodeName() : "null";
    String firstChildNodeName = element->firstChild() ? element->firstChild()->nodeName() : "null";

    for (unsigned int i = 0; i < depth; ++i)
        text += "  ";

    text += String::format("SVGElementInstance this=%p, (parentNode=%s (%p), firstChild=%s (%p), correspondingElement=%s (%p), directUseElement=%s (%p), shadowTreeElement=%s (%p), id=%s)\n",
                           targetInstance, parentNodeName.latin1().data(), element->parentNode(), firstChildNodeName.latin1().data(), element->firstChild(),
                           elementNodeName.latin1().data(), element, directUseElementName.latin1().data(), directUseElement, shadowTreeElementNodeName.latin1().data(), shadowTreeElement, elementId.latin1().data());

    for (unsigned int i = 0; i < depth; ++i)
        text += "  ";

    const HashSet<SVGElementInstance*>& elementInstances = element->instancesForElement();
    text += "Corresponding element is associated with " + String::number(elementInstances.size()) + " instance(s):\n";

    const HashSet<SVGElementInstance*>::const_iterator end = elementInstances.end();
    for (HashSet<SVGElementInstance*>::const_iterator it = elementInstances.begin(); it != end; ++it) {
        for (unsigned int i = 0; i < depth; ++i)
            text += "  ";

        text += String::format(" -> SVGElementInstance this=%p, (refCount: %i, shadowTreeElement in document? %i)\n",
                               *it, (*it)->refCount(), (*it)->shadowTreeElement()->inDocument());
    }

    ++depth;

    for (SVGElementInstance* instance = targetInstance->firstChild(); instance; instance = instance->nextSibling())
        dumpInstanceTree(depth, text, instance);

    --depth;
}
Example #15
0
void SVGUseElement::buildInstanceTree(SVGElement* target, SVGElementInstance* targetInstance, bool& foundProblem)
{
    ASSERT(target);
    ASSERT(targetInstance);

    // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced
    // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree.
    bool targetHasUseTag = target->hasTagName(SVGNames::useTag);
    SVGElement* newTarget = 0;
    if (targetHasUseTag) {
        foundProblem = hasCycleUseReferencing(static_cast<SVGUseElement*>(target), targetInstance, newTarget);
        if (foundProblem)
            return;
    }

    // A general description from the SVG spec, describing what buildInstanceTree() actually does.
    //
    // Spec: If the 'use' element references a 'g' which contains two 'rect' elements, then the instance tree
    // contains three SVGElementInstance objects, a root SVGElementInstance object whose correspondingElement
    // is the SVGGElement object for the 'g', and then two child SVGElementInstance objects, each of which has
    // its correspondingElement that is an SVGRectElement object.

    for (Node* node = target->firstChild(); node; node = node->nextSibling()) {
        SVGElement* element = 0;
        if (node->isSVGElement())
            element = static_cast<SVGElement*>(node);

        // Skip any non-svg nodes or any disallowed element.
        if (!element || isDisallowedElement(element))
            continue;

        // Create SVGElementInstance object, for both container/non-container nodes.
        RefPtr<SVGElementInstance> instance = SVGElementInstance::create(this, element);
        SVGElementInstance* instancePtr = instance.get();
        targetInstance->appendChild(instance.release());

        // Enter recursion, appending new instance tree nodes to the "instance" object.
        buildInstanceTree(element, instancePtr, foundProblem);
        if (foundProblem)
            return;
    }

    if (!targetHasUseTag || !newTarget)
        return;

    RefPtr<SVGElementInstance> newInstance = SVGElementInstance::create(this, newTarget);
    SVGElementInstance* newInstancePtr = newInstance.get();
    targetInstance->appendChild(newInstance.release());
    buildInstanceTree(newTarget, newInstancePtr, foundProblem);
}
Example #16
0
static bool isDirectReference(const SVGElement& element)
{
    using namespace SVGNames;
    return element.hasTagName(circleTag)
        || element.hasTagName(ellipseTag)
        || element.hasTagName(pathTag)
        || element.hasTagName(polygonTag)
        || element.hasTagName(polylineTag)
        || element.hasTagName(rectTag)
        || element.hasTagName(textTag);
}
void SVGAnimateTransformElement::applyResultsToTarget()
{
    if (!hasValidTarget())
        return;
    // We accumulate to the target element transform list so there is not much to do here.
    SVGElement* targetElement = this->targetElement();
    if (targetElement->renderer())
        targetElement->renderer()->setNeedsLayout(true);
    
    // ...except in case where we have additional instances in <use> trees.
    const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement();
    RefPtr<SVGTransformList> transformList = transformListFor(targetElement);
    const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
    for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
        SVGElement* shadowTreeElement = (*it)->shadowTreeElement();
        ASSERT(shadowTreeElement);
        if (shadowTreeElement->isStyledTransformable())
            static_cast<SVGStyledTransformableElement*>(shadowTreeElement)->setTransformBaseValue(transformList.get());
        else if (shadowTreeElement->hasTagName(SVGNames::textTag))
            static_cast<SVGTextElement*>(shadowTreeElement)->setTransformBaseValue(transformList.get());
        if (shadowTreeElement->renderer())
            shadowTreeElement->renderer()->setNeedsLayout(true);
    }
}
Example #18
0
void SVGUseElement::expandUseElementsInShadowTree(Node* element)
{
    // Why expand the <use> elements in the shadow tree here, and not just
    // do this directly in buildShadowTree, if we encounter a <use> element?
    //
    // Short answer: Because we may miss to expand some elements. Ie. if a <symbol>
    // contains <use> tags, we'd miss them. So once we're done with settin' up the
    // actual shadow tree (after the special case modification for svg/symbol) we have
    // to walk it completely and expand all <use> elements.
    if (element->hasTagName(SVGNames::useTag)) {
        SVGUseElement* use = static_cast<SVGUseElement*>(element);

        String id = SVGURIReference::getTarget(use->href());
        Element* targetElement = document()->getElementById(id); 
        SVGElement* target = 0;
        if (targetElement && targetElement->isSVGElement())
            target = static_cast<SVGElement*>(targetElement);

        // Don't ASSERT(target) here, it may be "pending", too.
        if (target) {
            // Setup sub-shadow tree root node
            RefPtr<SVGElement> cloneParent = new SVGGElement(SVGNames::gTag, document());

            // Spec: In the generated content, the 'use' will be replaced by 'g', where all attributes from the
            // 'use' element except for x, y, width, height and xlink:href are transferred to the generated 'g' element.
            transferUseAttributesToReplacedElement(use, cloneParent.get());

            // Spec: An additional transformation translate(x,y) is appended to the end
            // (i.e., right-side) of the transform attribute on the generated 'g', where x
            // and y represent the values of the x and y attributes on the 'use' element.
            if (use->x().value() != 0.0 || use->y().value() != 0.0) {
                if (!cloneParent->hasAttribute(SVGNames::transformAttr)) {
                    String transformString = String::format("translate(%f, %f)", use->x().value(), use->y().value());
                    cloneParent->setAttribute(SVGNames::transformAttr, transformString);
                } else {
                    String transformString = String::format(" translate(%f, %f)", use->x().value(), use->y().value());
                    const AtomicString& transformAttribute = cloneParent->getAttribute(SVGNames::transformAttr);
                    cloneParent->setAttribute(SVGNames::transformAttr, transformAttribute + transformString); 
                }
            }

            ExceptionCode ec = 0;
 
            // For instance <use> on <foreignObject> (direct case).
            if (isDisallowedElement(target)) {
                // We still have to setup the <use> replacment (<g>). Otherwhise
                // associateInstancesWithShadowTreeElements() makes wrong assumptions.
                // Replace <use> with referenced content.
                ASSERT(use->parentNode()); 
                use->parentNode()->replaceChild(cloneParent.release(), use, ec);
                ASSERT(ec == 0);
                return;
            }

            RefPtr<Node> newChild = target->cloneNode(true);

            // We don't walk the target tree element-by-element, and clone each element,
            // but instead use cloneNode(deep=true). This is an optimization for the common
            // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
            // Though if there are disallowed elements in the subtree, we have to remove them.
            // For instance: <use> on <g> containing <foreignObject> (indirect case).
            if (subtreeContainsDisallowedElement(newChild.get()))
                removeDisallowedElementsFromSubtree(newChild.get());

            SVGElement* newChildPtr = 0;
            if (newChild->isSVGElement())
                newChildPtr = static_cast<SVGElement*>(newChild.get());
            ASSERT(newChildPtr);

            cloneParent->appendChild(newChild.release(), ec);
            ASSERT(ec == 0);

            // Replace <use> with referenced content.
            ASSERT(use->parentNode()); 
            use->parentNode()->replaceChild(cloneParent.release(), use, ec);
            ASSERT(ec == 0);

            // Handle use referencing <svg> special case
            if (target->hasTagName(SVGNames::svgTag))
                alterShadowTreeForSVGTag(newChildPtr);

            // Immediately stop here, and restart expanding.
            expandUseElementsInShadowTree(m_shadowTreeRootElement.get());
            return;
        }
    }

    for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling())
        expandUseElementsInShadowTree(child.get());
}