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