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