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 "third_party/blink/renderer/core/svg/svg_svg_element.h"
24 
25 #include "third_party/blink/renderer/bindings/core/v8/script_event_listener.h"
26 #include "third_party/blink/renderer/core/css/css_resolution_units.h"
27 #include "third_party/blink/renderer/core/css/style_change_reason.h"
28 #include "third_party/blink/renderer/core/dom/document.h"
29 #include "third_party/blink/renderer/core/dom/element_traversal.h"
30 #include "third_party/blink/renderer/core/dom/events/event_listener.h"
31 #include "third_party/blink/renderer/core/dom/static_node_list.h"
32 #include "third_party/blink/renderer/core/dom/xml_document.h"
33 #include "third_party/blink/renderer/core/editing/frame_selection.h"
34 #include "third_party/blink/renderer/core/frame/deprecation.h"
35 #include "third_party/blink/renderer/core/frame/local_frame.h"
36 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
37 #include "third_party/blink/renderer/core/html_names.h"
38 #include "third_party/blink/renderer/core/layout/layout_object.h"
39 #include "third_party/blink/renderer/core/layout/svg/layout_svg_model_object.h"
40 #include "third_party/blink/renderer/core/layout/svg/layout_svg_root.h"
41 #include "third_party/blink/renderer/core/layout/svg/layout_svg_viewport_container.h"
42 #include "third_party/blink/renderer/core/svg/animation/smil_time_container.h"
43 #include "third_party/blink/renderer/core/svg/svg_angle_tear_off.h"
44 #include "third_party/blink/renderer/core/svg/svg_document_extensions.h"
45 #include "third_party/blink/renderer/core/svg/svg_length_tear_off.h"
46 #include "third_party/blink/renderer/core/svg/svg_matrix_tear_off.h"
47 #include "third_party/blink/renderer/core/svg/svg_number_tear_off.h"
48 #include "third_party/blink/renderer/core/svg/svg_point_tear_off.h"
49 #include "third_party/blink/renderer/core/svg/svg_preserve_aspect_ratio.h"
50 #include "third_party/blink/renderer/core/svg/svg_rect_tear_off.h"
51 #include "third_party/blink/renderer/core/svg/svg_transform.h"
52 #include "third_party/blink/renderer/core/svg/svg_transform_list.h"
53 #include "third_party/blink/renderer/core/svg/svg_transform_tear_off.h"
54 #include "third_party/blink/renderer/core/svg/svg_view_element.h"
55 #include "third_party/blink/renderer/core/svg/svg_view_spec.h"
56 #include "third_party/blink/renderer/core/svg_names.h"
57 #include "third_party/blink/renderer/platform/geometry/float_rect.h"
58 #include "third_party/blink/renderer/platform/geometry/length_functions.h"
59 #include "third_party/blink/renderer/platform/heap/heap.h"
60 #include "third_party/blink/renderer/platform/transforms/affine_transform.h"
61 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
62 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
63 
64 namespace blink {
65 
SVGSVGElement(Document & doc)66 SVGSVGElement::SVGSVGElement(Document& doc)
67     : SVGGraphicsElement(svg_names::kSVGTag, doc),
68       SVGFitToViewBox(this),
69       x_(MakeGarbageCollected<SVGAnimatedLength>(
70           this,
71           svg_names::kXAttr,
72           SVGLengthMode::kWidth,
73           SVGLength::Initial::kUnitlessZero,
74           CSSPropertyID::kX)),
75       y_(MakeGarbageCollected<SVGAnimatedLength>(
76           this,
77           svg_names::kYAttr,
78           SVGLengthMode::kHeight,
79           SVGLength::Initial::kUnitlessZero,
80           CSSPropertyID::kY)),
81       width_(MakeGarbageCollected<SVGAnimatedLength>(
82           this,
83           svg_names::kWidthAttr,
84           SVGLengthMode::kWidth,
85           SVGLength::Initial::kPercent100,
86           CSSPropertyID::kWidth)),
87       height_(MakeGarbageCollected<SVGAnimatedLength>(
88           this,
89           svg_names::kHeightAttr,
90           SVGLengthMode::kHeight,
91           SVGLength::Initial::kPercent100,
92           CSSPropertyID::kHeight)),
93       time_container_(MakeGarbageCollected<SMILTimeContainer>(*this)),
94       translation_(MakeGarbageCollected<SVGPoint>()),
95       current_scale_(1) {
96   AddToPropertyMap(x_);
97   AddToPropertyMap(y_);
98   AddToPropertyMap(width_);
99   AddToPropertyMap(height_);
100 
101   UseCounter::Count(doc, WebFeature::kSVGSVGElement);
102 }
103 
104 SVGSVGElement::~SVGSVGElement() = default;
105 
currentScale() const106 float SVGSVGElement::currentScale() const {
107   if (!isConnected() || !IsOutermostSVGSVGElement())
108     return 1;
109 
110   return current_scale_;
111 }
112 
setCurrentScale(float scale)113 void SVGSVGElement::setCurrentScale(float scale) {
114   DCHECK(std::isfinite(scale));
115   if (!isConnected() || !IsOutermostSVGSVGElement())
116     return;
117 
118   current_scale_ = scale;
119   UpdateUserTransform();
120 }
121 
122 class SVGCurrentTranslateTearOff : public SVGPointTearOff {
123  public:
SVGCurrentTranslateTearOff(SVGSVGElement * context_element)124   SVGCurrentTranslateTearOff(SVGSVGElement* context_element)
125       : SVGPointTearOff(context_element->translation_, context_element) {}
126 
CommitChange()127   void CommitChange() override {
128     DCHECK(ContextElement());
129     To<SVGSVGElement>(ContextElement())->UpdateUserTransform();
130   }
131 };
132 
currentTranslateFromJavascript()133 SVGPointTearOff* SVGSVGElement::currentTranslateFromJavascript() {
134   return MakeGarbageCollected<SVGCurrentTranslateTearOff>(this);
135 }
136 
SetCurrentTranslate(const FloatPoint & point)137 void SVGSVGElement::SetCurrentTranslate(const FloatPoint& point) {
138   translation_->SetValue(point);
139   UpdateUserTransform();
140 }
141 
UpdateUserTransform()142 void SVGSVGElement::UpdateUserTransform() {
143   if (LayoutObject* object = GetLayoutObject()) {
144     object->SetNeedsLayoutAndFullPaintInvalidation(
145         layout_invalidation_reason::kUnknown);
146   }
147 }
148 
ZoomAndPanEnabled() const149 bool SVGSVGElement::ZoomAndPanEnabled() const {
150   SVGZoomAndPanType zoom_and_pan = zoomAndPan();
151   if (view_spec_ && view_spec_->ZoomAndPan() != kSVGZoomAndPanUnknown)
152     zoom_and_pan = view_spec_->ZoomAndPan();
153   return zoom_and_pan == kSVGZoomAndPanMagnify;
154 }
155 
ParseAttribute(const AttributeModificationParams & params)156 void SVGSVGElement::ParseAttribute(const AttributeModificationParams& params) {
157   const QualifiedName& name = params.name;
158   const AtomicString& value = params.new_value;
159   if (!nearestViewportElement()) {
160     bool set_listener = true;
161 
162     // Only handle events if we're the outermost <svg> element
163     if (name == html_names::kOnunloadAttr) {
164       GetDocument().SetWindowAttributeEventListener(
165           event_type_names::kUnload,
166           CreateAttributeEventListener(GetDocument().GetFrame(), name, value));
167     } else if (name == html_names::kOnresizeAttr) {
168       GetDocument().SetWindowAttributeEventListener(
169           event_type_names::kResize,
170           CreateAttributeEventListener(GetDocument().GetFrame(), name, value));
171     } else if (name == html_names::kOnscrollAttr) {
172       GetDocument().SetWindowAttributeEventListener(
173           event_type_names::kScroll,
174           CreateAttributeEventListener(GetDocument().GetFrame(), name, value));
175     } else {
176       set_listener = false;
177     }
178 
179     if (set_listener)
180       return;
181   }
182 
183   if (name == html_names::kOnabortAttr) {
184     GetDocument().SetWindowAttributeEventListener(
185         event_type_names::kAbort,
186         CreateAttributeEventListener(GetDocument().GetFrame(), name, value));
187   } else if (name == html_names::kOnerrorAttr) {
188     GetDocument().SetWindowAttributeEventListener(
189         event_type_names::kError,
190         CreateAttributeEventListener(
191             GetDocument().GetFrame(), name, value,
192             JSEventHandler::HandlerType::kOnErrorEventHandler));
193   } else if (SVGZoomAndPan::ParseAttribute(name, value)) {
194   } else {
195     SVGElement::ParseAttribute(params);
196   }
197 }
198 
IsPresentationAttribute(const QualifiedName & name) const199 bool SVGSVGElement::IsPresentationAttribute(const QualifiedName& name) const {
200   if ((name == svg_names::kWidthAttr || name == svg_names::kHeightAttr) &&
201       !IsOutermostSVGSVGElement())
202     return false;
203   return SVGGraphicsElement::IsPresentationAttribute(name);
204 }
205 
CollectStyleForPresentationAttribute(const QualifiedName & name,const AtomicString & value,MutableCSSPropertyValueSet * style)206 void SVGSVGElement::CollectStyleForPresentationAttribute(
207     const QualifiedName& name,
208     const AtomicString& value,
209     MutableCSSPropertyValueSet* style) {
210   SVGAnimatedPropertyBase* property = PropertyFromAttribute(name);
211   if (property == x_) {
212     AddPropertyToPresentationAttributeStyle(style, property->CssPropertyId(),
213                                             x_->CssValue());
214   } else if (property == y_) {
215     AddPropertyToPresentationAttributeStyle(style, property->CssPropertyId(),
216                                             y_->CssValue());
217   } else if (IsOutermostSVGSVGElement() &&
218              (property == width_ || property == height_)) {
219     if (property == width_) {
220       AddPropertyToPresentationAttributeStyle(style, property->CssPropertyId(),
221                                               width_->CssValue());
222     } else if (property == height_) {
223       AddPropertyToPresentationAttributeStyle(style, property->CssPropertyId(),
224                                               height_->CssValue());
225     }
226   } else {
227     SVGGraphicsElement::CollectStyleForPresentationAttribute(name, value,
228                                                              style);
229   }
230 }
231 
SvgAttributeChanged(const QualifiedName & attr_name)232 void SVGSVGElement::SvgAttributeChanged(const QualifiedName& attr_name) {
233   bool update_relative_lengths_or_view_box = false;
234   bool width_or_height_changed =
235       attr_name == svg_names::kWidthAttr || attr_name == svg_names::kHeightAttr;
236   if (width_or_height_changed || attr_name == svg_names::kXAttr ||
237       attr_name == svg_names::kYAttr) {
238     update_relative_lengths_or_view_box = true;
239     UpdateRelativeLengthsInformation();
240     InvalidateRelativeLengthClients();
241 
242     // At the SVG/HTML boundary (aka LayoutSVGRoot), the width and
243     // height attributes can affect the replaced size so we need
244     // to mark it for updating.
245     if (width_or_height_changed) {
246       LayoutObject* layout_object = GetLayoutObject();
247       // If the element is not attached, we cannot be sure if it is (going to
248       // be) an outermost root, so always mark presentation attributes dirty in
249       // that case.
250       if (!layout_object || layout_object->IsSVGRoot()) {
251         InvalidateSVGPresentationAttributeStyle();
252         SetNeedsStyleRecalc(kLocalStyleChange,
253                             StyleChangeReasonForTracing::Create(
254                                 style_change_reason::kSVGContainerSizeChange));
255         if (layout_object)
256           ToLayoutSVGRoot(layout_object)->IntrinsicSizingInfoChanged();
257       }
258     } else {
259       InvalidateSVGPresentationAttributeStyle();
260       SetNeedsStyleRecalc(
261           kLocalStyleChange,
262           StyleChangeReasonForTracing::FromAttribute(attr_name));
263     }
264   }
265 
266   if (SVGFitToViewBox::IsKnownAttribute(attr_name)) {
267     update_relative_lengths_or_view_box = true;
268     InvalidateRelativeLengthClients();
269     if (LayoutObject* object = GetLayoutObject()) {
270       object->SetNeedsTransformUpdate();
271       if (attr_name == svg_names::kViewBoxAttr && object->IsSVGRoot())
272         ToLayoutSVGRoot(object)->IntrinsicSizingInfoChanged();
273     }
274   }
275 
276   if (update_relative_lengths_or_view_box ||
277       SVGZoomAndPan::IsKnownAttribute(attr_name)) {
278     SVGElement::InvalidationGuard invalidation_guard(this);
279     if (auto* layout_object = GetLayoutObject())
280       MarkForLayoutAndParentResourceInvalidation(*layout_object);
281     return;
282   }
283 
284   SVGGraphicsElement::SvgAttributeChanged(attr_name);
285 }
286 
287 // FloatRect::intersects does not consider horizontal or vertical lines (because
288 // of isEmpty()).
IntersectsAllowingEmpty(const FloatRect & r1,const FloatRect & r2)289 static bool IntersectsAllowingEmpty(const FloatRect& r1, const FloatRect& r2) {
290   if (r1.Width() < 0 || r1.Height() < 0 || r2.Width() < 0 || r2.Height() < 0)
291     return false;
292 
293   return r1.X() < r2.MaxX() && r2.X() < r1.MaxX() && r1.Y() < r2.MaxY() &&
294          r2.Y() < r1.MaxY();
295 }
296 
297 // One of the element types that can cause graphics to be drawn onto the target
298 // canvas.  Specifically: circle, ellipse, image, line, path, polygon, polyline,
299 // rect, text and use.
IsIntersectionOrEnclosureTarget(LayoutObject * layout_object)300 static bool IsIntersectionOrEnclosureTarget(LayoutObject* layout_object) {
301   return layout_object->IsSVGShape() || layout_object->IsSVGText() ||
302          layout_object->IsSVGImage() ||
303          IsA<SVGUseElement>(*layout_object->GetNode());
304 }
305 
CheckIntersectionOrEnclosure(const SVGElement & element,const FloatRect & rect,GeometryMatchingMode mode) const306 bool SVGSVGElement::CheckIntersectionOrEnclosure(
307     const SVGElement& element,
308     const FloatRect& rect,
309     GeometryMatchingMode mode) const {
310   LayoutObject* layout_object = element.GetLayoutObject();
311   DCHECK(!layout_object || layout_object->Style());
312   if (!layout_object ||
313       layout_object->StyleRef().PointerEvents() == EPointerEvents::kNone)
314     return false;
315 
316   if (!IsIntersectionOrEnclosureTarget(layout_object))
317     return false;
318 
319   AffineTransform ctm =
320       To<SVGGraphicsElement>(element).ComputeCTM(kAncestorScope, this);
321   FloatRect mapped_repaint_rect =
322       ctm.MapRect(layout_object->VisualRectInLocalSVGCoordinates());
323 
324   bool result = false;
325   switch (mode) {
326     case kCheckIntersection:
327       result = IntersectsAllowingEmpty(rect, mapped_repaint_rect);
328       break;
329     case kCheckEnclosure:
330       result = rect.Contains(mapped_repaint_rect);
331       break;
332     default:
333       NOTREACHED();
334       break;
335   }
336 
337   return result;
338 }
339 
DidMoveToNewDocument(Document & old_document)340 void SVGSVGElement::DidMoveToNewDocument(Document& old_document) {
341   SVGGraphicsElement::DidMoveToNewDocument(old_document);
342   if (TimeContainer()->IsStarted()) {
343     TimeContainer()->ResetDocumentTime();
344   }
345 }
346 
CollectIntersectionOrEnclosureList(const FloatRect & rect,SVGElement * reference_element,GeometryMatchingMode mode) const347 StaticNodeList* SVGSVGElement::CollectIntersectionOrEnclosureList(
348     const FloatRect& rect,
349     SVGElement* reference_element,
350     GeometryMatchingMode mode) const {
351   HeapVector<Member<Node>> nodes;
352 
353   const SVGElement* root = this;
354   if (reference_element) {
355     // Only the common subtree needs to be traversed.
356     if (contains(reference_element)) {
357       root = reference_element;
358     } else if (!IsDescendantOf(reference_element)) {
359       // No common subtree.
360       return StaticNodeList::Adopt(nodes);
361     }
362   }
363 
364   for (SVGGraphicsElement& element :
365        Traversal<SVGGraphicsElement>::DescendantsOf(*root)) {
366     if (CheckIntersectionOrEnclosure(element, rect, mode))
367       nodes.push_back(&element);
368   }
369 
370   return StaticNodeList::Adopt(nodes);
371 }
372 
getIntersectionList(SVGRectTearOff * rect,SVGElement * reference_element) const373 StaticNodeList* SVGSVGElement::getIntersectionList(
374     SVGRectTearOff* rect,
375     SVGElement* reference_element) const {
376   GetDocument().UpdateStyleAndLayoutForNode(this,
377                                             DocumentUpdateReason::kJavaScript);
378 
379   return CollectIntersectionOrEnclosureList(
380       rect->Target()->Value(), reference_element, kCheckIntersection);
381 }
382 
getEnclosureList(SVGRectTearOff * rect,SVGElement * reference_element) const383 StaticNodeList* SVGSVGElement::getEnclosureList(
384     SVGRectTearOff* rect,
385     SVGElement* reference_element) const {
386   GetDocument().UpdateStyleAndLayoutForNode(this,
387                                             DocumentUpdateReason::kJavaScript);
388 
389   return CollectIntersectionOrEnclosureList(rect->Target()->Value(),
390                                             reference_element, kCheckEnclosure);
391 }
392 
checkIntersection(SVGElement * element,SVGRectTearOff * rect) const393 bool SVGSVGElement::checkIntersection(SVGElement* element,
394                                       SVGRectTearOff* rect) const {
395   DCHECK(element);
396   GetDocument().UpdateStyleAndLayoutForNode(this,
397                                             DocumentUpdateReason::kJavaScript);
398 
399   return CheckIntersectionOrEnclosure(*element, rect->Target()->Value(),
400                                       kCheckIntersection);
401 }
402 
checkEnclosure(SVGElement * element,SVGRectTearOff * rect) const403 bool SVGSVGElement::checkEnclosure(SVGElement* element,
404                                    SVGRectTearOff* rect) const {
405   DCHECK(element);
406   GetDocument().UpdateStyleAndLayoutForNode(this,
407                                             DocumentUpdateReason::kJavaScript);
408 
409   return CheckIntersectionOrEnclosure(*element, rect->Target()->Value(),
410                                       kCheckEnclosure);
411 }
412 
deselectAll()413 void SVGSVGElement::deselectAll() {
414   if (LocalFrame* frame = GetDocument().GetFrame())
415     frame->Selection().Clear();
416 }
417 
createSVGNumber()418 SVGNumberTearOff* SVGSVGElement::createSVGNumber() {
419   return SVGNumberTearOff::CreateDetached();
420 }
421 
createSVGLength()422 SVGLengthTearOff* SVGSVGElement::createSVGLength() {
423   return SVGLengthTearOff::CreateDetached();
424 }
425 
createSVGAngle()426 SVGAngleTearOff* SVGSVGElement::createSVGAngle() {
427   return SVGAngleTearOff::CreateDetached();
428 }
429 
createSVGPoint()430 SVGPointTearOff* SVGSVGElement::createSVGPoint() {
431   return SVGPointTearOff::CreateDetached(FloatPoint(0, 0));
432 }
433 
createSVGMatrix()434 SVGMatrixTearOff* SVGSVGElement::createSVGMatrix() {
435   return MakeGarbageCollected<SVGMatrixTearOff>(AffineTransform());
436 }
437 
createSVGRect()438 SVGRectTearOff* SVGSVGElement::createSVGRect() {
439   return SVGRectTearOff::CreateDetached(FloatRect(0, 0, 0, 0));
440 }
441 
createSVGTransform()442 SVGTransformTearOff* SVGSVGElement::createSVGTransform() {
443   return SVGTransformTearOff::CreateDetached();
444 }
445 
createSVGTransformFromMatrix(SVGMatrixTearOff * matrix)446 SVGTransformTearOff* SVGSVGElement::createSVGTransformFromMatrix(
447     SVGMatrixTearOff* matrix) {
448   return MakeGarbageCollected<SVGTransformTearOff>(matrix);
449 }
450 
LocalCoordinateSpaceTransform(CTMScope mode) const451 AffineTransform SVGSVGElement::LocalCoordinateSpaceTransform(
452     CTMScope mode) const {
453   AffineTransform transform;
454   if (!IsOutermostSVGSVGElement()) {
455     SVGLengthContext length_context(this);
456     transform.Translate(x_->CurrentValue()->Value(length_context),
457                         y_->CurrentValue()->Value(length_context));
458   } else if (mode == kScreenScope) {
459     if (LayoutObject* layout_object = GetLayoutObject()) {
460       TransformationMatrix matrix;
461       // Adjust for the zoom level factored into CSS coordinates (WK bug
462       // #96361).
463       matrix.Scale(1.0 / layout_object->StyleRef().EffectiveZoom());
464 
465       // Apply transforms from our ancestor coordinate space, including any
466       // non-SVG ancestor transforms.
467       matrix.Multiply(layout_object->LocalToAbsoluteTransform());
468 
469       // At the SVG/HTML boundary (aka LayoutSVGRoot), we need to apply the
470       // localToBorderBoxTransform to map an element from SVG viewport
471       // coordinates to CSS box coordinates.
472       matrix.Multiply(
473           ToLayoutSVGRoot(layout_object)->LocalToBorderBoxTransform());
474       // Drop any potential non-affine parts, because we're not able to convey
475       // that information further anyway until getScreenCTM returns a DOMMatrix
476       // (4x4 matrix.)
477       return matrix.ToAffineTransform();
478     }
479   }
480   if (!HasEmptyViewBox()) {
481     FloatSize size = CurrentViewportSize();
482     transform.Multiply(ViewBoxToViewTransform(size.Width(), size.Height()));
483   }
484   return transform;
485 }
486 
LayoutObjectIsNeeded(const ComputedStyle & style) const487 bool SVGSVGElement::LayoutObjectIsNeeded(const ComputedStyle& style) const {
488   // FIXME: We should respect display: none on the documentElement svg element
489   // but many things in LocalFrameView and SVGImage depend on the LayoutSVGRoot
490   // when they should instead depend on the LayoutView.
491   // https://bugs.webkit.org/show_bug.cgi?id=103493
492   if (GetDocument().documentElement() == this)
493     return true;
494 
495   // <svg> elements don't need an SVG parent to render, so we bypass
496   // SVGElement::layoutObjectIsNeeded.
497   return IsValid() && Element::LayoutObjectIsNeeded(style);
498 }
499 
AttachLayoutTree(AttachContext & context)500 void SVGSVGElement::AttachLayoutTree(AttachContext& context) {
501   SVGGraphicsElement::AttachLayoutTree(context);
502 
503   if (GetLayoutObject() && GetLayoutObject()->IsSVGRoot())
504     ToLayoutSVGRoot(GetLayoutObject())->IntrinsicSizingInfoChanged();
505 }
506 
CreateLayoutObject(const ComputedStyle &,LegacyLayout)507 LayoutObject* SVGSVGElement::CreateLayoutObject(const ComputedStyle&,
508                                                 LegacyLayout) {
509   if (IsOutermostSVGSVGElement())
510     return new LayoutSVGRoot(this);
511 
512   return new LayoutSVGViewportContainer(this);
513 }
514 
InsertedInto(ContainerNode & root_parent)515 Node::InsertionNotificationRequest SVGSVGElement::InsertedInto(
516     ContainerNode& root_parent) {
517   if (root_parent.isConnected()) {
518     UseCounter::Count(GetDocument(), WebFeature::kSVGSVGElementInDocument);
519     if (IsA<XMLDocument>(root_parent.GetDocument()))
520       UseCounter::Count(GetDocument(), WebFeature::kSVGSVGElementInXMLDocument);
521 
522     GetDocument().AccessSVGExtensions().AddTimeContainer(this);
523 
524     // Animations are started at the end of document parsing and after firing
525     // the load event, but if we miss that train (deferred programmatic
526     // element insertion for example) we need to initialize the time container
527     // here.
528     if (!GetDocument().Parsing() && GetDocument().LoadEventFinished() &&
529         !TimeContainer()->IsStarted())
530       TimeContainer()->Start();
531   }
532   return SVGGraphicsElement::InsertedInto(root_parent);
533 }
534 
RemovedFrom(ContainerNode & root_parent)535 void SVGSVGElement::RemovedFrom(ContainerNode& root_parent) {
536   if (root_parent.isConnected()) {
537     SVGDocumentExtensions& svg_extensions = GetDocument().AccessSVGExtensions();
538     svg_extensions.RemoveTimeContainer(this);
539     svg_extensions.RemoveSVGRootWithRelativeLengthDescendents(this);
540   }
541 
542   SVGGraphicsElement::RemovedFrom(root_parent);
543 }
544 
pauseAnimations()545 void SVGSVGElement::pauseAnimations() {
546   if (!time_container_->IsPaused())
547     time_container_->Pause();
548 }
549 
unpauseAnimations()550 void SVGSVGElement::unpauseAnimations() {
551   if (time_container_->IsPaused())
552     time_container_->Unpause();
553 }
554 
animationsPaused() const555 bool SVGSVGElement::animationsPaused() const {
556   return time_container_->IsPaused();
557 }
558 
getCurrentTime() const559 float SVGSVGElement::getCurrentTime() const {
560   return clampTo<float>(time_container_->Elapsed().InSecondsF());
561 }
562 
setCurrentTime(float seconds)563 void SVGSVGElement::setCurrentTime(float seconds) {
564   DCHECK(std::isfinite(seconds));
565   time_container_->SetElapsed(SMILTime::FromSecondsD(std::max(seconds, 0.0f)));
566 }
567 
SelfHasRelativeLengths() const568 bool SVGSVGElement::SelfHasRelativeLengths() const {
569   return x_->CurrentValue()->IsRelative() || y_->CurrentValue()->IsRelative() ||
570          width_->CurrentValue()->IsRelative() ||
571          height_->CurrentValue()->IsRelative();
572 }
573 
ShouldSynthesizeViewBox() const574 bool SVGSVGElement::ShouldSynthesizeViewBox() const {
575   return GetLayoutObject() && GetLayoutObject()->IsSVGRoot() &&
576          ToLayoutSVGRoot(GetLayoutObject())->IsEmbeddedThroughSVGImage();
577 }
578 
CurrentViewBoxRect() const579 FloatRect SVGSVGElement::CurrentViewBoxRect() const {
580   if (view_spec_ && view_spec_->ViewBox())
581     return view_spec_->ViewBox()->Value();
582 
583   FloatRect use_view_box = viewBox()->CurrentValue()->Value();
584   if (!use_view_box.IsEmpty())
585     return use_view_box;
586   if (!ShouldSynthesizeViewBox())
587     return FloatRect();
588 
589   // If no viewBox is specified but non-relative width/height values, then we
590   // should always synthesize a viewBox if we're embedded through a SVGImage.
591   FloatSize synthesized_view_box_size(IntrinsicWidth(), IntrinsicHeight());
592   if (!HasIntrinsicWidth())
593     synthesized_view_box_size.SetWidth(
594         width()->CurrentValue()->ScaleByPercentage(
595             CurrentViewportSize().Width()));
596   if (!HasIntrinsicHeight())
597     synthesized_view_box_size.SetHeight(
598         height()->CurrentValue()->ScaleByPercentage(
599             CurrentViewportSize().Height()));
600   return FloatRect(FloatPoint(), synthesized_view_box_size);
601 }
602 
CurrentPreserveAspectRatio() const603 const SVGPreserveAspectRatio* SVGSVGElement::CurrentPreserveAspectRatio()
604     const {
605   if (view_spec_ && view_spec_->PreserveAspectRatio())
606     return view_spec_->PreserveAspectRatio();
607 
608   if (!HasValidViewBox() && ShouldSynthesizeViewBox()) {
609     // If no (valid) viewBox is specified and we're embedded through SVGImage,
610     // then synthesize a pAR with the value 'none'.
611     auto* synthesized_par = MakeGarbageCollected<SVGPreserveAspectRatio>();
612     synthesized_par->SetAlign(
613         SVGPreserveAspectRatio::kSvgPreserveaspectratioNone);
614     return synthesized_par;
615   }
616   return preserveAspectRatio()->CurrentValue();
617 }
618 
CurrentViewportSize() const619 FloatSize SVGSVGElement::CurrentViewportSize() const {
620   const LayoutObject* layout_object = GetLayoutObject();
621   if (!layout_object)
622     return FloatSize();
623 
624   if (layout_object->IsSVGRoot()) {
625     LayoutSize content_size = ToLayoutSVGRoot(layout_object)->ContentSize();
626     float zoom = layout_object->StyleRef().EffectiveZoom();
627     return FloatSize(content_size.Width() / zoom, content_size.Height() / zoom);
628   }
629 
630   FloatRect viewport_rect =
631       ToLayoutSVGViewportContainer(GetLayoutObject())->Viewport();
632   return viewport_rect.Size();
633 }
634 
HasIntrinsicWidth() const635 bool SVGSVGElement::HasIntrinsicWidth() const {
636   // TODO(crbug.com/979895): This is the result of a refactoring, which might
637   // have revealed an existing bug that we are not handling math functions
638   // involving percentages correctly. Fix it if necessary.
639   return !width()->CurrentValue()->IsPercentage();
640 }
641 
HasIntrinsicHeight() const642 bool SVGSVGElement::HasIntrinsicHeight() const {
643   // TODO(crbug.com/979895): This is the result of a refactoring, which might
644   // have revealed an existing bug that we are not handling math functions
645   // involving percentages correctly. Fix it if necessary.
646   return !height()->CurrentValue()->IsPercentage();
647 }
648 
IntrinsicWidth() const649 float SVGSVGElement::IntrinsicWidth() const {
650   if (!HasIntrinsicWidth())
651     return 0;
652 
653   SVGLengthContext length_context(this);
654   return width()->CurrentValue()->Value(length_context);
655 }
656 
IntrinsicHeight() const657 float SVGSVGElement::IntrinsicHeight() const {
658   if (!HasIntrinsicHeight())
659     return 0;
660 
661   SVGLengthContext length_context(this);
662   return height()->CurrentValue()->Value(length_context);
663 }
664 
ViewBoxToViewTransform(float view_width,float view_height) const665 AffineTransform SVGSVGElement::ViewBoxToViewTransform(float view_width,
666                                                       float view_height) const {
667   AffineTransform ctm = SVGFitToViewBox::ViewBoxToViewTransform(
668       CurrentViewBoxRect(), CurrentPreserveAspectRatio(), view_width,
669       view_height);
670   if (!view_spec_ || !view_spec_->Transform())
671     return ctm;
672 
673   const SVGTransformList* transform_list = view_spec_->Transform();
674   if (transform_list->IsEmpty())
675     return ctm;
676 
677   AffineTransform transform;
678   if (transform_list->Concatenate(transform))
679     ctm *= transform;
680 
681   return ctm;
682 }
683 
SetViewSpec(const SVGViewSpec * view_spec)684 void SVGSVGElement::SetViewSpec(const SVGViewSpec* view_spec) {
685   // Even if the viewspec object itself doesn't change, it could still
686   // have been mutated, so only treat a "no viewspec" -> "no viewspec"
687   // transition as a no-op.
688   if (!view_spec_ && !view_spec)
689     return;
690   view_spec_ = view_spec;
691   if (LayoutObject* layout_object = GetLayoutObject())
692     MarkForLayoutAndParentResourceInvalidation(*layout_object);
693 }
694 
SetupInitialView(const String & fragment_identifier,Element * anchor_node)695 void SVGSVGElement::SetupInitialView(const String& fragment_identifier,
696                                      Element* anchor_node) {
697   if (fragment_identifier.StartsWith("svgView(")) {
698     SVGViewSpec* view_spec =
699         SVGViewSpec::CreateFromFragment(fragment_identifier);
700     if (view_spec) {
701       UseCounter::Count(GetDocument(),
702                         WebFeature::kSVGSVGElementFragmentSVGView);
703       SetViewSpec(view_spec);
704       return;
705     }
706   }
707   if (auto* svg_view_element = DynamicTo<SVGViewElement>(anchor_node)) {
708     // Spec: If the SVG fragment identifier addresses a 'view' element within an
709     // SVG document (e.g., MyDrawing.svg#MyView) then the root 'svg' element is
710     // displayed in the SVG viewport. Any view specification attributes included
711     // on the given 'view' element override the corresponding view specification
712     // attributes on the root 'svg' element.
713     SVGViewSpec* view_spec =
714         SVGViewSpec::CreateForViewElement(*svg_view_element);
715     UseCounter::Count(GetDocument(),
716                       WebFeature::kSVGSVGElementFragmentSVGViewElement);
717     SetViewSpec(view_spec);
718     return;
719   }
720   SetViewSpec(nullptr);
721 }
722 
FinishParsingChildren()723 void SVGSVGElement::FinishParsingChildren() {
724   SVGGraphicsElement::FinishParsingChildren();
725 
726   // The outermost SVGSVGElement SVGLoad event is fired through
727   // LocalDOMWindow::dispatchWindowLoadEvent.
728   if (IsOutermostSVGSVGElement())
729     return;
730 
731   // finishParsingChildren() is called when the close tag is reached for an
732   // element (e.g. </svg>) we send SVGLoad events here if we can, otherwise
733   // they'll be sent when any required loads finish
734   SendSVGLoadEventIfPossible();
735 }
736 
Trace(Visitor * visitor)737 void SVGSVGElement::Trace(Visitor* visitor) {
738   visitor->Trace(x_);
739   visitor->Trace(y_);
740   visitor->Trace(width_);
741   visitor->Trace(height_);
742   visitor->Trace(translation_);
743   visitor->Trace(time_container_);
744   visitor->Trace(view_spec_);
745   SVGGraphicsElement::Trace(visitor);
746   SVGFitToViewBox::Trace(visitor);
747 }
748 
749 }  // namespace blink
750