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 * Copyright (C) 2014 Google, Inc.
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
25 #include "core/svg/SVGSVGElement.h"
26
27 #include "bindings/core/v8/ScriptEventListener.h"
28 #include "core/HTMLNames.h"
29 #include "core/SVGNames.h"
30 #include "core/css/CSSHelper.h"
31 #include "core/dom/Document.h"
32 #include "core/dom/ElementTraversal.h"
33 #include "core/dom/StaticNodeList.h"
34 #include "core/editing/FrameSelection.h"
35 #include "core/events/EventListener.h"
36 #include "core/frame/LocalFrame.h"
37 #include "core/page/FrameTree.h"
38 #include "core/frame/FrameView.h"
39 #include "core/frame/UseCounter.h"
40 #include "core/rendering/RenderObject.h"
41 #include "core/rendering/RenderPart.h"
42 #include "core/rendering/svg/RenderSVGModelObject.h"
43 #include "core/rendering/svg/RenderSVGResource.h"
44 #include "core/rendering/svg/RenderSVGRoot.h"
45 #include "core/rendering/svg/RenderSVGViewportContainer.h"
46 #include "core/svg/SVGAngleTearOff.h"
47 #include "core/svg/SVGNumberTearOff.h"
48 #include "core/svg/SVGPreserveAspectRatio.h"
49 #include "core/svg/SVGRectTearOff.h"
50 #include "core/svg/SVGTransform.h"
51 #include "core/svg/SVGTransformList.h"
52 #include "core/svg/SVGTransformTearOff.h"
53 #include "core/svg/SVGViewElement.h"
54 #include "core/svg/SVGViewSpec.h"
55 #include "core/svg/animation/SMILTimeContainer.h"
56 #include "platform/FloatConversion.h"
57 #include "platform/LengthFunctions.h"
58 #include "platform/geometry/FloatRect.h"
59 #include "platform/transforms/AffineTransform.h"
60 #include "wtf/StdLibExtras.h"
61
62 namespace blink {
63
SVGSVGElement(Document & doc)64 inline SVGSVGElement::SVGSVGElement(Document& doc)
65 : SVGGraphicsElement(SVGNames::svgTag, doc)
66 , SVGFitToViewBox(this)
67 , m_x(SVGAnimatedLength::create(this, SVGNames::xAttr, SVGLength::create(LengthModeWidth), AllowNegativeLengths))
68 , m_y(SVGAnimatedLength::create(this, SVGNames::yAttr, SVGLength::create(LengthModeHeight), AllowNegativeLengths))
69 , m_width(SVGAnimatedLength::create(this, SVGNames::widthAttr, SVGLength::create(LengthModeWidth), ForbidNegativeLengths))
70 , m_height(SVGAnimatedLength::create(this, SVGNames::heightAttr, SVGLength::create(LengthModeHeight), ForbidNegativeLengths))
71 , m_useCurrentView(false)
72 , m_timeContainer(SMILTimeContainer::create(*this))
73 , m_translation(SVGPoint::create())
74 {
75 m_width->setDefaultValueAsString("100%");
76 m_height->setDefaultValueAsString("100%");
77
78 addToPropertyMap(m_x);
79 addToPropertyMap(m_y);
80 addToPropertyMap(m_width);
81 addToPropertyMap(m_height);
82
83 UseCounter::count(doc, UseCounter::SVGSVGElement);
84 }
85
DEFINE_NODE_FACTORY(SVGSVGElement)86 DEFINE_NODE_FACTORY(SVGSVGElement)
87
88 SVGSVGElement::~SVGSVGElement()
89 {
90 #if !ENABLE(OILPAN)
91 if (m_viewSpec)
92 m_viewSpec->detachContextElement();
93
94 // There are cases where removedFromDocument() is not called.
95 // see ContainerNode::removeAllChildren, called by its destructor.
96 // With Oilpan, either removedFrom is called or the document
97 // is dead as well and there is no reason to clear the extensions.
98 document().accessSVGExtensions().removeTimeContainer(this);
99
100 ASSERT(inDocument() || !accessDocumentSVGExtensions().isSVGRootWithRelativeLengthDescendents(this));
101 #endif
102 }
103
viewport() const104 PassRefPtr<SVGRectTearOff> SVGSVGElement::viewport() const
105 {
106 // FIXME: This method doesn't follow the spec and is basically untested. Parent documents are not considered here.
107 // As we have no test coverage for this, we're going to disable it completly for now.
108 return SVGRectTearOff::create(SVGRect::create(), 0, PropertyIsNotAnimVal);
109 }
110
pixelUnitToMillimeterX() const111 float SVGSVGElement::pixelUnitToMillimeterX() const
112 {
113 return 1 / cssPixelsPerMillimeter;
114 }
115
pixelUnitToMillimeterY() const116 float SVGSVGElement::pixelUnitToMillimeterY() const
117 {
118 return 1 / cssPixelsPerMillimeter;
119 }
120
screenPixelToMillimeterX() const121 float SVGSVGElement::screenPixelToMillimeterX() const
122 {
123 return pixelUnitToMillimeterX();
124 }
125
screenPixelToMillimeterY() const126 float SVGSVGElement::screenPixelToMillimeterY() const
127 {
128 return pixelUnitToMillimeterY();
129 }
130
currentView()131 SVGViewSpec* SVGSVGElement::currentView()
132 {
133 if (!m_viewSpec)
134 m_viewSpec = SVGViewSpec::create(this);
135 return m_viewSpec.get();
136 }
137
currentScale() const138 float SVGSVGElement::currentScale() const
139 {
140 if (!inDocument() || !isOutermostSVGSVGElement())
141 return 1;
142
143 LocalFrame* frame = document().frame();
144 if (!frame)
145 return 1;
146
147 const FrameTree& frameTree = frame->tree();
148
149 // The behaviour of currentScale() is undefined, when we're dealing with non-standalone SVG documents.
150 // If the svg is embedded, the scaling is handled by the host renderer, so when asking from inside
151 // the SVG document, a scale value of 1 seems reasonable, as it doesn't know anything about the parent scale.
152 return frameTree.parent() ? 1 : frame->pageZoomFactor();
153 }
154
setCurrentScale(float scale)155 void SVGSVGElement::setCurrentScale(float scale)
156 {
157 if (!inDocument() || !isOutermostSVGSVGElement())
158 return;
159
160 LocalFrame* frame = document().frame();
161 if (!frame)
162 return;
163
164 const FrameTree& frameTree = frame->tree();
165
166 // The behaviour of setCurrentScale() is undefined, when we're dealing with non-standalone SVG documents.
167 // We choose the ignore this call, it's pretty useless to support calling setCurrentScale() from within
168 // an embedded SVG document, for the same reasons as in currentScale() - needs resolution by SVG WG.
169 if (frameTree.parent())
170 return;
171
172 frame->setPageZoomFactor(scale);
173 }
174
175 class SVGCurrentTranslateTearOff : public SVGPointTearOff {
176 public:
create(SVGSVGElement * contextElement)177 static PassRefPtr<SVGCurrentTranslateTearOff> create(SVGSVGElement* contextElement)
178 {
179 return adoptRef(new SVGCurrentTranslateTearOff(contextElement));
180 }
181
commitChange()182 virtual void commitChange() OVERRIDE
183 {
184 ASSERT(contextElement());
185 toSVGSVGElement(contextElement())->updateCurrentTranslate();
186 }
187
188 private:
SVGCurrentTranslateTearOff(SVGSVGElement * contextElement)189 SVGCurrentTranslateTearOff(SVGSVGElement* contextElement)
190 : SVGPointTearOff(contextElement->m_translation, contextElement, PropertyIsNotAnimVal)
191 {
192 }
193 };
194
currentTranslateFromJavascript()195 PassRefPtr<SVGPointTearOff> SVGSVGElement::currentTranslateFromJavascript()
196 {
197 return SVGCurrentTranslateTearOff::create(this);
198 }
199
setCurrentTranslate(const FloatPoint & point)200 void SVGSVGElement::setCurrentTranslate(const FloatPoint& point)
201 {
202 m_translation->setValue(point);
203 updateCurrentTranslate();
204 }
205
updateCurrentTranslate()206 void SVGSVGElement::updateCurrentTranslate()
207 {
208 if (RenderObject* object = renderer())
209 object->setNeedsLayoutAndFullPaintInvalidation();
210 }
211
parseAttribute(const QualifiedName & name,const AtomicString & value)212 void SVGSVGElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
213 {
214 SVGParsingError parseError = NoError;
215
216 if (!nearestViewportElement()) {
217 bool setListener = true;
218
219 // Only handle events if we're the outermost <svg> element
220 if (name == HTMLNames::onunloadAttr)
221 document().setWindowAttributeEventListener(EventTypeNames::unload, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
222 else if (name == HTMLNames::onresizeAttr)
223 document().setWindowAttributeEventListener(EventTypeNames::resize, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
224 else if (name == HTMLNames::onscrollAttr)
225 document().setWindowAttributeEventListener(EventTypeNames::scroll, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
226 else if (name == SVGNames::onzoomAttr)
227 document().setWindowAttributeEventListener(EventTypeNames::zoom, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
228 else
229 setListener = false;
230
231 if (setListener)
232 return;
233 }
234
235 if (name == HTMLNames::onabortAttr) {
236 document().setWindowAttributeEventListener(EventTypeNames::abort, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
237 } else if (name == HTMLNames::onerrorAttr) {
238 document().setWindowAttributeEventListener(EventTypeNames::error, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
239 } else if (name == SVGNames::xAttr) {
240 m_x->setBaseValueAsString(value, parseError);
241 } else if (name == SVGNames::yAttr) {
242 m_y->setBaseValueAsString(value, parseError);
243 } else if (name == SVGNames::widthAttr) {
244 m_width->setBaseValueAsString(value, parseError);
245 } else if (name == SVGNames::heightAttr) {
246 m_height->setBaseValueAsString(value, parseError);
247 } else if (SVGFitToViewBox::parseAttribute(name, value, document(), parseError)) {
248 } else if (SVGZoomAndPan::parseAttribute(name, value)) {
249 } else {
250 SVGGraphicsElement::parseAttribute(name, value);
251 }
252
253 reportAttributeParsingError(parseError, name, value);
254 }
255
isPresentationAttribute(const QualifiedName & name) const256 bool SVGSVGElement::isPresentationAttribute(const QualifiedName& name) const
257 {
258 if (isOutermostSVGSVGElement() && (name == SVGNames::widthAttr || name == SVGNames::heightAttr))
259 return true;
260 return SVGGraphicsElement::isPresentationAttribute(name);
261 }
262
collectStyleForPresentationAttribute(const QualifiedName & name,const AtomicString & value,MutableStylePropertySet * style)263 void SVGSVGElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
264 {
265 if (isOutermostSVGSVGElement() && (name == SVGNames::widthAttr || name == SVGNames::heightAttr)) {
266 RefPtr<SVGLength> length = SVGLength::create(LengthModeOther);
267 TrackExceptionState exceptionState;
268 length->setValueAsString(value, exceptionState);
269 if (!exceptionState.hadException()) {
270 if (name == SVGNames::widthAttr)
271 addPropertyToPresentationAttributeStyle(style, CSSPropertyWidth, value);
272 else if (name == SVGNames::heightAttr)
273 addPropertyToPresentationAttributeStyle(style, CSSPropertyHeight, value);
274 }
275 } else {
276 SVGGraphicsElement::collectStyleForPresentationAttribute(name, value, style);
277 }
278 }
279
svgAttributeChanged(const QualifiedName & attrName)280 void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName)
281 {
282 bool updateRelativeLengthsOrViewBox = false;
283 bool widthChanged = attrName == SVGNames::widthAttr;
284 bool heightChanged = attrName == SVGNames::heightAttr;
285 if (widthChanged || heightChanged
286 || attrName == SVGNames::xAttr
287 || attrName == SVGNames::yAttr) {
288 updateRelativeLengthsOrViewBox = true;
289 updateRelativeLengthsInformation();
290 invalidateRelativeLengthClients();
291
292 // At the SVG/HTML boundary (aka RenderSVGRoot), the width and
293 // height attributes can affect the replaced size so we need
294 // to mark it for updating.
295 //
296 // FIXME: For width/height animated as XML attributes on SVG
297 // roots, there is an attribute synchronization missing. See
298 // http://crbug.com/364807
299 if (widthChanged || heightChanged) {
300 RenderObject* renderObject = renderer();
301 if (renderObject && renderObject->isSVGRoot()) {
302 invalidateSVGPresentationAttributeStyle();
303 setNeedsStyleRecalc(LocalStyleChange);
304 }
305 }
306 }
307
308 if (SVGFitToViewBox::isKnownAttribute(attrName)) {
309 updateRelativeLengthsOrViewBox = true;
310 if (RenderObject* object = renderer())
311 object->setNeedsTransformUpdate();
312 }
313
314 SVGElement::InvalidationGuard invalidationGuard(this);
315
316 if (updateRelativeLengthsOrViewBox
317 || SVGZoomAndPan::isKnownAttribute(attrName)) {
318 if (renderer())
319 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer());
320 return;
321 }
322
323 SVGGraphicsElement::svgAttributeChanged(attrName);
324 }
325
326 // FloatRect::intersects does not consider horizontal or vertical lines (because of isEmpty()).
intersectsAllowingEmpty(const FloatRect & r1,const FloatRect & r2)327 static bool intersectsAllowingEmpty(const FloatRect& r1, const FloatRect& r2)
328 {
329 if (r1.width() < 0 || r1.height() < 0 || r2.width() < 0 || r2.height() < 0)
330 return false;
331
332 return r1.x() < r2.maxX() && r2.x() < r1.maxX()
333 && r1.y() < r2.maxY() && r2.y() < r1.maxY();
334 }
335
336 // One of the element types that can cause graphics to be drawn onto the target canvas.
337 // Specifically: circle, ellipse, image, line, path, polygon, polyline, rect, text and use.
isIntersectionOrEnclosureTarget(RenderObject * renderer)338 static bool isIntersectionOrEnclosureTarget(RenderObject* renderer)
339 {
340 return renderer->isSVGShape()
341 || renderer->isSVGText()
342 || renderer->isSVGImage()
343 || isSVGUseElement(*renderer->node());
344 }
345
checkIntersectionOrEnclosure(const SVGElement & element,const FloatRect & rect,CheckIntersectionOrEnclosure mode) const346 bool SVGSVGElement::checkIntersectionOrEnclosure(const SVGElement& element, const FloatRect& rect,
347 CheckIntersectionOrEnclosure mode) const
348 {
349 RenderObject* renderer = element.renderer();
350 ASSERT(!renderer || renderer->style());
351 if (!renderer || renderer->style()->pointerEvents() == PE_NONE)
352 return false;
353
354 if (!isIntersectionOrEnclosureTarget(renderer))
355 return false;
356
357 AffineTransform ctm = toSVGGraphicsElement(element).computeCTM(AncestorScope, DisallowStyleUpdate, this);
358 FloatRect mappedRepaintRect = ctm.mapRect(renderer->paintInvalidationRectInLocalCoordinates());
359
360 bool result = false;
361 switch (mode) {
362 case CheckIntersection:
363 result = intersectsAllowingEmpty(rect, mappedRepaintRect);
364 break;
365 case CheckEnclosure:
366 result = rect.contains(mappedRepaintRect);
367 break;
368 default:
369 ASSERT_NOT_REACHED();
370 break;
371 }
372
373 return result;
374 }
375
collectIntersectionOrEnclosureList(const FloatRect & rect,SVGElement * referenceElement,CheckIntersectionOrEnclosure mode) const376 PassRefPtrWillBeRawPtr<StaticNodeList> SVGSVGElement::collectIntersectionOrEnclosureList(const FloatRect& rect,
377 SVGElement* referenceElement, CheckIntersectionOrEnclosure mode) const
378 {
379 WillBeHeapVector<RefPtrWillBeMember<Node> > nodes;
380
381 const SVGElement* root = this;
382 if (referenceElement) {
383 // Only the common subtree needs to be traversed.
384 if (contains(referenceElement)) {
385 root = referenceElement;
386 } else if (!isDescendantOf(referenceElement)) {
387 // No common subtree.
388 return StaticNodeList::adopt(nodes);
389 }
390 }
391
392 for (SVGGraphicsElement* element = Traversal<SVGGraphicsElement>::firstWithin(*root); element;
393 element = Traversal<SVGGraphicsElement>::next(*element, root)) {
394 if (checkIntersectionOrEnclosure(*element, rect, mode))
395 nodes.append(element);
396 }
397
398 return StaticNodeList::adopt(nodes);
399 }
400
getIntersectionList(PassRefPtr<SVGRectTearOff> rect,SVGElement * referenceElement) const401 PassRefPtrWillBeRawPtr<StaticNodeList> SVGSVGElement::getIntersectionList(PassRefPtr<SVGRectTearOff> rect, SVGElement* referenceElement) const
402 {
403 document().updateLayoutIgnorePendingStylesheets();
404
405 return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CheckIntersection);
406 }
407
getEnclosureList(PassRefPtr<SVGRectTearOff> rect,SVGElement * referenceElement) const408 PassRefPtrWillBeRawPtr<StaticNodeList> SVGSVGElement::getEnclosureList(PassRefPtr<SVGRectTearOff> rect, SVGElement* referenceElement) const
409 {
410 document().updateLayoutIgnorePendingStylesheets();
411
412 return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CheckEnclosure);
413 }
414
checkIntersection(SVGElement * element,PassRefPtr<SVGRectTearOff> rect) const415 bool SVGSVGElement::checkIntersection(SVGElement* element, PassRefPtr<SVGRectTearOff> rect) const
416 {
417 ASSERT(element);
418 document().updateLayoutIgnorePendingStylesheets();
419
420 return checkIntersectionOrEnclosure(*element, rect->target()->value(), CheckIntersection);
421 }
422
checkEnclosure(SVGElement * element,PassRefPtr<SVGRectTearOff> rect) const423 bool SVGSVGElement::checkEnclosure(SVGElement* element, PassRefPtr<SVGRectTearOff> rect) const
424 {
425 ASSERT(element);
426 document().updateLayoutIgnorePendingStylesheets();
427
428 return checkIntersectionOrEnclosure(*element, rect->target()->value(), CheckEnclosure);
429 }
430
deselectAll()431 void SVGSVGElement::deselectAll()
432 {
433 if (LocalFrame* frame = document().frame())
434 frame->selection().clear();
435 }
436
createSVGNumber()437 PassRefPtr<SVGNumberTearOff> SVGSVGElement::createSVGNumber()
438 {
439 return SVGNumberTearOff::create(SVGNumber::create(0.0f), 0, PropertyIsNotAnimVal);
440 }
441
createSVGLength()442 PassRefPtr<SVGLengthTearOff> SVGSVGElement::createSVGLength()
443 {
444 return SVGLengthTearOff::create(SVGLength::create(), 0, PropertyIsNotAnimVal);
445 }
446
createSVGAngle()447 PassRefPtr<SVGAngleTearOff> SVGSVGElement::createSVGAngle()
448 {
449 return SVGAngleTearOff::create(SVGAngle::create(), 0, PropertyIsNotAnimVal);
450 }
451
createSVGPoint()452 PassRefPtr<SVGPointTearOff> SVGSVGElement::createSVGPoint()
453 {
454 return SVGPointTearOff::create(SVGPoint::create(), 0, PropertyIsNotAnimVal);
455 }
456
createSVGMatrix()457 PassRefPtr<SVGMatrixTearOff> SVGSVGElement::createSVGMatrix()
458 {
459 return SVGMatrixTearOff::create(AffineTransform());
460 }
461
createSVGRect()462 PassRefPtr<SVGRectTearOff> SVGSVGElement::createSVGRect()
463 {
464 return SVGRectTearOff::create(SVGRect::create(), 0, PropertyIsNotAnimVal);
465 }
466
createSVGTransform()467 PassRefPtr<SVGTransformTearOff> SVGSVGElement::createSVGTransform()
468 {
469 return SVGTransformTearOff::create(SVGTransform::create(SVG_TRANSFORM_MATRIX), 0, PropertyIsNotAnimVal);
470 }
471
createSVGTransformFromMatrix(PassRefPtr<SVGMatrixTearOff> matrix)472 PassRefPtr<SVGTransformTearOff> SVGSVGElement::createSVGTransformFromMatrix(PassRefPtr<SVGMatrixTearOff> matrix)
473 {
474 return SVGTransformTearOff::create(SVGTransform::create(matrix->value()), 0, PropertyIsNotAnimVal);
475 }
476
localCoordinateSpaceTransform(SVGElement::CTMScope mode) const477 AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGElement::CTMScope mode) const
478 {
479 AffineTransform viewBoxTransform;
480 if (!hasEmptyViewBox()) {
481 FloatSize size = currentViewportSize();
482 viewBoxTransform = viewBoxToViewTransform(size.width(), size.height());
483 }
484
485 AffineTransform transform;
486 if (!isOutermostSVGSVGElement()) {
487 SVGLengthContext lengthContext(this);
488 transform.translate(m_x->currentValue()->value(lengthContext), m_y->currentValue()->value(lengthContext));
489 } else if (mode == SVGElement::ScreenScope) {
490 if (RenderObject* renderer = this->renderer()) {
491 FloatPoint location;
492 float zoomFactor = 1;
493
494 // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform
495 // to map an element from SVG viewport coordinates to CSS box coordinates.
496 // RenderSVGRoot's localToAbsolute method expects CSS box coordinates.
497 // We also need to adjust for the zoom level factored into CSS coordinates (bug #96361).
498 if (renderer->isSVGRoot()) {
499 location = toRenderSVGRoot(renderer)->localToBorderBoxTransform().mapPoint(location);
500 zoomFactor = 1 / renderer->style()->effectiveZoom();
501 }
502
503 // Translate in our CSS parent coordinate space
504 // FIXME: This doesn't work correctly with CSS transforms.
505 location = renderer->localToAbsolute(location, UseTransforms);
506 location.scale(zoomFactor, zoomFactor);
507
508 // Be careful here! localToBorderBoxTransform() included the x/y offset coming from the viewBoxToViewTransform(),
509 // so we have to subtract it here (original cause of bug #27183)
510 transform.translate(location.x() - viewBoxTransform.e(), location.y() - viewBoxTransform.f());
511
512 // Respect scroll offset.
513 if (FrameView* view = document().view()) {
514 LayoutSize scrollOffset = view->scrollOffset();
515 scrollOffset.scale(zoomFactor);
516 transform.translate(-scrollOffset.width(), -scrollOffset.height());
517 }
518 }
519 }
520
521 return transform.multiply(viewBoxTransform);
522 }
523
rendererIsNeeded(const RenderStyle & style)524 bool SVGSVGElement::rendererIsNeeded(const RenderStyle& style)
525 {
526 // FIXME: We should respect display: none on the documentElement svg element
527 // but many things in FrameView and SVGImage depend on the RenderSVGRoot when
528 // they should instead depend on the RenderView.
529 // https://bugs.webkit.org/show_bug.cgi?id=103493
530 if (document().documentElement() == this)
531 return true;
532 return Element::rendererIsNeeded(style);
533 }
534
createRenderer(RenderStyle *)535 RenderObject* SVGSVGElement::createRenderer(RenderStyle*)
536 {
537 if (isOutermostSVGSVGElement())
538 return new RenderSVGRoot(this);
539
540 return new RenderSVGViewportContainer(this);
541 }
542
insertedInto(ContainerNode * rootParent)543 Node::InsertionNotificationRequest SVGSVGElement::insertedInto(ContainerNode* rootParent)
544 {
545 if (rootParent->inDocument()) {
546 UseCounter::count(document(), UseCounter::SVGSVGElementInDocument);
547 if (rootParent->document().isXMLDocument())
548 UseCounter::count(document(), UseCounter::SVGSVGElementInXMLDocument);
549
550 document().accessSVGExtensions().addTimeContainer(this);
551
552 // Animations are started at the end of document parsing and after firing the load event,
553 // but if we miss that train (deferred programmatic element insertion for example) we need
554 // to initialize the time container here.
555 if (!document().parsing() && !document().processingLoadEvent() && document().loadEventFinished() && !timeContainer()->isStarted())
556 timeContainer()->begin();
557 }
558 return SVGGraphicsElement::insertedInto(rootParent);
559 }
560
removedFrom(ContainerNode * rootParent)561 void SVGSVGElement::removedFrom(ContainerNode* rootParent)
562 {
563 if (rootParent->inDocument()) {
564 SVGDocumentExtensions& svgExtensions = document().accessSVGExtensions();
565 svgExtensions.removeTimeContainer(this);
566 svgExtensions.removeSVGRootWithRelativeLengthDescendents(this);
567 }
568
569 SVGGraphicsElement::removedFrom(rootParent);
570 }
571
pauseAnimations()572 void SVGSVGElement::pauseAnimations()
573 {
574 if (!m_timeContainer->isPaused())
575 m_timeContainer->pause();
576 }
577
unpauseAnimations()578 void SVGSVGElement::unpauseAnimations()
579 {
580 if (m_timeContainer->isPaused())
581 m_timeContainer->resume();
582 }
583
animationsPaused() const584 bool SVGSVGElement::animationsPaused() const
585 {
586 return m_timeContainer->isPaused();
587 }
588
getCurrentTime() const589 float SVGSVGElement::getCurrentTime() const
590 {
591 return narrowPrecisionToFloat(m_timeContainer->elapsed().value());
592 }
593
setCurrentTime(float seconds)594 void SVGSVGElement::setCurrentTime(float seconds)
595 {
596 if (std::isnan(seconds))
597 return;
598 seconds = max(seconds, 0.0f);
599 m_timeContainer->setElapsed(seconds);
600 }
601
selfHasRelativeLengths() const602 bool SVGSVGElement::selfHasRelativeLengths() const
603 {
604 return m_x->currentValue()->isRelative()
605 || m_y->currentValue()->isRelative()
606 || m_width->currentValue()->isRelative()
607 || m_height->currentValue()->isRelative()
608 || hasAttribute(SVGNames::viewBoxAttr);
609 }
610
currentViewBoxRect() const611 FloatRect SVGSVGElement::currentViewBoxRect() const
612 {
613 if (m_useCurrentView)
614 return m_viewSpec ? m_viewSpec->viewBox()->currentValue()->value() : FloatRect();
615
616 FloatRect useViewBox = viewBox()->currentValue()->value();
617 if (!useViewBox.isEmpty())
618 return useViewBox;
619 if (!renderer() || !renderer()->isSVGRoot())
620 return FloatRect();
621 if (!toRenderSVGRoot(renderer())->isEmbeddedThroughSVGImage())
622 return FloatRect();
623
624 // If no viewBox is specified but non-relative width/height values, then we
625 // should always synthesize a viewBox if we're embedded through a SVGImage.
626 return FloatRect(FloatPoint(), FloatSize(floatValueForLength(intrinsicWidth(), 0), floatValueForLength(intrinsicHeight(), 0)));
627 }
628
currentViewportSize() const629 FloatSize SVGSVGElement::currentViewportSize() const
630 {
631 if (!renderer())
632 return FloatSize();
633
634 if (renderer()->isSVGRoot()) {
635 LayoutRect contentBoxRect = toRenderSVGRoot(renderer())->contentBoxRect();
636 return FloatSize(contentBoxRect.width() / renderer()->style()->effectiveZoom(), contentBoxRect.height() / renderer()->style()->effectiveZoom());
637 }
638
639 FloatRect viewportRect = toRenderSVGViewportContainer(renderer())->viewport();
640 return FloatSize(viewportRect.width(), viewportRect.height());
641 }
642
hasIntrinsicWidth() const643 bool SVGSVGElement::hasIntrinsicWidth() const
644 {
645 return width()->currentValue()->unitType() != LengthTypePercentage;
646 }
647
hasIntrinsicHeight() const648 bool SVGSVGElement::hasIntrinsicHeight() const
649 {
650 return height()->currentValue()->unitType() != LengthTypePercentage;
651 }
652
intrinsicWidth() const653 Length SVGSVGElement::intrinsicWidth() const
654 {
655 if (width()->currentValue()->unitType() == LengthTypePercentage)
656 return Length(0, Fixed);
657
658 SVGLengthContext lengthContext(this);
659 return Length(width()->currentValue()->value(lengthContext), Fixed);
660 }
661
intrinsicHeight() const662 Length SVGSVGElement::intrinsicHeight() const
663 {
664 if (height()->currentValue()->unitType() == LengthTypePercentage)
665 return Length(0, Fixed);
666
667 SVGLengthContext lengthContext(this);
668 return Length(height()->currentValue()->value(lengthContext), Fixed);
669 }
670
viewBoxToViewTransform(float viewWidth,float viewHeight) const671 AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const
672 {
673 if (!m_useCurrentView || !m_viewSpec)
674 return SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), preserveAspectRatio()->currentValue(), viewWidth, viewHeight);
675
676 AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), m_viewSpec->preserveAspectRatio()->currentValue(), viewWidth, viewHeight);
677 RefPtr<SVGTransformList> transformList = m_viewSpec->transform();
678 if (transformList->isEmpty())
679 return ctm;
680
681 AffineTransform transform;
682 if (transformList->concatenate(transform))
683 ctm *= transform;
684
685 return ctm;
686 }
687
setupInitialView(const String & fragmentIdentifier,Element * anchorNode)688 void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* anchorNode)
689 {
690 RenderObject* renderer = this->renderer();
691 SVGViewSpec* view = m_viewSpec.get();
692 if (view)
693 view->reset();
694
695 bool hadUseCurrentView = m_useCurrentView;
696 m_useCurrentView = false;
697
698 if (fragmentIdentifier.startsWith("xpointer(")) {
699 // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491)
700 if (renderer && hadUseCurrentView)
701 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
702 return;
703 }
704
705 if (fragmentIdentifier.startsWith("svgView(")) {
706 if (!view)
707 view = currentView(); // Create the SVGViewSpec.
708
709 if (view->parseViewSpec(fragmentIdentifier))
710 m_useCurrentView = true;
711 else
712 view->reset();
713
714 if (renderer && (hadUseCurrentView || m_useCurrentView))
715 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
716 return;
717 }
718
719 // Spec: If the SVG fragment identifier addresses a ‘view’ element within an SVG document (e.g., MyDrawing.svg#MyView
720 // or MyDrawing.svg#xpointer(id('MyView'))) then the closest ancestor ‘svg’ element is displayed in the viewport.
721 // Any view specification attributes included on the given ‘view’ element override the corresponding view specification
722 // attributes on the closest ancestor ‘svg’ element.
723 if (isSVGViewElement(anchorNode)) {
724 SVGViewElement& viewElement = toSVGViewElement(*anchorNode);
725
726 if (SVGSVGElement* svg = viewElement.ownerSVGElement()) {
727 svg->inheritViewAttributes(&viewElement);
728
729 if (RenderObject* renderer = svg->renderer())
730 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
731 }
732 }
733
734 // FIXME: We need to decide which <svg> to focus on, and zoom to it.
735 // FIXME: We need to actually "highlight" the viewTarget(s).
736 }
737
inheritViewAttributes(SVGViewElement * viewElement)738 void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement)
739 {
740 SVGViewSpec* view = currentView();
741 m_useCurrentView = true;
742
743 if (viewElement->hasAttribute(SVGNames::viewBoxAttr))
744 view->viewBox()->baseValue()->setValue(viewElement->viewBox()->currentValue()->value());
745 else
746 view->viewBox()->baseValue()->setValue(viewBox()->currentValue()->value());
747
748 if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr)) {
749 view->preserveAspectRatio()->baseValue()->setAlign(viewElement->preserveAspectRatio()->currentValue()->align());
750 view->preserveAspectRatio()->baseValue()->setMeetOrSlice(viewElement->preserveAspectRatio()->currentValue()->meetOrSlice());
751 } else {
752 view->preserveAspectRatio()->baseValue()->setAlign(preserveAspectRatio()->currentValue()->align());
753 view->preserveAspectRatio()->baseValue()->setMeetOrSlice(preserveAspectRatio()->currentValue()->meetOrSlice());
754 }
755
756 if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr))
757 view->setZoomAndPan(viewElement->zoomAndPan());
758 else
759 view->setZoomAndPan(zoomAndPan());
760 }
761
finishParsingChildren()762 void SVGSVGElement::finishParsingChildren()
763 {
764 SVGGraphicsElement::finishParsingChildren();
765
766 // The outermost SVGSVGElement SVGLoad event is fired through Document::dispatchWindowLoadEvent.
767 if (isOutermostSVGSVGElement())
768 return;
769
770 // finishParsingChildren() is called when the close tag is reached for an element (e.g. </svg>)
771 // we send SVGLoad events here if we can, otherwise they'll be sent when any required loads finish
772 sendSVGLoadEventIfPossible();
773 }
774
trace(Visitor * visitor)775 void SVGSVGElement::trace(Visitor* visitor)
776 {
777 visitor->trace(m_timeContainer);
778 visitor->trace(m_viewSpec);
779 SVGGraphicsElement::trace(visitor);
780 }
781
782 } // namespace blink
783