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