• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.14.38 API Reference
  • KDE Home
  • Contact Us
 

KHTML

  • khtml
  • svg
SVGUseElement.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4
5 This file is part of the KDE project
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
16
17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA.
21*/
22
23#include "config.h"
24#include "wtf/Platform.h"
25
26// Dump SVGElementInstance object tree - useful to debug instanceRoot problems
27// #define DUMP_INSTANCE_TREE
28
29// Dump the deep-expanded shadow tree (where the renderers are built from)
30// #define DUMP_SHADOW_TREE
31
32#if ENABLE(SVG)
33#include "SVGUseElement.h"
34
35#include "css/cssstyleselector.h"
36/*#include "CString.h"*/
37#include "Document.h"
38/*#include "Event.h"
39#include "HTMLNames.h"*/
40#include "RenderSVGTransformableContainer.h"
41#include "SVGElementInstance.h"
42#include "SVGElementInstanceList.h"
43#include "SVGGElement.h"
44#include "SVGLength.h"
45#include "SVGNames.h"
46#include "SVGPreserveAspectRatio.h"
47/*#include "SVGSMILElement.h"*/
48#include "SVGSVGElement.h"
49#include "SVGSymbolElement.h"
50#include "XLinkNames.h"
51/*#include "XMLSerializer.h"*/
52#include <wtf/OwnPtr.h>
53
54namespace WebCore {
55
56SVGUseElement::SVGUseElement(const QualifiedName& tagName, Document* doc)
57 : SVGStyledTransformableElement(tagName, doc)
58 , SVGTests()
59 , SVGLangSpace()
60 , SVGExternalResourcesRequired()
61 , SVGURIReference()
62 , m_x(this, LengthModeWidth)
63 , m_y(this, LengthModeHeight)
64 , m_width(this, LengthModeWidth)
65 , m_height(this, LengthModeHeight)
66{
67}
68
69SVGUseElement::~SVGUseElement()
70{
71}
72
73ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement, SVGLength, Length, length, X, x, SVGNames::xAttr, m_x)
74ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement, SVGLength, Length, length, Y, y, SVGNames::yAttr, m_y)
75ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement, SVGLength, Length, length, Width, width, SVGNames::widthAttr, m_width)
76ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement, SVGLength, Length, length, Height, height, SVGNames::heightAttr, m_height)
77
78SVGElementInstance* SVGUseElement::instanceRoot() const
79{
80 return m_targetElementInstance.get();
81}
82
83SVGElementInstance* SVGUseElement::animatedInstanceRoot() const
84{
85 // FIXME: Implement me.
86 return 0;
87}
88
89void SVGUseElement::parseMappedAttribute(MappedAttribute* attr)
90{
91 if (attr->name() == SVGNames::xAttr)
92 setXBaseValue(SVGLength(this, LengthModeWidth, attr->value()));
93 else if (attr->name() == SVGNames::yAttr)
94 setYBaseValue(SVGLength(this, LengthModeHeight, attr->value()));
95 else if (attr->name() == SVGNames::widthAttr) {
96 setWidthBaseValue(SVGLength(this, LengthModeWidth, attr->value()));
97 if (width().value() < 0.0)
98 document()->accessSVGExtensions()->reportError("A negative value for use attribute <width> is not allowed");
99 } else if (attr->name() == SVGNames::heightAttr) {
100 setHeightBaseValue(SVGLength(this, LengthModeHeight, attr->value()));
101 if (height().value() < 0.0)
102 document()->accessSVGExtensions()->reportError("A negative value for use attribute <height> is not allowed");
103 } else {
104 if (SVGTests::parseMappedAttribute(attr))
105 return;
106 if (SVGLangSpace::parseMappedAttribute(attr))
107 return;
108 if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
109 return;
110 if (SVGURIReference::parseMappedAttribute(attr))
111 return;
112 SVGStyledTransformableElement::parseMappedAttribute(attr);
113 }
114}
115
116void SVGUseElement::insertedIntoDocument()
117{
118 SVGElement::insertedIntoDocument();
119 buildPendingResource();
120}
121
122void SVGUseElement::removedFromDocument()
123{
124 m_targetElementInstance = 0;
125 m_shadowTreeRootElement = 0;
126 SVGElement::removedFromDocument();
127}
128
129void SVGUseElement::svgAttributeChanged(const QualifiedName& attrName)
130{
131 SVGStyledTransformableElement::svgAttributeChanged(attrName);
132
133 if (!attached())
134 return;
135
136 if (attrName == SVGNames::xAttr || attrName == SVGNames::yAttr ||
137 attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr ||
138 SVGTests::isKnownAttribute(attrName) ||
139 SVGLangSpace::isKnownAttribute(attrName) ||
140 SVGExternalResourcesRequired::isKnownAttribute(attrName) ||
141 SVGURIReference::isKnownAttribute(attrName) ||
142 SVGStyledTransformableElement::isKnownAttribute(attrName)) {
143 // TODO: Now that we're aware of the attribute name, we can finally optimize
144 // updating <use> attributes - to not reclone every time.
145 buildPendingResource();
146
147 if (m_shadowTreeRootElement)
148 m_shadowTreeRootElement->setChanged();
149 }
150}
151
152void SVGUseElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
153{
154 Q_UNUSED(changedByParser);
155 Q_UNUSED(beforeChange);
156 Q_UNUSED(afterChange);
157 Q_UNUSED(childCountDelta);
158 SVGElement::childrenChanged(/*changedByParser, beforeChange, afterChange, childCountDelta*/);
159
160 if (!attached())
161 return;
162
163 buildPendingResource();
164
165 if (m_shadowTreeRootElement)
166 m_shadowTreeRootElement->setChanged();
167}
168
169void SVGUseElement::recalcStyle(StyleChange change)
170{
171 SVGStyledElement::recalcStyle(change);
172
173 // The shadow tree root element is NOT a direct child element of us.
174 // So we have to take care it receives style updates, manually.
175 if (!m_shadowTreeRootElement || !m_shadowTreeRootElement->attached())
176 return;
177
178 // Mimic Element::recalcStyle(). The main difference is that we don't call attach() on the
179 // shadow tree root element, but call attachShadowTree() here. Calling attach() will crash
180 // as the shadow tree root element has no (direct) parent node. Yes, shadow trees are tricky.
181 if (change >= Inherit || m_shadowTreeRootElement->changed()) {
182 RenderStyle* newStyle = document()->styleSelector()->styleForElement(m_shadowTreeRootElement.get());
183 newStyle->ref();
184 StyleChange ch = m_shadowTreeRootElement->diff((m_shadowTreeRootElement->renderer() ? m_shadowTreeRootElement->renderer()->style() : 0)/*renderStyle()*/, newStyle);
185 if (ch == Detach) {
186 ASSERT(m_shadowTreeRootElement->attached());
187 m_shadowTreeRootElement->detach();
188 attachShadowTree();
189
190 // attach recalulates the style for all children. No need to do it twice.
191 m_shadowTreeRootElement->setChanged(false);
192 m_shadowTreeRootElement->setHasChangedChild(false);
193 newStyle->deref();
194 return;
195 }
196
197 newStyle->deref();
198 }
199
200 // Only change==Detach needs special treatment, for anything else recalcStyle() works.
201 m_shadowTreeRootElement->recalcStyle(change);
202}
203
204#ifdef DUMP_INSTANCE_TREE
205void dumpInstanceTree(unsigned int& depth, String& text, SVGElementInstance* targetInstance)
206{
207 SVGElement* element = targetInstance->correspondingElement();
208 ASSERT(element);
209
210 String elementId = element->getIDAttribute();
211 String elementNodeName = element->nodeName();
212 String parentNodeName = element->parentNode() ? element->parentNode()->nodeName() : "null";
213 String firstChildNodeName = element->firstChild() ? element->firstChild()->nodeName() : "null";
214
215 for (unsigned int i = 0; i < depth; ++i)
216 text += " ";
217
218 text += String::format("SVGElementInstance (parentNode=%s, firstChild=%s, correspondingElement=%s, id=%s)\n",
219 parentNodeName.latin1().data(), firstChildNodeName.latin1().data(), elementNodeName.latin1().data(), elementId.latin1().data());
220
221 depth++;
222
223 for (SVGElementInstance* instance = targetInstance->firstChild(); instance; instance = instance->nextSibling())
224 dumpInstanceTree(depth, text, instance);
225
226 depth--;
227}
228#endif
229
230static bool isDisallowedElement(Node* element)
231{
232 Q_UNUSED(element);
233#if ENABLE(SVG_FOREIGN_OBJECT)
234 // <foreignObject> should never be contained in a <use> tree. Too dangerous side effects possible.
235 if (element->hasTagName(SVGNames::foreignObjectTag))
236 return true;
237#endif
238#if ENABLE(SVG_ANIMATION)
239 if (SVGSMILElement::isSMILElement(element))
240 return true;
241#endif
242
243 return false;
244}
245
246static bool subtreeContainsDisallowedElement(Node* start)
247{
248 if (isDisallowedElement(start))
249 return true;
250
251 for (Node* cur = start->firstChild(); cur; cur = cur->nextSibling()) {
252 if (subtreeContainsDisallowedElement(cur))
253 return true;
254 }
255
256 return false;
257}
258
259void SVGUseElement::buildPendingResource()
260{
261 String id = SVGURIReference::getTarget(href());
262 Element* targetElement = document()->getElementById(id);
263
264 if (!targetElement) {
265 // TODO: We want to deregister as pending resource, if our href() changed!
266 // TODO: Move to svgAttributeChanged, once we're fixing use & the new dynamic update concept.
267 document()->accessSVGExtensions()->addPendingResource(id, this);
268 return;
269 }
270
271 // Do not build the shadow/instance tree for <use> elements living in a shadow tree.
272 // The will be expanded soon anyway - see expandUseElementsInShadowTree().
273 Node* parent = parentNode();
274 while (parent) {
275 if (parent->isShadowNode())
276 return;
277
278 parent = parent->parentNode();
279 }
280
281 SVGElement* target = 0;
282 if (targetElement && targetElement->isSVGElement())
283 target = static_cast<SVGElement*>(targetElement);
284
285 // Do not allow self-referencing.
286 // 'target' may be null, if it's a non SVG namespaced element.
287 if (!target || target == this) {
288 m_targetElementInstance = 0;
289 m_shadowTreeRootElement = 0;
290 return;
291 }
292
293 // Why a separated instance/shadow tree? SVG demands it:
294 // The instance tree is accesable from JavaScript, and has to
295 // expose a 1:1 copy of the referenced tree, whereas internally we need
296 // to alter the tree for correct "use-on-symbol", "use-on-svg" support.
297
298 // Build instance tree. Create root SVGElementInstance object for the first sub-tree node.
299 //
300 // Spec: If the 'use' element references a simple graphics element such as a 'rect', then there is only a
301 // single SVGElementInstance object, and the correspondingElement attribute on this SVGElementInstance object
302 // is the SVGRectElement that corresponds to the referenced 'rect' element.
303 m_targetElementInstance = new SVGElementInstance(this, target);
304
305 // Eventually enter recursion to build SVGElementInstance objects for the sub-tree children
306 bool foundProblem = false;
307 buildInstanceTree(target, m_targetElementInstance.get(), foundProblem);
308
309 // SVG specification does not say a word about <use> & cycles. My view on this is: just ignore it!
310 // Non-appearing <use> content is easier to debug, then half-appearing content.
311 if (foundProblem) {
312 m_targetElementInstance = 0;
313 m_shadowTreeRootElement = 0;
314 return;
315 }
316
317 // Assure instance tree building was successful
318 ASSERT(m_targetElementInstance);
319 ASSERT(m_targetElementInstance->correspondingUseElement() == this);
320
321 // Setup shadow tree root node
322 m_shadowTreeRootElement = new SVGGElement(SVGNames::gTag, document());
323 m_shadowTreeRootElement->setInDocument();
324 m_shadowTreeRootElement->setShadowParentNode(this);
325
326 // Spec: An additional transformation translate(x,y) is appended to the end
327 // (i.e., right-side) of the transform attribute on the generated 'g', where x
328 // and y represent the values of the x and y attributes on the 'use' element.
329 if (x().value() != 0.0 || y().value() != 0.0) {
330 String transformString = String::format("translate(%f, %f)", x().value(), y().value());
331 m_shadowTreeRootElement->setAttribute(SVGNames::transformAttr, transformString);
332 }
333
334 // Build shadow tree from instance tree
335 // This also handles the special cases: <use> on <symbol>, <use> on <svg>.
336 buildShadowTree(target, m_targetElementInstance.get());
337
338#if ENABLE(SVG) && ENABLE(SVG_USE)
339 // Expand all <use> elements in the shadow tree.
340 // Expand means: replace the actual <use> element by what it references.
341 expandUseElementsInShadowTree(m_shadowTreeRootElement.get());
342
343 // Expand all <symbol> elements in the shadow tree.
344 // Expand means: replace the actual <symbol> element by the <svg> element.
345 expandSymbolElementsInShadowTree(m_shadowTreeRootElement.get());
346
347#endif
348
349 // Now that the shadow tree is completely expanded, we can associate
350 // shadow tree elements <-> instances in the instance tree.
351 associateInstancesWithShadowTreeElements(m_shadowTreeRootElement->firstChild(), m_targetElementInstance.get());
352
353 // Eventually dump instance tree
354#ifdef DUMP_INSTANCE_TREE
355 String text;
356 unsigned int depth = 0;
357
358 dumpInstanceTree(depth, text, m_targetElementInstance.get());
359 fprintf(stderr, "\nDumping <use> instance tree:\n%s\n", text.latin1().data());
360#endif
361
362 // Eventually dump shadow tree
363#ifdef DUMP_SHADOW_TREE
364 ExceptionCode ec = 0;
365
366 PassRefPtr<XMLSerializer> serializer = XMLSerializer::create();
367
368 String markup = serializer->serializeToString(m_shadowTreeRootElement.get(), ec);
369 ASSERT(ec == 0);
370
371 fprintf(stderr, "Dumping <use> shadow tree markup:\n%s\n", markup.latin1().data());
372#endif
373
374 // The DOM side is setup properly. Now we have to attach the root shadow
375 // tree element manually - using attach() won't work for "shadow nodes".
376 attachShadowTree();
377}
378
379RenderObject* SVGUseElement::createRenderer(RenderArena* arena, RenderStyle*)
380{
381 return new (arena) RenderSVGTransformableContainer(this);
382}
383
384void SVGUseElement::attach()
385{
386 SVGStyledTransformableElement::attach();
387
388 // If we're a pending resource, this doesn't have any effect.
389 attachShadowTree();
390}
391
392void SVGUseElement::detach()
393{
394 if (m_shadowTreeRootElement)
395 m_shadowTreeRootElement->detach();
396
397 SVGStyledTransformableElement::detach();
398}
399
400static bool isDirectReference(Node* n)
401{
402 return n->hasTagName(SVGNames::pathTag) ||
403 n->hasTagName(SVGNames::rectTag) ||
404 n->hasTagName(SVGNames::circleTag) ||
405 n->hasTagName(SVGNames::ellipseTag) ||
406 n->hasTagName(SVGNames::polygonTag) ||
407 n->hasTagName(SVGNames::polylineTag) ||
408 n->hasTagName(SVGNames::textTag);
409}
410
411Path SVGUseElement::toClipPath() const
412{
413 if (!m_shadowTreeRootElement)
414 const_cast<SVGUseElement*>(this)->buildPendingResource();
415
416 if (!m_shadowTreeRootElement)
417 return Path();
418
419 Node* n = m_shadowTreeRootElement->firstChild();
420 if (n->isSVGElement() && static_cast<SVGElement*>(n)->isStyledTransformable()) {
421 if (!isDirectReference(n))
422 // Spec: Indirect references are an error (14.3.5)
423 document()->accessSVGExtensions()->reportError("Not allowed to use indirect reference in <clip-path>");
424 else
425 return static_cast<SVGStyledTransformableElement*>(n)->toClipPath();
426 }
427
428 return Path();
429}
430
431void SVGUseElement::buildInstanceTree(SVGElement* target, SVGElementInstance* targetInstance, bool& foundProblem)
432{
433 ASSERT(target);
434 ASSERT(targetInstance);
435
436 // A general description from the SVG spec, describing what buildInstanceTree() actually does.
437 //
438 // Spec: If the 'use' element references a 'g' which contains two 'rect' elements, then the instance tree
439 // contains three SVGElementInstance objects, a root SVGElementInstance object whose correspondingElement
440 // is the SVGGElement object for the 'g', and then two child SVGElementInstance objects, each of which has
441 // its correspondingElement that is an SVGRectElement object.
442
443 for (Node* node = target->firstChild(); node; node = node->nextSibling()) {
444 SVGElement* element = 0;
445 if (node->isSVGElement())
446 element = static_cast<SVGElement*>(node);
447
448 // Skip any non-svg nodes or any disallowed element.
449 if (!element || isDisallowedElement(element))
450 continue;
451
452 // Create SVGElementInstance object, for both container/non-container nodes.
453 SVGElementInstance* instancePtr = new SVGElementInstance(this, element);
454
455 RefPtr<SVGElementInstance> instance = instancePtr;
456 targetInstance->appendChild(instance.release());
457
458 // Enter recursion, appending new instance tree nodes to the "instance" object.
459 if (element->hasChildNodes())
460 buildInstanceTree(element, instancePtr, foundProblem);
461
462 // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced
463 // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree.
464 if (element->hasTagName(SVGNames::useTag))
465 handleDeepUseReferencing(element, instancePtr, foundProblem);
466 }
467
468 // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced
469 // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree.
470 if (target->hasTagName(SVGNames::useTag))
471 handleDeepUseReferencing(target, targetInstance, foundProblem);
472}
473
474void SVGUseElement::handleDeepUseReferencing(SVGElement* use, SVGElementInstance* targetInstance, bool& foundProblem)
475{
476 String id = SVGURIReference::getTarget(use->href());
477 Element* targetElement = document()->getElementById(id);
478 SVGElement* target = 0;
479 if (targetElement && targetElement->isSVGElement())
480 target = static_cast<SVGElement*>(targetElement);
481
482 if (!target)
483 return;
484
485 // Cycle detection first!
486 foundProblem = (target == this);
487
488 // Shortcut for self-references
489 if (foundProblem)
490 return;
491
492 SVGElementInstance* instance = targetInstance->parentNode();
493 while (instance) {
494 SVGElement* element = instance->correspondingElement();
495
496 if (element->getIDAttribute() == id) {
497 foundProblem = true;
498 return;
499 }
500
501 instance = instance->parentNode();
502 }
503
504 // Create an instance object, even if we're dealing with a cycle
505 SVGElementInstance* newInstance = new SVGElementInstance(this, target);
506 targetInstance->appendChild(newInstance);
507
508 // Eventually enter recursion to build SVGElementInstance objects for the sub-tree children
509 buildInstanceTree(target, newInstance, foundProblem);
510}
511
512void SVGUseElement::alterShadowTreeForSVGTag(SVGElement* target)
513{
514 String widthString = String::number(width().value());
515 String heightString = String::number(height().value());
516
517 if (hasAttribute(SVGNames::widthAttr))
518 target->setAttribute(SVGNames::widthAttr, widthString);
519
520 if (hasAttribute(SVGNames::heightAttr))
521 target->setAttribute(SVGNames::heightAttr, heightString);
522}
523
524void SVGUseElement::removeDisallowedElementsFromSubtree(Node* subtree)
525{
526 Q_UNUSED(subtree);
527 // Implement me: khtml, NodeImpl::traverseNextSibling
528 /*ASSERT(!subtree->inDocument());
529 ExceptionCode ec;
530 Node* node = subtree->firstChild();
531 while (node) {
532 if (isDisallowedElement(node)) {
533 Node* next = node->traverseNextSibling(subtree);
534 // The subtree is not in document so this won't generate events that could mutate the tree.
535 node->parent()->removeChild(node, ec);
536 node = next;
537 } else
538 node = node->traverseNextNode(subtree);
539 }*/
540}
541
542void SVGUseElement::buildShadowTree(SVGElement* target, SVGElementInstance* targetInstance)
543{
544 // For instance <use> on <foreignObject> (direct case).
545 if (isDisallowedElement(target))
546 return;
547
548 PassRefPtr<NodeImpl> newChild = targetInstance->correspondingElement()->cloneNode(true);
549
550 // We don't walk the target tree element-by-element, and clone each element,
551 // but instead use cloneNode(deep=true). This is an optimization for the common
552 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
553 // Though if there are disallowed elements in the subtree, we have to remove them.
554 // For instance: <use> on <g> containing <foreignObject> (indirect case).
555 if (subtreeContainsDisallowedElement(newChild.get()))
556 removeDisallowedElementsFromSubtree(newChild.get());
557
558 SVGElement* newChildPtr = 0;
559 if (newChild->isSVGElement())
560 newChildPtr = static_cast<SVGElement*>(newChild.get());
561 ASSERT(newChildPtr);
562
563 /*ExceptionCode*//*khtml*/int ec = 0;
564 m_shadowTreeRootElement->appendChild(newChild.releaseRef(), ec);
565 ASSERT(ec == 0);
566
567 // Handle use referencing <svg> special case
568 if (target->hasTagName(SVGNames::svgTag))
569 alterShadowTreeForSVGTag(newChildPtr);
570}
571
572#if ENABLE(SVG) && ENABLE(SVG_USE)
573void SVGUseElement::expandUseElementsInShadowTree(Node* element)
574{
575 // Why expand the <use> elements in the shadow tree here, and not just
576 // do this directly in buildShadowTree, if we encounter a <use> element?
577 //
578 // Short answer: Because we may miss to expand some elements. Ie. if a <symbol>
579 // contains <use> tags, we'd miss them. So once we're done with settin' up the
580 // actual shadow tree (after the special case modification for svg/symbol) we have
581 // to walk it completely and expand all <use> elements.
582 if (element->hasTagName(SVGNames::useTag)) {
583 SVGUseElement* use = static_cast<SVGUseElement*>(element);
584
585 String id = SVGURIReference::getTarget(use->href());
586 Element* targetElement = document()->getElementById(id);
587 SVGElement* target = 0;
588 if (targetElement && targetElement->isSVGElement())
589 target = static_cast<SVGElement*>(targetElement);
590
591 // Don't ASSERT(target) here, it may be "pending", too.
592 if (target) {
593 // Setup sub-shadow tree root node
594 RefPtr<SVGElement> cloneParent = new SVGGElement(SVGNames::gTag, document());
595
596 // Spec: In the generated content, the 'use' will be replaced by 'g', where all attributes from the
597 // 'use' element except for x, y, width, height and xlink:href are transferred to the generated 'g' element.
598 transferUseAttributesToReplacedElement(use, cloneParent.get());
599
600 // Spec: An additional transformation translate(x,y) is appended to the end
601 // (i.e., right-side) of the transform attribute on the generated 'g', where x
602 // and y represent the values of the x and y attributes on the 'use' element.
603 if (use->x().value() != 0.0 || use->y().value() != 0.0) {
604 if (!cloneParent->hasAttribute(SVGNames::transformAttr)) {
605 String transformString = String::format("translate(%f, %f)", use->x().value(), use->y().value());
606 cloneParent->setAttribute(SVGNames::transformAttr, transformString);
607 } else {
608 String transformString = String::format(" translate(%f, %f)", use->x().value(), use->y().value());
609 const AtomicString& transformAttribute = cloneParent->getAttribute(SVGNames::transformAttr);
610 cloneParent->setAttribute(SVGNames::transformAttr, transformAttribute + transformString);
611 }
612 }
613
614 ExceptionCode ec = 0;
615
616 // For instance <use> on <foreignObject> (direct case).
617 if (isDisallowedElement(target)) {
618 // We still have to setup the <use> replacment (<g>). Otherwhise
619 // associateInstancesWithShadowTreeElements() makes wrong assumptions.
620 // Replace <use> with referenced content.
621 ASSERT(use->parentNode());
622 use->parentNode()->replaceChild(cloneParent.release(), use, ec);
623 ASSERT(ec == 0);
624 return;
625 }
626
627 RefPtr<Node> newChild = target->cloneNode(true);
628
629 // We don't walk the target tree element-by-element, and clone each element,
630 // but instead use cloneNode(deep=true). This is an optimization for the common
631 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
632 // Though if there are disallowed elements in the subtree, we have to remove them.
633 // For instance: <use> on <g> containing <foreignObject> (indirect case).
634 if (subtreeContainsDisallowedElement(newChild.get()))
635 removeDisallowedElementsFromSubtree(newChild.get());
636
637 SVGElement* newChildPtr = 0;
638 if (newChild->isSVGElement())
639 newChildPtr = static_cast<SVGElement*>(newChild.get());
640 ASSERT(newChildPtr);
641
642 cloneParent->appendChild(newChild.release(), ec);
643 ASSERT(ec == 0);
644
645 // Replace <use> with referenced content.
646 ASSERT(use->parentNode());
647 use->parentNode()->replaceChild(cloneParent.release(), use, ec);
648 ASSERT(ec == 0);
649
650 // Handle use referencing <svg> special case
651 if (target->hasTagName(SVGNames::svgTag))
652 alterShadowTreeForSVGTag(newChildPtr);
653
654 // Immediately stop here, and restart expanding.
655 expandUseElementsInShadowTree(m_shadowTreeRootElement.get());
656 return;
657 }
658 }
659
660 for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling())
661 expandUseElementsInShadowTree(child.get());
662}
663
664void SVGUseElement::expandSymbolElementsInShadowTree(Node* element)
665{
666 if (element->hasTagName(SVGNames::symbolTag)) {
667 // Spec: The referenced 'symbol' and its contents are deep-cloned into the generated tree,
668 // with the exception that the 'symbol' is replaced by an 'svg'. This generated 'svg' will
669 // always have explicit values for attributes width and height. If attributes width and/or
670 // height are provided on the 'use' element, then these attributes will be transferred to
671 // the generated 'svg'. If attributes width and/or height are not specified, the generated
672 // 'svg' element will use values of 100% for these attributes.
673 RefPtr<SVGSVGElement> svgElement = new SVGSVGElement(SVGNames::svgTag, document());
674
675 // Transfer all attributes from <symbol> to the new <svg> element
676 svgElement->attributes()->setAttributes(*element->attributes());
677
678 // Explicitly re-set width/height values
679 String widthString = String::number(width().value());
680 String heightString = String::number(height().value());
681
682 svgElement->setAttribute(SVGNames::widthAttr, hasAttribute(SVGNames::widthAttr) ? widthString : "100%");
683 svgElement->setAttribute(SVGNames::heightAttr, hasAttribute(SVGNames::heightAttr) ? heightString : "100%");
684
685 ExceptionCode ec = 0;
686
687 // Only clone symbol children, and add them to the new <svg> element
688 for (Node* child = element->firstChild(); child; child = child->nextSibling()) {
689 RefPtr<Node> newChild = child->cloneNode(true);
690 svgElement->appendChild(newChild.release(), ec);
691 ASSERT(ec == 0);
692 }
693
694 // We don't walk the target tree element-by-element, and clone each element,
695 // but instead use cloneNode(deep=true). This is an optimization for the common
696 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
697 // Though if there are disallowed elements in the subtree, we have to remove them.
698 // For instance: <use> on <g> containing <foreignObject> (indirect case).
699 if (subtreeContainsDisallowedElement(svgElement.get()))
700 removeDisallowedElementsFromSubtree(svgElement.get());
701
702 // Replace <symbol> with <svg>.
703 ASSERT(element->parentNode());
704 element->parentNode()->replaceChild(svgElement.release(), element, ec);
705 ASSERT(ec == 0);
706
707 // Immediately stop here, and restart expanding.
708 expandSymbolElementsInShadowTree(m_shadowTreeRootElement.get());
709 return;
710 }
711
712 for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling())
713 expandSymbolElementsInShadowTree(child.get());
714}
715
716#endif
717
718void SVGUseElement::attachShadowTree()
719{
720 if (!m_shadowTreeRootElement || m_shadowTreeRootElement->attached() || /*khtml !document()->shouldCreateRenderers() ||*/ !attached() || !renderer())
721 return;
722
723 // Inspired by RenderTextControl::createSubtreeIfNeeded().
724 if (renderer()->childAllowed()/*canHaveChildren()*/ && childShouldCreateRenderer(m_shadowTreeRootElement.get())) {
725 RenderStyle* style = m_shadowTreeRootElement->styleForRenderer(renderer());
726 style->ref();
727
728 if (m_shadowTreeRootElement->rendererIsNeeded(style)) {
729 m_shadowTreeRootElement->setRenderer(m_shadowTreeRootElement->createRenderer(document()->renderArena(), style));
730 if (RenderObject* shadowRenderer = m_shadowTreeRootElement->renderer()) {
731 shadowRenderer->setStyle(style);
732 renderer()->addChild(shadowRenderer, m_shadowTreeRootElement->nextRenderer());
733 m_shadowTreeRootElement->setAttached();
734 }
735 }
736
737 style->deref();
738
739 // This will take care of attaching all shadow tree child nodes.
740 for (Node* child = m_shadowTreeRootElement->firstChild(); child; child = child->nextSibling())
741 child->attach();
742 }
743}
744
745void SVGUseElement::associateInstancesWithShadowTreeElements(Node* target, SVGElementInstance* targetInstance)
746{
747 if (!target || !targetInstance)
748 return;
749
750 SVGElement* originalElement = targetInstance->correspondingElement();
751
752 if (originalElement->hasTagName(SVGNames::useTag)) {
753#if ENABLE(SVG) && ENABLE(SVG_USE)
754 // <use> gets replaced by <g>
755 /*ASSERT(target->nodeName() == SVGNames::gTag);*/
756#else
757 /*ASSERT(target->nodeName() == SVGNames::gTag || target->nodeName() == SVGNames::useTag);*/
758#endif
759 } else if (originalElement->hasTagName(SVGNames::symbolTag)) {
760 // <symbol> gets replaced by <svg>
761#if ENABLE(SVG) && ENABLE(SVG_USE) && ENABLE(SVG_FOREIGN_OBJECT)
762 ASSERT(target->nodeName() == SVGNames::svgTag);
763#endif
764 } else
765 ASSERT(target->nodeName() == originalElement->nodeName());
766
767 SVGElement* element = 0;
768 if (target->isSVGElement())
769 element = static_cast<SVGElement*>(target);
770
771 ASSERT(!targetInstance->shadowTreeElement());
772 targetInstance->setShadowTreeElement(element);
773
774 Node* node = target->firstChild();
775 for (SVGElementInstance* instance = targetInstance->firstChild(); node && instance; instance = instance->nextSibling()) {
776 // Skip any non-svg elements in shadow tree
777 while (node && !node->isSVGElement())
778 node = node->nextSibling();
779
780 associateInstancesWithShadowTreeElements(node, instance);
781 node = node->nextSibling();
782 }
783}
784
785SVGElementInstance* SVGUseElement::instanceForShadowTreeElement(Node* element) const
786{
787 return instanceForShadowTreeElement(element, m_targetElementInstance.get());
788}
789
790SVGElementInstance* SVGUseElement::instanceForShadowTreeElement(Node* element, SVGElementInstance* instance) const
791{
792 ASSERT(element);
793 ASSERT(instance);
794 ASSERT(instance->shadowTreeElement());
795
796 if (element == instance->shadowTreeElement())
797 return instance;
798
799 for (SVGElementInstance* current = instance->firstChild(); current; current = current->nextSibling()) {
800 SVGElementInstance* search = instanceForShadowTreeElement(element, current);
801 if (search)
802 return search;
803 }
804
805 return 0;
806}
807
808void SVGUseElement::transferUseAttributesToReplacedElement(SVGElement* from, SVGElement* to) const
809{
810 Q_UNUSED(from);
811 Q_UNUSED(to);
812 // Implement me: khtml
813 /*ASSERT(from);
814 ASSERT(to);
815
816 to->attributes()->setAttributes(*from->attributes());
817
818 ExceptionCode ec = 0;
819
820 to->removeAttribute(SVGNames::xAttr, ec);
821 ASSERT(ec == 0);
822
823 to->removeAttribute(SVGNames::yAttr, ec);
824 ASSERT(ec == 0);
825
826 to->removeAttribute(SVGNames::widthAttr, ec);
827 ASSERT(ec == 0);
828
829 to->removeAttribute(SVGNames::heightAttr, ec);
830 ASSERT(ec == 0);
831
832 to->removeAttribute(XLinkNames::hrefAttr, ec);
833 ASSERT(ec == 0);*/
834}
835
836}
837
838#endif // ENABLE(SVG)
SVGElementInstanceList.h
SVGElementInstance.h
SVGGElement.h
SVGLength.h
SVGNames.h
SVGPreserveAspectRatio.h
SVGSVGElement.h
SVGSymbolElement.h
SVGUseElement.h
XLinkNames.h
WebCore
Definition: CSSHelper.h:7
WebCore::String
DOM::DOMString String
Definition: PlatformString.h:8
WebCore::Path
khtml::Path Path
Definition: PathTraversalState.h:37
X
khtml::ExceptionCode
unsigned short ExceptionCode
Definition: ExceptionCode.h:37
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Feb 20 2023 00:00:00 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KHTML

Skip menu "KHTML"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs-4.14.38 API Reference

Skip menu "kdelibs-4.14.38 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal