KHtml

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

KDE's Doxygen guidelines are available online.