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

KHTML

  • sources
  • kde-4.12
  • kdelibs
  • 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 
54 namespace WebCore {
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 0;
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  } 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 
116 void SVGUseElement::insertedIntoDocument()
117 {
118  SVGElement::insertedIntoDocument();
119  buildPendingResource();
120 }
121 
122 void SVGUseElement::removedFromDocument()
123 {
124  m_targetElementInstance = 0;
125  m_shadowTreeRootElement = 0;
126  SVGElement::removedFromDocument();
127 }
128 
129 void 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 
152 void 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 
169 void 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
205 void 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 
230 static 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 
246 static 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 
259 void 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 
379 RenderObject* SVGUseElement::createRenderer(RenderArena* arena, RenderStyle*)
380 {
381  return new (arena) RenderSVGTransformableContainer(this);
382 }
383 
384 void SVGUseElement::attach()
385 {
386  SVGStyledTransformableElement::attach();
387 
388  // If we're a pending resource, this doesn't have any effect.
389  attachShadowTree();
390 }
391 
392 void SVGUseElement::detach()
393 {
394  if (m_shadowTreeRootElement)
395  m_shadowTreeRootElement->detach();
396 
397  SVGStyledTransformableElement::detach();
398 }
399 
400 static 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 
411 Path 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 
431 void 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 
474 void 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 
512 void 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 
524 void 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 
542 void 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)
573 void 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 
664 void 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 
718 void 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 
745 void 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 
785 SVGElementInstance* SVGUseElement::instanceForShadowTreeElement(Node* element) const
786 {
787  return instanceForShadowTreeElement(element, m_targetElementInstance.get());
788 }
789 
790 SVGElementInstance* 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 
808 void 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)
WebCore::SVGNames::widthAttr
DOM::QualifiedName widthAttr
Definition: SVGNames.cpp:334
WebCore::SVGNames::useTag
DOM::QualifiedName useTag
Definition: SVGNames.cpp:99
WebCore::SVGNames::svgTag
DOM::QualifiedName svgTag
Definition: SVGNames.cpp:91
WebCore::SVGNames::polylineTag
DOM::QualifiedName polylineTag
Definition: SVGNames.cpp:85
WebCore::SVGNames::polygonTag
DOM::QualifiedName polygonTag
Definition: SVGNames.cpp:84
WebCore::SVGNames::transformAttr
DOM::QualifiedName transformAttr
Definition: SVGNames.cpp:312
WebCore::SVGNames::circleTag
DOM::QualifiedName circleTag
Definition: SVGNames.cpp:30
WebCore::SVGNames::ellipseTag
DOM::QualifiedName ellipseTag
Definition: SVGNames.cpp:37
SVGLength.h
SVGGElement.h
SVGSVGElement.h
WebCore::Path
khtml::Path Path
Definition: PathTraversalState.h:37
WebCore::SVGNames::rectTag
DOM::QualifiedName rectTag
Definition: SVGNames.cpp:87
WebCore::SVGNames::pathTag
DOM::QualifiedName pathTag
Definition: SVGNames.cpp:82
SVGPreserveAspectRatio.h
WebCore::SVGNames::yAttr
DOM::QualifiedName yAttr
Definition: SVGNames.cpp:343
SVGUseElement.h
WebCore::SVGNames::heightAttr
DOM::QualifiedName heightAttr
Definition: SVGNames.cpp:186
SVGNames.h
SVGElementInstanceList.h
SVGSymbolElement.h
WebCore::SVGNames::gTag
DOM::QualifiedName gTag
Definition: SVGNames.cpp:70
XLinkNames.h
WebCore::SVGNames::foreignObjectTag
DOM::QualifiedName foreignObjectTag
Definition: SVGNames.cpp:69
SVGElementInstance.h
WebCore::String
DOM::DOMString String
Definition: PlatformString.h:8
WebCore::SVGNames::xAttr
DOM::QualifiedName xAttr
Definition: SVGNames.cpp:338
WebCore::SVGNames::symbolTag
DOM::QualifiedName symbolTag
Definition: SVGNames.cpp:93
WebCore::SVGNames::textTag
DOM::QualifiedName textTag
Definition: SVGNames.cpp:94
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:51:22 by doxygen 1.8.7 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 API Reference

Skip menu "kdelibs 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
  • kjsembed
  •   WTF
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Nepomuk-Core
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver

Search



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