1 /*
2 * Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010 Rob Buis <buis@kde.org>
4 * Copyright (C) 2007 Apple Inc. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22 #include "config.h"
23
24 #include "core/svg/SVGSVGElement.h"
25
26 #include "HTMLNames.h"
27 #include "SVGNames.h"
28 #include "bindings/v8/ScriptEventListener.h"
29 #include "core/css/CSSHelper.h"
30 #include "core/dom/Document.h"
31 #include "core/dom/ElementTraversal.h"
32 #include "core/dom/NodeTraversal.h"
33 #include "core/dom/StaticNodeList.h"
34 #include "core/editing/FrameSelection.h"
35 #include "core/events/EventListener.h"
36 #include "core/events/ThreadLocalEventNames.h"
37 #include "core/frame/Frame.h"
38 #include "core/page/FrameTree.h"
39 #include "core/frame/FrameView.h"
40 #include "core/frame/UseCounter.h"
41 #include "core/rendering/RenderObject.h"
42 #include "core/rendering/RenderPart.h"
43 #include "core/rendering/svg/RenderSVGModelObject.h"
44 #include "core/rendering/svg/RenderSVGResource.h"
45 #include "core/rendering/svg/RenderSVGRoot.h"
46 #include "core/rendering/svg/RenderSVGViewportContainer.h"
47 #include "core/svg/SVGAngle.h"
48 #include "core/svg/SVGElementInstance.h"
49 #include "core/svg/SVGPreserveAspectRatio.h"
50 #include "core/svg/SVGTransform.h"
51 #include "core/svg/SVGTransformList.h"
52 #include "core/svg/SVGViewElement.h"
53 #include "core/svg/SVGViewSpec.h"
54 #include "core/svg/animation/SMILTimeContainer.h"
55 #include "platform/FloatConversion.h"
56 #include "platform/LengthFunctions.h"
57 #include "platform/geometry/FloatRect.h"
58 #include "platform/transforms/AffineTransform.h"
59 #include "wtf/StdLibExtras.h"
60
61 namespace WebCore {
62
63 // Animated property definitions
DEFINE_ANIMATED_LENGTH(SVGSVGElement,SVGNames::xAttr,X,x)64 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::xAttr, X, x)
65 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::yAttr, Y, y)
66 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::widthAttr, Width, width)
67 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::heightAttr, Height, height)
68 DEFINE_ANIMATED_BOOLEAN(SVGSVGElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
69 DEFINE_ANIMATED_PRESERVEASPECTRATIO(SVGSVGElement, SVGNames::preserveAspectRatioAttr, PreserveAspectRatio, preserveAspectRatio)
70 DEFINE_ANIMATED_RECT(SVGSVGElement, SVGNames::viewBoxAttr, ViewBox, viewBox)
71
72 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGSVGElement)
73 REGISTER_LOCAL_ANIMATED_PROPERTY(x)
74 REGISTER_LOCAL_ANIMATED_PROPERTY(y)
75 REGISTER_LOCAL_ANIMATED_PROPERTY(width)
76 REGISTER_LOCAL_ANIMATED_PROPERTY(height)
77 REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired)
78 REGISTER_LOCAL_ANIMATED_PROPERTY(viewBox)
79 REGISTER_LOCAL_ANIMATED_PROPERTY(preserveAspectRatio)
80 REGISTER_PARENT_ANIMATED_PROPERTIES(SVGGraphicsElement)
81 END_REGISTER_ANIMATED_PROPERTIES
82
83 inline SVGSVGElement::SVGSVGElement(Document& doc)
84 : SVGGraphicsElement(SVGNames::svgTag, doc)
85 , m_x(LengthModeWidth)
86 , m_y(LengthModeHeight)
87 , m_width(LengthModeWidth, "100%")
88 , m_height(LengthModeHeight, "100%")
89 , m_useCurrentView(false)
90 , m_zoomAndPan(SVGZoomAndPanMagnify)
91 , m_timeContainer(SMILTimeContainer::create(this))
92 , m_weakFactory(this)
93 {
94 ScriptWrappable::init(this);
95 registerAnimatedPropertiesForSVGSVGElement();
96
97 UseCounter::count(doc, UseCounter::SVGSVGElement);
98 }
99
create(Document & document)100 PassRefPtr<SVGSVGElement> SVGSVGElement::create(Document& document)
101 {
102 return adoptRef(new SVGSVGElement(document));
103 }
104
~SVGSVGElement()105 SVGSVGElement::~SVGSVGElement()
106 {
107 // There are cases where removedFromDocument() is not called.
108 // see ContainerNode::removeAllChildren, called by its destructor.
109 document().accessSVGExtensions()->removeTimeContainer(this);
110
111 ASSERT(inDocument() || !accessDocumentSVGExtensions()->isSVGRootWithRelativeLengthDescendents(this));
112 }
113
contentScriptType() const114 const AtomicString& SVGSVGElement::contentScriptType() const
115 {
116 DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/ecmascript", AtomicString::ConstructFromLiteral));
117 const AtomicString& n = fastGetAttribute(SVGNames::contentScriptTypeAttr);
118 return n.isNull() ? defaultValue : n;
119 }
120
setContentScriptType(const AtomicString & type)121 void SVGSVGElement::setContentScriptType(const AtomicString& type)
122 {
123 setAttribute(SVGNames::contentScriptTypeAttr, type);
124 }
125
contentStyleType() const126 const AtomicString& SVGSVGElement::contentStyleType() const
127 {
128 DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/css", AtomicString::ConstructFromLiteral));
129 const AtomicString& n = fastGetAttribute(SVGNames::contentStyleTypeAttr);
130 return n.isNull() ? defaultValue : n;
131 }
132
setContentStyleType(const AtomicString & type)133 void SVGSVGElement::setContentStyleType(const AtomicString& type)
134 {
135 setAttribute(SVGNames::contentStyleTypeAttr, type);
136 }
137
viewport() const138 SVGRect SVGSVGElement::viewport() const
139 {
140 // FIXME: This method doesn't follow the spec and is basically untested. Parent documents are not considered here.
141 // As we have no test coverage for this, we're going to disable it completly for now.
142 return SVGRect();
143 }
144
pixelUnitToMillimeterX() const145 float SVGSVGElement::pixelUnitToMillimeterX() const
146 {
147 return 1 / cssPixelsPerMillimeter;
148 }
149
pixelUnitToMillimeterY() const150 float SVGSVGElement::pixelUnitToMillimeterY() const
151 {
152 return 1 / cssPixelsPerMillimeter;
153 }
154
screenPixelToMillimeterX() const155 float SVGSVGElement::screenPixelToMillimeterX() const
156 {
157 return pixelUnitToMillimeterX();
158 }
159
screenPixelToMillimeterY() const160 float SVGSVGElement::screenPixelToMillimeterY() const
161 {
162 return pixelUnitToMillimeterY();
163 }
164
currentView()165 SVGViewSpec* SVGSVGElement::currentView()
166 {
167 if (!m_viewSpec)
168 m_viewSpec = SVGViewSpec::create(m_weakFactory.createWeakPtr());
169 return m_viewSpec.get();
170 }
171
currentScale() const172 float SVGSVGElement::currentScale() const
173 {
174 if (!inDocument() || !isOutermostSVGSVGElement())
175 return 1;
176
177 Frame* frame = document().frame();
178 if (!frame)
179 return 1;
180
181 const FrameTree& frameTree = frame->tree();
182
183 // The behaviour of currentScale() is undefined, when we're dealing with non-standalone SVG documents.
184 // If the svg is embedded, the scaling is handled by the host renderer, so when asking from inside
185 // the SVG document, a scale value of 1 seems reasonable, as it doesn't know anything about the parent scale.
186 return frameTree.parent() ? 1 : frame->pageZoomFactor();
187 }
188
setCurrentScale(float scale)189 void SVGSVGElement::setCurrentScale(float scale)
190 {
191 if (!inDocument() || !isOutermostSVGSVGElement())
192 return;
193
194 Frame* frame = document().frame();
195 if (!frame)
196 return;
197
198 const FrameTree& frameTree = frame->tree();
199
200 // The behaviour of setCurrentScale() is undefined, when we're dealing with non-standalone SVG documents.
201 // We choose the ignore this call, it's pretty useless to support calling setCurrentScale() from within
202 // an embedded SVG document, for the same reasons as in currentScale() - needs resolution by SVG WG.
203 if (frameTree.parent())
204 return;
205
206 frame->setPageZoomFactor(scale);
207 }
208
setCurrentTranslate(const FloatPoint & translation)209 void SVGSVGElement::setCurrentTranslate(const FloatPoint& translation)
210 {
211 m_translation = translation;
212 updateCurrentTranslate();
213 }
214
updateCurrentTranslate()215 void SVGSVGElement::updateCurrentTranslate()
216 {
217 if (RenderObject* object = renderer())
218 object->setNeedsLayout();
219
220 if (parentNode() == document() && document().renderer())
221 document().renderer()->repaint();
222 }
223
parseAttribute(const QualifiedName & name,const AtomicString & value)224 void SVGSVGElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
225 {
226 SVGParsingError parseError = NoError;
227
228 if (!nearestViewportElement()) {
229 bool setListener = true;
230
231 // Only handle events if we're the outermost <svg> element
232 if (name == HTMLNames::onunloadAttr)
233 document().setWindowAttributeEventListener(EventTypeNames::unload, createAttributeEventListener(document().frame(), name, value));
234 else if (name == HTMLNames::onresizeAttr)
235 document().setWindowAttributeEventListener(EventTypeNames::resize, createAttributeEventListener(document().frame(), name, value));
236 else if (name == HTMLNames::onscrollAttr)
237 document().setWindowAttributeEventListener(EventTypeNames::scroll, createAttributeEventListener(document().frame(), name, value));
238 else if (name == SVGNames::onzoomAttr)
239 document().setWindowAttributeEventListener(EventTypeNames::zoom, createAttributeEventListener(document().frame(), name, value));
240 else
241 setListener = false;
242
243 if (setListener)
244 return;
245 }
246
247 if (name == HTMLNames::onabortAttr)
248 document().setWindowAttributeEventListener(EventTypeNames::abort, createAttributeEventListener(document().frame(), name, value));
249 else if (name == HTMLNames::onerrorAttr)
250 document().setWindowAttributeEventListener(EventTypeNames::error, createAttributeEventListener(document().frame(), name, value));
251 else if (name == SVGNames::xAttr)
252 setXBaseValue(SVGLength::construct(LengthModeWidth, value, parseError));
253 else if (name == SVGNames::yAttr)
254 setYBaseValue(SVGLength::construct(LengthModeHeight, value, parseError));
255 else if (name == SVGNames::widthAttr)
256 setWidthBaseValue(SVGLength::construct(LengthModeWidth, value, parseError, ForbidNegativeLengths));
257 else if (name == SVGNames::heightAttr)
258 setHeightBaseValue(SVGLength::construct(LengthModeHeight, value, parseError, ForbidNegativeLengths));
259 else if (SVGExternalResourcesRequired::parseAttribute(name, value)
260 || SVGFitToViewBox::parseAttribute(this, name, value)
261 || SVGZoomAndPan::parseAttribute(this, name, value)) {
262 } else
263 SVGGraphicsElement::parseAttribute(name, value);
264
265 reportAttributeParsingError(parseError, name, value);
266 }
267
svgAttributeChanged(const QualifiedName & attrName)268 void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName)
269 {
270 bool updateRelativeLengthsOrViewBox = false;
271 bool widthChanged = attrName == SVGNames::widthAttr;
272 if (widthChanged
273 || attrName == SVGNames::heightAttr
274 || attrName == SVGNames::xAttr
275 || attrName == SVGNames::yAttr) {
276 updateRelativeLengthsOrViewBox = true;
277 updateRelativeLengthsInformation();
278 invalidateRelativeLengthClients();
279
280 // At the SVG/HTML boundary (aka RenderSVGRoot), the width attribute can
281 // affect the replaced size so we need to mark it for updating.
282 if (widthChanged) {
283 RenderObject* renderObject = renderer();
284 if (renderObject && renderObject->isSVGRoot())
285 toRenderSVGRoot(renderObject)->setNeedsLayoutAndPrefWidthsRecalc();
286 }
287 }
288
289 if (SVGFitToViewBox::isKnownAttribute(attrName)) {
290 updateRelativeLengthsOrViewBox = true;
291 if (RenderObject* object = renderer())
292 object->setNeedsTransformUpdate();
293 }
294
295 SVGElementInstance::InvalidationGuard invalidationGuard(this);
296
297 if (updateRelativeLengthsOrViewBox
298 || SVGExternalResourcesRequired::isKnownAttribute(attrName)
299 || SVGZoomAndPan::isKnownAttribute(attrName)) {
300 if (renderer())
301 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer());
302 return;
303 }
304
305 SVGGraphicsElement::svgAttributeChanged(attrName);
306 }
307
suspendRedraw(unsigned)308 unsigned SVGSVGElement::suspendRedraw(unsigned /* maxWaitMilliseconds */)
309 {
310 // FIXME: Implement me (see bug 11275)
311 return 0;
312 }
313
unsuspendRedraw(unsigned)314 void SVGSVGElement::unsuspendRedraw(unsigned /* suspendHandleId */)
315 {
316 // FIXME: Implement me (see bug 11275)
317 }
318
unsuspendRedrawAll()319 void SVGSVGElement::unsuspendRedrawAll()
320 {
321 // FIXME: Implement me (see bug 11275)
322 }
323
forceRedraw()324 void SVGSVGElement::forceRedraw()
325 {
326 // FIXME: Implement me (see bug 11275)
327 }
328
collectIntersectionOrEnclosureList(const SVGRect & rect,SVGElement * referenceElement,CollectIntersectionOrEnclosure collect) const329 PassRefPtr<NodeList> SVGSVGElement::collectIntersectionOrEnclosureList(const SVGRect& rect, SVGElement* referenceElement, CollectIntersectionOrEnclosure collect) const
330 {
331 Vector<RefPtr<Node> > nodes;
332 Element* element = ElementTraversal::next(*(referenceElement ? referenceElement : this));
333 while (element) {
334 if (element->isSVGElement()) {
335 SVGElement* svgElement = toSVGElement(element);
336 if (collect == CollectIntersectionList) {
337 if (checkIntersection(svgElement, rect))
338 nodes.append(element);
339 } else {
340 if (checkEnclosure(svgElement, rect))
341 nodes.append(element);
342 }
343 }
344
345 element = ElementTraversal::next(*element, referenceElement ? referenceElement : this);
346 }
347 return StaticNodeList::adopt(nodes);
348 }
349
getIntersectionList(const SVGRect & rect,SVGElement * referenceElement) const350 PassRefPtr<NodeList> SVGSVGElement::getIntersectionList(const SVGRect& rect, SVGElement* referenceElement) const
351 {
352 return collectIntersectionOrEnclosureList(rect, referenceElement, CollectIntersectionList);
353 }
354
getEnclosureList(const SVGRect & rect,SVGElement * referenceElement) const355 PassRefPtr<NodeList> SVGSVGElement::getEnclosureList(const SVGRect& rect, SVGElement* referenceElement) const
356 {
357 return collectIntersectionOrEnclosureList(rect, referenceElement, CollectEnclosureList);
358 }
359
checkIntersection(SVGElement * element,const SVGRect & rect) const360 bool SVGSVGElement::checkIntersection(SVGElement* element, const SVGRect& rect) const
361 {
362 if (!element)
363 return false;
364 return RenderSVGModelObject::checkIntersection(element->renderer(), rect);
365 }
366
checkEnclosure(SVGElement * element,const SVGRect & rect) const367 bool SVGSVGElement::checkEnclosure(SVGElement* element, const SVGRect& rect) const
368 {
369 if (!element)
370 return false;
371 return RenderSVGModelObject::checkEnclosure(element->renderer(), rect);
372 }
373
deselectAll()374 void SVGSVGElement::deselectAll()
375 {
376 if (Frame* frame = document().frame())
377 frame->selection().clear();
378 }
379
createSVGNumber()380 float SVGSVGElement::createSVGNumber()
381 {
382 return 0.0f;
383 }
384
createSVGLength()385 SVGLength SVGSVGElement::createSVGLength()
386 {
387 return SVGLength();
388 }
389
createSVGAngle()390 SVGAngle SVGSVGElement::createSVGAngle()
391 {
392 return SVGAngle();
393 }
394
createSVGPoint()395 SVGPoint SVGSVGElement::createSVGPoint()
396 {
397 return SVGPoint();
398 }
399
createSVGMatrix()400 SVGMatrix SVGSVGElement::createSVGMatrix()
401 {
402 return SVGMatrix();
403 }
404
createSVGRect()405 SVGRect SVGSVGElement::createSVGRect()
406 {
407 return SVGRect();
408 }
409
createSVGTransform()410 SVGTransform SVGSVGElement::createSVGTransform()
411 {
412 return SVGTransform(SVGTransform::SVG_TRANSFORM_MATRIX);
413 }
414
createSVGTransformFromMatrix(const SVGMatrix & matrix)415 SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const SVGMatrix& matrix)
416 {
417 return SVGTransform(static_cast<const AffineTransform&>(matrix));
418 }
419
localCoordinateSpaceTransform(SVGElement::CTMScope mode) const420 AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGElement::CTMScope mode) const
421 {
422 AffineTransform viewBoxTransform;
423 if (!hasEmptyViewBox()) {
424 FloatSize size = currentViewportSize();
425 viewBoxTransform = viewBoxToViewTransform(size.width(), size.height());
426 }
427
428 AffineTransform transform;
429 if (!isOutermostSVGSVGElement()) {
430 SVGLengthContext lengthContext(this);
431 transform.translate(xCurrentValue().value(lengthContext), yCurrentValue().value(lengthContext));
432 } else if (mode == SVGElement::ScreenScope) {
433 if (RenderObject* renderer = this->renderer()) {
434 FloatPoint location;
435 float zoomFactor = 1;
436
437 // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform
438 // to map an element from SVG viewport coordinates to CSS box coordinates.
439 // RenderSVGRoot's localToAbsolute method expects CSS box coordinates.
440 // We also need to adjust for the zoom level factored into CSS coordinates (bug #96361).
441 if (renderer->isSVGRoot()) {
442 location = toRenderSVGRoot(renderer)->localToBorderBoxTransform().mapPoint(location);
443 zoomFactor = 1 / renderer->style()->effectiveZoom();
444 }
445
446 // Translate in our CSS parent coordinate space
447 // FIXME: This doesn't work correctly with CSS transforms.
448 location = renderer->localToAbsolute(location, UseTransforms);
449 location.scale(zoomFactor, zoomFactor);
450
451 // Be careful here! localToBorderBoxTransform() included the x/y offset coming from the viewBoxToViewTransform(),
452 // so we have to subtract it here (original cause of bug #27183)
453 transform.translate(location.x() - viewBoxTransform.e(), location.y() - viewBoxTransform.f());
454
455 // Respect scroll offset.
456 if (FrameView* view = document().view()) {
457 LayoutSize scrollOffset = view->scrollOffset();
458 scrollOffset.scale(zoomFactor);
459 transform.translate(-scrollOffset.width(), -scrollOffset.height());
460 }
461 }
462 }
463
464 return transform.multiply(viewBoxTransform);
465 }
466
rendererIsNeeded(const RenderStyle & style)467 bool SVGSVGElement::rendererIsNeeded(const RenderStyle& style)
468 {
469 // FIXME: We should respect display: none on the documentElement svg element
470 // but many things in FrameView and SVGImage depend on the RenderSVGRoot when
471 // they should instead depend on the RenderView.
472 // https://bugs.webkit.org/show_bug.cgi?id=103493
473 if (document().documentElement() == this)
474 return true;
475 return Element::rendererIsNeeded(style);
476 }
477
createRenderer(RenderStyle *)478 RenderObject* SVGSVGElement::createRenderer(RenderStyle*)
479 {
480 if (isOutermostSVGSVGElement())
481 return new RenderSVGRoot(this);
482
483 return new RenderSVGViewportContainer(this);
484 }
485
insertedInto(ContainerNode * rootParent)486 Node::InsertionNotificationRequest SVGSVGElement::insertedInto(ContainerNode* rootParent)
487 {
488 if (rootParent->inDocument()) {
489 document().accessSVGExtensions()->addTimeContainer(this);
490
491 // Animations are started at the end of document parsing and after firing the load event,
492 // but if we miss that train (deferred programmatic element insertion for example) we need
493 // to initialize the time container here.
494 if (!document().parsing() && !document().processingLoadEvent() && document().loadEventFinished() && !timeContainer()->isStarted())
495 timeContainer()->begin();
496 }
497 return SVGGraphicsElement::insertedInto(rootParent);
498 }
499
removedFrom(ContainerNode * rootParent)500 void SVGSVGElement::removedFrom(ContainerNode* rootParent)
501 {
502 if (rootParent->inDocument()) {
503 SVGDocumentExtensions* svgExtensions = document().accessSVGExtensions();
504 svgExtensions->removeTimeContainer(this);
505 svgExtensions->removeSVGRootWithRelativeLengthDescendents(this);
506 }
507
508 SVGGraphicsElement::removedFrom(rootParent);
509 }
510
pauseAnimations()511 void SVGSVGElement::pauseAnimations()
512 {
513 if (!m_timeContainer->isPaused())
514 m_timeContainer->pause();
515 }
516
unpauseAnimations()517 void SVGSVGElement::unpauseAnimations()
518 {
519 if (m_timeContainer->isPaused())
520 m_timeContainer->resume();
521 }
522
animationsPaused() const523 bool SVGSVGElement::animationsPaused() const
524 {
525 return m_timeContainer->isPaused();
526 }
527
getCurrentTime() const528 float SVGSVGElement::getCurrentTime() const
529 {
530 return narrowPrecisionToFloat(m_timeContainer->elapsed().value());
531 }
532
setCurrentTime(float seconds)533 void SVGSVGElement::setCurrentTime(float seconds)
534 {
535 if (std::isnan(seconds))
536 return;
537 seconds = max(seconds, 0.0f);
538 m_timeContainer->setElapsed(seconds);
539 }
540
selfHasRelativeLengths() const541 bool SVGSVGElement::selfHasRelativeLengths() const
542 {
543 return xCurrentValue().isRelative()
544 || yCurrentValue().isRelative()
545 || widthCurrentValue().isRelative()
546 || heightCurrentValue().isRelative()
547 || hasAttribute(SVGNames::viewBoxAttr);
548 }
549
currentViewBoxRect() const550 SVGRect SVGSVGElement::currentViewBoxRect() const
551 {
552 if (m_useCurrentView)
553 return m_viewSpec ? m_viewSpec->viewBoxCurrentValue() : SVGRect();
554
555 FloatRect useViewBox = viewBoxCurrentValue();
556 if (!useViewBox.isEmpty())
557 return useViewBox;
558 if (!renderer() || !renderer()->isSVGRoot())
559 return SVGRect();
560 if (!toRenderSVGRoot(renderer())->isEmbeddedThroughSVGImage())
561 return SVGRect();
562
563 Length intrinsicWidth = this->intrinsicWidth();
564 Length intrinsicHeight = this->intrinsicHeight();
565 if (!intrinsicWidth.isFixed() || !intrinsicHeight.isFixed())
566 return SVGRect();
567
568 // If no viewBox is specified but non-relative width/height values, then we
569 // should always synthesize a viewBox if we're embedded through a SVGImage.
570 return SVGRect(FloatPoint(), FloatSize(floatValueForLength(intrinsicWidth, 0, 0), floatValueForLength(intrinsicHeight, 0, 0)));
571 }
572
currentViewportSize() const573 FloatSize SVGSVGElement::currentViewportSize() const
574 {
575 Length intrinsicWidth = this->intrinsicWidth();
576 Length intrinsicHeight = this->intrinsicHeight();
577 if (intrinsicWidth.isFixed() && intrinsicHeight.isFixed())
578 return FloatSize(floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0));
579
580 if (!renderer())
581 return FloatSize();
582
583 if (renderer()->isSVGRoot()) {
584 LayoutRect contentBoxRect = toRenderSVGRoot(renderer())->contentBoxRect();
585 return FloatSize(contentBoxRect.width() / renderer()->style()->effectiveZoom(), contentBoxRect.height() / renderer()->style()->effectiveZoom());
586 }
587
588 FloatRect viewportRect = toRenderSVGViewportContainer(renderer())->viewport();
589 return FloatSize(viewportRect.width(), viewportRect.height());
590 }
591
widthAttributeEstablishesViewport() const592 bool SVGSVGElement::widthAttributeEstablishesViewport() const
593 {
594 if (!renderer() || renderer()->isSVGViewportContainer())
595 return true;
596
597 // Spec: http://www.w3.org/TR/SVG/coords.html#ViewportSpace
598 // The ‘width’ attribute on the outermost svg element establishes the viewport's width, unless the following conditions are met:
599 // - the SVG content is a separately stored resource that is embedded by reference (such as the ‘object’ element in XHTML [XHTML]), or
600 // the SVG content is embedded inline within a containing document;
601 // - and the referencing element or containing document is styled using CSS [CSS2] or XSL [XSL];
602 // - and there are CSS-compatible positioning properties ([CSS2], section 9.3) specified on the referencing element (e.g., the ‘object’ element)
603 // or on the containing document's outermost svg element that are sufficient to establish the width of the viewport. Under these conditions,
604 // the positioning properties establish the viewport's width.
605 RenderSVGRoot* root = toRenderSVGRoot(renderer());
606
607 // SVG embedded through object/embed/iframe.
608 if (root->isEmbeddedThroughFrameContainingSVGDocument())
609 return !root->hasReplacedLogicalWidth() && !document().frame()->ownerRenderer()->hasReplacedLogicalWidth();
610
611 // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG.
612 if (root->isEmbeddedThroughSVGImage() || document().documentElement() != this)
613 return !root->hasReplacedLogicalWidth();
614
615 return true;
616 }
617
heightAttributeEstablishesViewport() const618 bool SVGSVGElement::heightAttributeEstablishesViewport() const
619 {
620 if (!renderer() || renderer()->isSVGViewportContainer())
621 return true;
622
623 // Spec: http://www.w3.org/TR/SVG/coords.html#IntrinsicSizing
624 // Similarly, if there are positioning properties specified on the referencing element or on the outermost svg element
625 // that are sufficient to establish the height of the viewport, then these positioning properties establish the viewport's
626 // height; otherwise, the ‘height’ attribute on the outermost svg element establishes the viewport's height.
627 RenderSVGRoot* root = toRenderSVGRoot(renderer());
628
629 // SVG embedded through object/embed/iframe.
630 if (root->isEmbeddedThroughFrameContainingSVGDocument())
631 return !root->hasReplacedLogicalHeight() && !document().frame()->ownerRenderer()->hasReplacedLogicalHeight();
632
633 // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG.
634 if (root->isEmbeddedThroughSVGImage() || document().documentElement() != this)
635 return !root->hasReplacedLogicalHeight();
636
637 return true;
638 }
639
intrinsicWidth(ConsiderCSSMode mode) const640 Length SVGSVGElement::intrinsicWidth(ConsiderCSSMode mode) const
641 {
642 if (widthAttributeEstablishesViewport() || mode == IgnoreCSSProperties) {
643 if (widthCurrentValue().unitType() == LengthTypePercentage)
644 return Length(widthCurrentValue().valueAsPercentage() * 100, Percent);
645
646 SVGLengthContext lengthContext(this);
647 return Length(widthCurrentValue().value(lengthContext), Fixed);
648 }
649
650 ASSERT(renderer());
651 return renderer()->style()->width();
652 }
653
intrinsicHeight(ConsiderCSSMode mode) const654 Length SVGSVGElement::intrinsicHeight(ConsiderCSSMode mode) const
655 {
656 if (heightAttributeEstablishesViewport() || mode == IgnoreCSSProperties) {
657 if (heightCurrentValue().unitType() == LengthTypePercentage)
658 return Length(heightCurrentValue().valueAsPercentage() * 100, Percent);
659
660 SVGLengthContext lengthContext(this);
661 return Length(heightCurrentValue().value(lengthContext), Fixed);
662 }
663
664 ASSERT(renderer());
665 return renderer()->style()->height();
666 }
667
viewBoxToViewTransform(float viewWidth,float viewHeight) const668 AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const
669 {
670 if (!m_useCurrentView || !m_viewSpec)
671 return SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), preserveAspectRatioCurrentValue(), viewWidth, viewHeight);
672
673 AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), m_viewSpec->preserveAspectRatioCurrentValue(), viewWidth, viewHeight);
674 const SVGTransformList& transformList = m_viewSpec->transformBaseValue();
675 if (transformList.isEmpty())
676 return ctm;
677
678 AffineTransform transform;
679 if (transformList.concatenate(transform))
680 ctm *= transform;
681
682 return ctm;
683 }
684
setupInitialView(const String & fragmentIdentifier,Element * anchorNode)685 void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* anchorNode)
686 {
687 RenderObject* renderer = this->renderer();
688 SVGViewSpec* view = m_viewSpec.get();
689 if (view)
690 view->reset();
691
692 bool hadUseCurrentView = m_useCurrentView;
693 m_useCurrentView = false;
694
695 if (fragmentIdentifier.startsWith("xpointer(")) {
696 // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491)
697 if (renderer && hadUseCurrentView)
698 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
699 return;
700 }
701
702 if (fragmentIdentifier.startsWith("svgView(")) {
703 if (!view)
704 view = currentView(); // Create the SVGViewSpec.
705
706 if (view->parseViewSpec(fragmentIdentifier))
707 m_useCurrentView = true;
708 else
709 view->reset();
710
711 if (renderer && (hadUseCurrentView || m_useCurrentView))
712 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
713 return;
714 }
715
716 // Spec: If the SVG fragment identifier addresses a ‘view’ element within an SVG document (e.g., MyDrawing.svg#MyView
717 // or MyDrawing.svg#xpointer(id('MyView'))) then the closest ancestor ‘svg’ element is displayed in the viewport.
718 // Any view specification attributes included on the given ‘view’ element override the corresponding view specification
719 // attributes on the closest ancestor ‘svg’ element.
720 if (anchorNode && anchorNode->hasTagName(SVGNames::viewTag)) {
721 SVGViewElement* viewElement = toSVGViewElement(anchorNode);
722 if (!viewElement)
723 return;
724
725 if (SVGSVGElement* svg = viewElement->ownerSVGElement()) {
726 svg->inheritViewAttributes(viewElement);
727
728 if (RenderObject* renderer = svg->renderer())
729 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
730 }
731 }
732
733 // FIXME: We need to decide which <svg> to focus on, and zoom to it.
734 // FIXME: We need to actually "highlight" the viewTarget(s).
735 }
736
inheritViewAttributes(SVGViewElement * viewElement)737 void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement)
738 {
739 SVGViewSpec* view = currentView();
740 m_useCurrentView = true;
741
742 if (viewElement->hasAttribute(SVGNames::viewBoxAttr))
743 view->setViewBoxBaseValue(viewElement->viewBoxCurrentValue());
744 else
745 view->setViewBoxBaseValue(viewBoxCurrentValue());
746
747 if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr))
748 view->setPreserveAspectRatioBaseValue(viewElement->preserveAspectRatioBaseValue());
749 else
750 view->setPreserveAspectRatioBaseValue(preserveAspectRatioBaseValue());
751
752 if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr))
753 view->setZoomAndPanBaseValue(viewElement->zoomAndPan());
754 else
755 view->setZoomAndPanBaseValue(zoomAndPan());
756 }
757
758 // getElementById on SVGSVGElement is restricted to only the child subtree defined by the <svg> element.
759 // See http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGSVGElement
getElementById(const AtomicString & id) const760 Element* SVGSVGElement::getElementById(const AtomicString& id) const
761 {
762 Element* element = treeScope().getElementById(id);
763 if (element && element->isDescendantOf(this))
764 return element;
765
766 // Fall back to traversing our subtree. Duplicate ids are allowed, the first found will
767 // be returned.
768 for (Node* node = firstChild(); node; node = NodeTraversal::next(*node, this)) {
769 if (!node->isElementNode())
770 continue;
771
772 Element* element = toElement(node);
773 if (element->getIdAttribute() == id)
774 return element;
775 }
776 return 0;
777 }
778
779 }
780