blob: f2ce82cbdc0c737806a7370672d2d1a171a2ef2c [file] [log] [blame]
/*
* Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org>
* Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010 Rob Buis <buis@kde.org>
* Copyright (C) 2007 Apple Inc. All rights reserved.
* Copyright (C) 2014 Google, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "third_party/blink/renderer/core/svg/svg_svg_element.h"
#include "third_party/blink/renderer/bindings/core/v8/script_event_listener.h"
#include "third_party/blink/renderer/core/css/css_resolution_units.h"
#include "third_party/blink/renderer/core/css/style_change_reason.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/element_traversal.h"
#include "third_party/blink/renderer/core/dom/events/event_listener.h"
#include "third_party/blink/renderer/core/dom/static_node_list.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/frame/deprecation.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_model_object.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_root.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_viewport_container.h"
#include "third_party/blink/renderer/core/svg/animation/smil_time_container.h"
#include "third_party/blink/renderer/core/svg/svg_angle_tear_off.h"
#include "third_party/blink/renderer/core/svg/svg_document_extensions.h"
#include "third_party/blink/renderer/core/svg/svg_length_tear_off.h"
#include "third_party/blink/renderer/core/svg/svg_matrix_tear_off.h"
#include "third_party/blink/renderer/core/svg/svg_number_tear_off.h"
#include "third_party/blink/renderer/core/svg/svg_point_tear_off.h"
#include "third_party/blink/renderer/core/svg/svg_preserve_aspect_ratio.h"
#include "third_party/blink/renderer/core/svg/svg_rect_tear_off.h"
#include "third_party/blink/renderer/core/svg/svg_transform.h"
#include "third_party/blink/renderer/core/svg/svg_transform_list.h"
#include "third_party/blink/renderer/core/svg/svg_transform_tear_off.h"
#include "third_party/blink/renderer/core/svg/svg_view_element.h"
#include "third_party/blink/renderer/core/svg/svg_view_spec.h"
#include "third_party/blink/renderer/core/svg_names.h"
#include "third_party/blink/renderer/platform/geometry/float_rect.h"
#include "third_party/blink/renderer/platform/geometry/length_functions.h"
#include "third_party/blink/renderer/platform/transforms/affine_transform.h"
#include "third_party/blink/renderer/platform/wtf/math_extras.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
namespace blink {
inline SVGSVGElement::SVGSVGElement(Document& doc)
: SVGGraphicsElement(svg_names::kSVGTag, doc),
SVGFitToViewBox(this),
x_(SVGAnimatedLength::Create(this,
svg_names::kXAttr,
SVGLengthMode::kWidth,
SVGLength::Initial::kUnitlessZero,
CSSPropertyID::kX)),
y_(SVGAnimatedLength::Create(this,
svg_names::kYAttr,
SVGLengthMode::kHeight,
SVGLength::Initial::kUnitlessZero,
CSSPropertyID::kY)),
width_(SVGAnimatedLength::Create(this,
svg_names::kWidthAttr,
SVGLengthMode::kWidth,
SVGLength::Initial::kPercent100,
CSSPropertyID::kWidth)),
height_(SVGAnimatedLength::Create(this,
svg_names::kHeightAttr,
SVGLengthMode::kHeight,
SVGLength::Initial::kPercent100,
CSSPropertyID::kHeight)),
time_container_(SMILTimeContainer::Create(*this)),
translation_(SVGPoint::Create()),
current_scale_(1) {
AddToPropertyMap(x_);
AddToPropertyMap(y_);
AddToPropertyMap(width_);
AddToPropertyMap(height_);
UseCounter::Count(doc, WebFeature::kSVGSVGElement);
}
DEFINE_NODE_FACTORY(SVGSVGElement)
SVGSVGElement::~SVGSVGElement() = default;
float SVGSVGElement::currentScale() const {
if (!isConnected() || !IsOutermostSVGSVGElement())
return 1;
return current_scale_;
}
void SVGSVGElement::setCurrentScale(float scale) {
DCHECK(std::isfinite(scale));
if (!isConnected() || !IsOutermostSVGSVGElement())
return;
current_scale_ = scale;
UpdateUserTransform();
}
class SVGCurrentTranslateTearOff : public SVGPointTearOff {
public:
static SVGCurrentTranslateTearOff* Create(SVGSVGElement* context_element) {
return MakeGarbageCollected<SVGCurrentTranslateTearOff>(context_element);
}
SVGCurrentTranslateTearOff(SVGSVGElement* context_element)
: SVGPointTearOff(context_element->translation_, context_element) {}
void CommitChange() override {
DCHECK(ContextElement());
ToSVGSVGElement(ContextElement())->UpdateUserTransform();
}
};
SVGPointTearOff* SVGSVGElement::currentTranslateFromJavascript() {
return SVGCurrentTranslateTearOff::Create(this);
}
void SVGSVGElement::SetCurrentTranslate(const FloatPoint& point) {
translation_->SetValue(point);
UpdateUserTransform();
}
void SVGSVGElement::UpdateUserTransform() {
if (LayoutObject* object = GetLayoutObject()) {
object->SetNeedsLayoutAndFullPaintInvalidation(
layout_invalidation_reason::kUnknown);
}
}
bool SVGSVGElement::ZoomAndPanEnabled() const {
SVGZoomAndPanType zoom_and_pan = this->zoomAndPan();
if (view_spec_ && view_spec_->ZoomAndPan() != kSVGZoomAndPanUnknown)
zoom_and_pan = view_spec_->ZoomAndPan();
return zoom_and_pan == kSVGZoomAndPanMagnify;
}
void SVGSVGElement::ParseAttribute(const AttributeModificationParams& params) {
const QualifiedName& name = params.name;
const AtomicString& value = params.new_value;
if (!nearestViewportElement()) {
bool set_listener = true;
// Only handle events if we're the outermost <svg> element
if (name == html_names::kOnunloadAttr) {
GetDocument().SetWindowAttributeEventListener(
event_type_names::kUnload,
CreateAttributeEventListener(GetDocument().GetFrame(), name, value));
} else if (name == html_names::kOnresizeAttr) {
GetDocument().SetWindowAttributeEventListener(
event_type_names::kResize,
CreateAttributeEventListener(GetDocument().GetFrame(), name, value));
} else if (name == html_names::kOnscrollAttr) {
GetDocument().SetWindowAttributeEventListener(
event_type_names::kScroll,
CreateAttributeEventListener(GetDocument().GetFrame(), name, value));
} else {
set_listener = false;
}
if (set_listener)
return;
}
if (name == html_names::kOnabortAttr) {
GetDocument().SetWindowAttributeEventListener(
event_type_names::kAbort,
CreateAttributeEventListener(GetDocument().GetFrame(), name, value));
} else if (name == html_names::kOnerrorAttr) {
GetDocument().SetWindowAttributeEventListener(
event_type_names::kError,
CreateAttributeEventListener(
GetDocument().GetFrame(), name, value,
JSEventHandler::HandlerType::kOnErrorEventHandler));
} else if (SVGZoomAndPan::ParseAttribute(name, value)) {
} else {
SVGElement::ParseAttribute(params);
}
}
bool SVGSVGElement::IsPresentationAttribute(const QualifiedName& name) const {
if ((name == svg_names::kWidthAttr || name == svg_names::kHeightAttr) &&
!IsOutermostSVGSVGElement())
return false;
return SVGGraphicsElement::IsPresentationAttribute(name);
}
bool SVGSVGElement::IsPresentationAttributeWithSVGDOM(
const QualifiedName& attr_name) const {
if (attr_name == svg_names::kWidthAttr || attr_name == svg_names::kHeightAttr)
return false;
return SVGGraphicsElement::IsPresentationAttributeWithSVGDOM(attr_name);
}
void SVGSVGElement::CollectStyleForPresentationAttribute(
const QualifiedName& name,
const AtomicString& value,
MutableCSSPropertyValueSet* style) {
SVGAnimatedPropertyBase* property = PropertyFromAttribute(name);
if (property == x_) {
AddPropertyToPresentationAttributeStyle(style, property->CssPropertyId(),
x_->CssValue());
} else if (property == y_) {
AddPropertyToPresentationAttributeStyle(style, property->CssPropertyId(),
y_->CssValue());
} else if (IsOutermostSVGSVGElement() &&
(property == width_ || property == height_)) {
if (property == width_) {
AddPropertyToPresentationAttributeStyle(style, property->CssPropertyId(),
width_->CssValue());
} else if (property == height_) {
AddPropertyToPresentationAttributeStyle(style, property->CssPropertyId(),
height_->CssValue());
}
} else {
SVGGraphicsElement::CollectStyleForPresentationAttribute(name, value,
style);
}
}
void SVGSVGElement::SvgAttributeChanged(const QualifiedName& attr_name) {
bool update_relative_lengths_or_view_box = false;
bool width_or_height_changed =
attr_name == svg_names::kWidthAttr || attr_name == svg_names::kHeightAttr;
if (width_or_height_changed || attr_name == svg_names::kXAttr ||
attr_name == svg_names::kYAttr) {
update_relative_lengths_or_view_box = true;
UpdateRelativeLengthsInformation();
InvalidateRelativeLengthClients();
// At the SVG/HTML boundary (aka LayoutSVGRoot), the width and
// height attributes can affect the replaced size so we need
// to mark it for updating.
if (width_or_height_changed) {
LayoutObject* layout_object = this->GetLayoutObject();
// If the element is not attached, we cannot be sure if it is (going to
// be) an outermost root, so always mark presentation attributes dirty in
// that case.
if (!layout_object || layout_object->IsSVGRoot()) {
InvalidateSVGPresentationAttributeStyle();
SetNeedsStyleRecalc(kLocalStyleChange,
StyleChangeReasonForTracing::Create(
style_change_reason::kSVGContainerSizeChange));
if (layout_object)
ToLayoutSVGRoot(layout_object)->IntrinsicSizingInfoChanged();
}
} else {
InvalidateSVGPresentationAttributeStyle();
SetNeedsStyleRecalc(
kLocalStyleChange,
StyleChangeReasonForTracing::FromAttribute(attr_name));
}
}
if (SVGFitToViewBox::IsKnownAttribute(attr_name)) {
update_relative_lengths_or_view_box = true;
InvalidateRelativeLengthClients();
if (LayoutObject* object = GetLayoutObject()) {
object->SetNeedsTransformUpdate();
if (attr_name == svg_names::kViewBoxAttr && object->IsSVGRoot())
ToLayoutSVGRoot(object)->IntrinsicSizingInfoChanged();
}
}
if (update_relative_lengths_or_view_box ||
SVGZoomAndPan::IsKnownAttribute(attr_name)) {
SVGElement::InvalidationGuard invalidation_guard(this);
if (auto* layout_object = GetLayoutObject())
MarkForLayoutAndParentResourceInvalidation(*layout_object);
return;
}
SVGGraphicsElement::SvgAttributeChanged(attr_name);
}
// FloatRect::intersects does not consider horizontal or vertical lines (because
// of isEmpty()).
static bool IntersectsAllowingEmpty(const FloatRect& r1, const FloatRect& r2) {
if (r1.Width() < 0 || r1.Height() < 0 || r2.Width() < 0 || r2.Height() < 0)
return false;
return r1.X() < r2.MaxX() && r2.X() < r1.MaxX() && r1.Y() < r2.MaxY() &&
r2.Y() < r1.MaxY();
}
// One of the element types that can cause graphics to be drawn onto the target
// canvas. Specifically: circle, ellipse, image, line, path, polygon, polyline,
// rect, text and use.
static bool IsIntersectionOrEnclosureTarget(LayoutObject* layout_object) {
return layout_object->IsSVGShape() || layout_object->IsSVGText() ||
layout_object->IsSVGImage() ||
IsSVGUseElement(*layout_object->GetNode());
}
bool SVGSVGElement::CheckIntersectionOrEnclosure(
const SVGElement& element,
const FloatRect& rect,
GeometryMatchingMode mode) const {
LayoutObject* layout_object = element.GetLayoutObject();
DCHECK(!layout_object || layout_object->Style());
if (!layout_object ||
layout_object->StyleRef().PointerEvents() == EPointerEvents::kNone)
return false;
if (!IsIntersectionOrEnclosureTarget(layout_object))
return false;
AffineTransform ctm =
ToSVGGraphicsElement(element).ComputeCTM(kAncestorScope, this);
FloatRect mapped_repaint_rect =
ctm.MapRect(layout_object->VisualRectInLocalSVGCoordinates());
bool result = false;
switch (mode) {
case kCheckIntersection:
result = IntersectsAllowingEmpty(rect, mapped_repaint_rect);
break;
case kCheckEnclosure:
result = rect.Contains(mapped_repaint_rect);
break;
default:
NOTREACHED();
break;
}
return result;
}
StaticNodeList* SVGSVGElement::CollectIntersectionOrEnclosureList(
const FloatRect& rect,
SVGElement* reference_element,
GeometryMatchingMode mode) const {
HeapVector<Member<Node>> nodes;
const SVGElement* root = this;
if (reference_element) {
// Only the common subtree needs to be traversed.
if (contains(reference_element)) {
root = reference_element;
} else if (!IsDescendantOf(reference_element)) {
// No common subtree.
return StaticNodeList::Adopt(nodes);
}
}
for (SVGGraphicsElement& element :
Traversal<SVGGraphicsElement>::DescendantsOf(*root)) {
if (CheckIntersectionOrEnclosure(element, rect, mode))
nodes.push_back(&element);
}
return StaticNodeList::Adopt(nodes);
}
StaticNodeList* SVGSVGElement::getIntersectionList(
SVGRectTearOff* rect,
SVGElement* reference_element) const {
GetDocument().UpdateStyleAndLayoutForNode(this);
return CollectIntersectionOrEnclosureList(
rect->Target()->Value(), reference_element, kCheckIntersection);
}
StaticNodeList* SVGSVGElement::getEnclosureList(
SVGRectTearOff* rect,
SVGElement* reference_element) const {
GetDocument().UpdateStyleAndLayoutForNode(this);
return CollectIntersectionOrEnclosureList(rect->Target()->Value(),
reference_element, kCheckEnclosure);
}
bool SVGSVGElement::checkIntersection(SVGElement* element,
SVGRectTearOff* rect) const {
DCHECK(element);
GetDocument().UpdateStyleAndLayoutForNode(this);
return CheckIntersectionOrEnclosure(*element, rect->Target()->Value(),
kCheckIntersection);
}
bool SVGSVGElement::checkEnclosure(SVGElement* element,
SVGRectTearOff* rect) const {
DCHECK(element);
GetDocument().UpdateStyleAndLayoutForNode(this);
return CheckIntersectionOrEnclosure(*element, rect->Target()->Value(),
kCheckEnclosure);
}
void SVGSVGElement::deselectAll() {
if (LocalFrame* frame = GetDocument().GetFrame())
frame->Selection().Clear();
}
SVGNumberTearOff* SVGSVGElement::createSVGNumber() {
return SVGNumberTearOff::CreateDetached();
}
SVGLengthTearOff* SVGSVGElement::createSVGLength() {
return SVGLengthTearOff::CreateDetached();
}
SVGAngleTearOff* SVGSVGElement::createSVGAngle() {
return SVGAngleTearOff::CreateDetached();
}
SVGPointTearOff* SVGSVGElement::createSVGPoint() {
return SVGPointTearOff::CreateDetached(FloatPoint(0, 0));
}
SVGMatrixTearOff* SVGSVGElement::createSVGMatrix() {
return SVGMatrixTearOff::Create(AffineTransform());
}
SVGRectTearOff* SVGSVGElement::createSVGRect() {
return SVGRectTearOff::CreateDetached(FloatRect(0, 0, 0, 0));
}
SVGTransformTearOff* SVGSVGElement::createSVGTransform() {
return SVGTransformTearOff::CreateDetached();
}
SVGTransformTearOff* SVGSVGElement::createSVGTransformFromMatrix(
SVGMatrixTearOff* matrix) {
return SVGTransformTearOff::Create(matrix);
}
AffineTransform SVGSVGElement::LocalCoordinateSpaceTransform(
CTMScope mode) const {
AffineTransform transform;
if (!IsOutermostSVGSVGElement()) {
SVGLengthContext length_context(this);
transform.Translate(x_->CurrentValue()->Value(length_context),
y_->CurrentValue()->Value(length_context));
} else if (mode == kScreenScope) {
if (LayoutObject* layout_object = this->GetLayoutObject()) {
TransformationMatrix matrix;
// Adjust for the zoom level factored into CSS coordinates (WK bug
// #96361).
matrix.Scale(1.0 / layout_object->StyleRef().EffectiveZoom());
// Apply transforms from our ancestor coordinate space, including any
// non-SVG ancestor transforms.
matrix.Multiply(layout_object->LocalToAbsoluteTransform());
// At the SVG/HTML boundary (aka LayoutSVGRoot), we need to apply the
// localToBorderBoxTransform to map an element from SVG viewport
// coordinates to CSS box coordinates.
matrix.Multiply(
ToLayoutSVGRoot(layout_object)->LocalToBorderBoxTransform());
// Drop any potential non-affine parts, because we're not able to convey
// that information further anyway until getScreenCTM returns a DOMMatrix
// (4x4 matrix.)
return matrix.ToAffineTransform();
}
}
if (!HasEmptyViewBox()) {
FloatSize size = CurrentViewportSize();
transform.Multiply(ViewBoxToViewTransform(size.Width(), size.Height()));
}
return transform;
}
bool SVGSVGElement::LayoutObjectIsNeeded(const ComputedStyle& style) const {
// FIXME: We should respect display: none on the documentElement svg element
// but many things in LocalFrameView and SVGImage depend on the LayoutSVGRoot
// when they should instead depend on the LayoutView.
// https://bugs.webkit.org/show_bug.cgi?id=103493
if (GetDocument().documentElement() == this)
return true;
// <svg> elements don't need an SVG parent to render, so we bypass
// SVGElement::layoutObjectIsNeeded.
return IsValid() && Element::LayoutObjectIsNeeded(style);
}
void SVGSVGElement::AttachLayoutTree(AttachContext& context) {
SVGGraphicsElement::AttachLayoutTree(context);
if (GetLayoutObject() && GetLayoutObject()->IsSVGRoot())
ToLayoutSVGRoot(GetLayoutObject())->IntrinsicSizingInfoChanged();
}
LayoutObject* SVGSVGElement::CreateLayoutObject(const ComputedStyle&) {
if (IsOutermostSVGSVGElement())
return new LayoutSVGRoot(this);
return new LayoutSVGViewportContainer(this);
}
Node::InsertionNotificationRequest SVGSVGElement::InsertedInto(
ContainerNode& root_parent) {
if (root_parent.isConnected()) {
UseCounter::Count(GetDocument(), WebFeature::kSVGSVGElementInDocument);
if (root_parent.GetDocument().IsXMLDocument())
UseCounter::Count(GetDocument(), WebFeature::kSVGSVGElementInXMLDocument);
GetDocument().AccessSVGExtensions().AddTimeContainer(this);
// Animations are started at the end of document parsing and after firing
// the load event, but if we miss that train (deferred programmatic
// element insertion for example) we need to initialize the time container
// here.
if (!GetDocument().Parsing() && GetDocument().LoadEventFinished() &&
!TimeContainer()->IsStarted())
TimeContainer()->Start();
}
return SVGGraphicsElement::InsertedInto(root_parent);
}
void SVGSVGElement::RemovedFrom(ContainerNode& root_parent) {
if (root_parent.isConnected()) {
SVGDocumentExtensions& svg_extensions = GetDocument().AccessSVGExtensions();
svg_extensions.RemoveTimeContainer(this);
svg_extensions.RemoveSVGRootWithRelativeLengthDescendents(this);
}
SVGGraphicsElement::RemovedFrom(root_parent);
}
void SVGSVGElement::pauseAnimations() {
if (!time_container_->IsPaused())
time_container_->Pause();
}
void SVGSVGElement::unpauseAnimations() {
if (time_container_->IsPaused())
time_container_->Unpause();
}
bool SVGSVGElement::animationsPaused() const {
return time_container_->IsPaused();
}
float SVGSVGElement::getCurrentTime() const {
return clampTo<float>(time_container_->Elapsed());
}
void SVGSVGElement::setCurrentTime(float seconds) {
DCHECK(std::isfinite(seconds));
seconds = max(seconds, 0.0f);
time_container_->SetElapsed(seconds);
}
bool SVGSVGElement::SelfHasRelativeLengths() const {
return x_->CurrentValue()->IsRelative() || y_->CurrentValue()->IsRelative() ||
width_->CurrentValue()->IsRelative() ||
height_->CurrentValue()->IsRelative();
}
bool SVGSVGElement::ShouldSynthesizeViewBox() const {
return GetLayoutObject() && GetLayoutObject()->IsSVGRoot() &&
ToLayoutSVGRoot(GetLayoutObject())->IsEmbeddedThroughSVGImage();
}
FloatRect SVGSVGElement::CurrentViewBoxRect() const {
if (view_spec_ && view_spec_->ViewBox())
return view_spec_->ViewBox()->Value();
FloatRect use_view_box = viewBox()->CurrentValue()->Value();
if (!use_view_box.IsEmpty())
return use_view_box;
if (!ShouldSynthesizeViewBox())
return FloatRect();
// If no viewBox is specified but non-relative width/height values, then we
// should always synthesize a viewBox if we're embedded through a SVGImage.
FloatSize synthesized_view_box_size(IntrinsicWidth(), IntrinsicHeight());
if (!HasIntrinsicWidth())
synthesized_view_box_size.SetWidth(
width()->CurrentValue()->ScaleByPercentage(
CurrentViewportSize().Width()));
if (!HasIntrinsicHeight())
synthesized_view_box_size.SetHeight(
height()->CurrentValue()->ScaleByPercentage(
CurrentViewportSize().Height()));
return FloatRect(FloatPoint(), synthesized_view_box_size);
}
const SVGPreserveAspectRatio* SVGSVGElement::CurrentPreserveAspectRatio()
const {
if (view_spec_ && view_spec_->PreserveAspectRatio())
return view_spec_->PreserveAspectRatio();
if (!HasValidViewBox() && ShouldSynthesizeViewBox()) {
// If no (valid) viewBox is specified and we're embedded through SVGImage,
// then synthesize a pAR with the value 'none'.
SVGPreserveAspectRatio* synthesized_par = SVGPreserveAspectRatio::Create();
synthesized_par->SetAlign(
SVGPreserveAspectRatio::kSvgPreserveaspectratioNone);
return synthesized_par;
}
return preserveAspectRatio()->CurrentValue();
}
FloatSize SVGSVGElement::CurrentViewportSize() const {
const LayoutObject* layout_object = GetLayoutObject();
if (!layout_object)
return FloatSize();
if (layout_object->IsSVGRoot()) {
LayoutSize content_size = ToLayoutSVGRoot(layout_object)->ContentSize();
float zoom = layout_object->StyleRef().EffectiveZoom();
return FloatSize(content_size.Width() / zoom, content_size.Height() / zoom);
}
FloatRect viewport_rect =
ToLayoutSVGViewportContainer(GetLayoutObject())->Viewport();
return viewport_rect.Size();
}
bool SVGSVGElement::HasIntrinsicWidth() const {
return width()->CurrentValue()->TypeWithCalcResolved() !=
CSSPrimitiveValue::UnitType::kPercentage;
}
bool SVGSVGElement::HasIntrinsicHeight() const {
return height()->CurrentValue()->TypeWithCalcResolved() !=
CSSPrimitiveValue::UnitType::kPercentage;
}
float SVGSVGElement::IntrinsicWidth() const {
if (width()->CurrentValue()->TypeWithCalcResolved() ==
CSSPrimitiveValue::UnitType::kPercentage)
return 0;
SVGLengthContext length_context(this);
return width()->CurrentValue()->Value(length_context);
}
float SVGSVGElement::IntrinsicHeight() const {
if (height()->CurrentValue()->TypeWithCalcResolved() ==
CSSPrimitiveValue::UnitType::kPercentage)
return 0;
SVGLengthContext length_context(this);
return height()->CurrentValue()->Value(length_context);
}
AffineTransform SVGSVGElement::ViewBoxToViewTransform(float view_width,
float view_height) const {
AffineTransform ctm = SVGFitToViewBox::ViewBoxToViewTransform(
CurrentViewBoxRect(), CurrentPreserveAspectRatio(), view_width,
view_height);
if (!view_spec_ || !view_spec_->Transform())
return ctm;
const SVGTransformList* transform_list = view_spec_->Transform();
if (transform_list->IsEmpty())
return ctm;
AffineTransform transform;
if (transform_list->Concatenate(transform))
ctm *= transform;
return ctm;
}
void SVGSVGElement::SetViewSpec(const SVGViewSpec* view_spec) {
// Even if the viewspec object itself doesn't change, it could still
// have been mutated, so only treat a "no viewspec" -> "no viewspec"
// transition as a no-op.
if (!view_spec_ && !view_spec)
return;
view_spec_ = view_spec;
if (LayoutObject* layout_object = GetLayoutObject())
MarkForLayoutAndParentResourceInvalidation(*layout_object);
}
void SVGSVGElement::SetupInitialView(const String& fragment_identifier,
Element* anchor_node) {
if (fragment_identifier.StartsWith("svgView(")) {
SVGViewSpec* view_spec =
SVGViewSpec::CreateFromFragment(fragment_identifier);
if (view_spec) {
UseCounter::Count(GetDocument(),
WebFeature::kSVGSVGElementFragmentSVGView);
SetViewSpec(view_spec);
return;
}
}
if (IsSVGViewElement(anchor_node)) {
// Spec: If the SVG fragment identifier addresses a 'view' element within an
// SVG document (e.g., MyDrawing.svg#MyView) then the root 'svg' element is
// displayed in the SVG viewport. Any view specification attributes included
// on the given 'view' element override the corresponding view specification
// attributes on the root 'svg' element.
SVGViewSpec* view_spec =
SVGViewSpec::CreateForViewElement(ToSVGViewElement(*anchor_node));
UseCounter::Count(GetDocument(),
WebFeature::kSVGSVGElementFragmentSVGViewElement);
SetViewSpec(view_spec);
return;
}
SetViewSpec(nullptr);
}
void SVGSVGElement::FinishParsingChildren() {
SVGGraphicsElement::FinishParsingChildren();
// The outermost SVGSVGElement SVGLoad event is fired through
// LocalDOMWindow::dispatchWindowLoadEvent.
if (IsOutermostSVGSVGElement())
return;
// finishParsingChildren() is called when the close tag is reached for an
// element (e.g. </svg>) we send SVGLoad events here if we can, otherwise
// they'll be sent when any required loads finish
SendSVGLoadEventIfPossible();
}
void SVGSVGElement::Trace(blink::Visitor* visitor) {
visitor->Trace(x_);
visitor->Trace(y_);
visitor->Trace(width_);
visitor->Trace(height_);
visitor->Trace(translation_);
visitor->Trace(time_container_);
visitor->Trace(view_spec_);
SVGGraphicsElement::Trace(visitor);
SVGFitToViewBox::Trace(visitor);
}
} // namespace blink