From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- dom/svg/DOMSVGLength.cpp | 495 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 495 insertions(+) create mode 100644 dom/svg/DOMSVGLength.cpp (limited to 'dom/svg/DOMSVGLength.cpp') diff --git a/dom/svg/DOMSVGLength.cpp b/dom/svg/DOMSVGLength.cpp new file mode 100644 index 0000000000..3146dfb58d --- /dev/null +++ b/dom/svg/DOMSVGLength.cpp @@ -0,0 +1,495 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DOMSVGLength.h" + +#include "DOMSVGLengthList.h" +#include "DOMSVGAnimatedLengthList.h" +#include "nsError.h" +#include "nsMathUtils.h" +#include "SVGAnimatedLength.h" +#include "SVGAnimatedLengthList.h" +#include "SVGAttrTearoffTable.h" +#include "SVGLength.h" +#include "mozilla/dom/SVGElement.h" +#include "mozilla/dom/SVGLengthBinding.h" +#include "mozilla/FloatingPoint.h" + +// See the architecture comment in DOMSVGAnimatedLengthList.h. + +namespace mozilla::dom { + +static SVGAttrTearoffTable + sBaseSVGLengthTearOffTable, sAnimSVGLengthTearOffTable; + +// We could use NS_IMPL_CYCLE_COLLECTION(, except that in Unlink() we need to +// clear our list's weak ref to us to be safe. (The other option would be to +// not unlink and rely on the breaking of the other edges in the cycle, as +// NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.) +NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGLength) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGLength) + tmp->CleanupWeakRefs(); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner) + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGLength) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMSVGLength) + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +DOMSVGLength::DOMSVGLength(DOMSVGLengthList* aList, uint8_t aAttrEnum, + uint32_t aListIndex, bool aIsAnimValItem) + : mOwner(aList), + mListIndex(aListIndex), + mAttrEnum(aAttrEnum), + mIsAnimValItem(aIsAnimValItem), + mUnit(SVGLength_Binding::SVG_LENGTHTYPE_NUMBER) { + MOZ_ASSERT(aList, "bad arg"); + MOZ_ASSERT(mAttrEnum == aAttrEnum, "bitfield too small"); + MOZ_ASSERT(aListIndex <= MaxListIndex(), "list index too large"); + MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGNumber!"); +} + +DOMSVGLength::DOMSVGLength() + : mOwner(nullptr), + mListIndex(0), + mAttrEnum(0), + mIsAnimValItem(false), + mUnit(SVGLength_Binding::SVG_LENGTHTYPE_NUMBER) {} + +DOMSVGLength::DOMSVGLength(SVGAnimatedLength* aVal, SVGElement* aSVGElement, + bool aAnimVal) + : mOwner(aSVGElement), + mListIndex(0), + mAttrEnum(aVal->mAttrEnum), + mIsAnimValItem(aAnimVal), + mUnit(SVGLength_Binding::SVG_LENGTHTYPE_NUMBER) { + MOZ_ASSERT(aVal, "bad arg"); + MOZ_ASSERT(mAttrEnum == aVal->mAttrEnum, "bitfield too small"); +} + +void DOMSVGLength::CleanupWeakRefs() { + // Our mList's weak ref to us must be nulled out when we die (or when we're + // cycle collected), so we that don't leave behind a pointer to + // free / soon-to-be-free memory. + if (nsCOMPtr lengthList = do_QueryInterface(mOwner)) { + MOZ_ASSERT(lengthList->mItems[mListIndex] == this, + "Clearing out the wrong list index...?"); + lengthList->mItems[mListIndex] = nullptr; + } + + // Similarly, we must update the tearoff table to remove its (non-owning) + // pointer to mVal. + if (nsCOMPtr svg = do_QueryInterface(mOwner)) { + auto& table = mIsAnimValItem ? sAnimSVGLengthTearOffTable + : sBaseSVGLengthTearOffTable; + table.RemoveTearoff(svg->GetAnimatedLength(mAttrEnum)); + } +} + +already_AddRefed DOMSVGLength::GetTearOff(SVGAnimatedLength* aVal, + SVGElement* aSVGElement, + bool aAnimVal) { + auto& table = + aAnimVal ? sAnimSVGLengthTearOffTable : sBaseSVGLengthTearOffTable; + RefPtr domLength = table.GetTearoff(aVal); + if (!domLength) { + domLength = new DOMSVGLength(aVal, aSVGElement, aAnimVal); + table.AddTearoff(aVal, domLength); + } + + return domLength.forget(); +} + +DOMSVGLength* DOMSVGLength::Copy() { + NS_ASSERTION(HasOwner(), "unexpected caller"); + DOMSVGLength* copy = new DOMSVGLength(); + uint16_t unit; + float value; + if (nsCOMPtr svg = do_QueryInterface(mOwner)) { + SVGAnimatedLength* length = svg->GetAnimatedLength(mAttrEnum); + if (mIsAnimValItem) { + unit = length->GetAnimUnitType(); + value = length->GetAnimValInSpecifiedUnits(); + } else { + unit = length->GetBaseUnitType(); + value = length->GetBaseValInSpecifiedUnits(); + } + } else { + const SVGLength& length = InternalItem(); + unit = length.GetUnit(); + value = length.GetValueInCurrentUnits(); + } + copy->NewValueSpecifiedUnits(unit, value, IgnoreErrors()); + return copy; +} + +uint16_t DOMSVGLength::UnitType() { + if (mIsAnimValItem) { + Element()->FlushAnimations(); + } + uint16_t unitType; + if (nsCOMPtr svg = do_QueryInterface(mOwner)) { + unitType = mIsAnimValItem + ? svg->GetAnimatedLength(mAttrEnum)->GetAnimUnitType() + : svg->GetAnimatedLength(mAttrEnum)->GetBaseUnitType(); + } else { + unitType = HasOwner() ? InternalItem().GetUnit() : mUnit; + } + + return SVGLength::IsValidUnitType(unitType) + ? unitType + : SVGLength_Binding::SVG_LENGTHTYPE_UNKNOWN; +} + +float DOMSVGLength::GetValue(ErrorResult& aRv) { + if (mIsAnimValItem) { + Element()->FlushAnimations(); // May make HasOwner() == false + } + + // If the unit depends on style or layout then we need to flush before + // converting to pixels. + FlushIfNeeded(); + + if (nsCOMPtr svg = do_QueryInterface(mOwner)) { + SVGAnimatedLength* length = svg->GetAnimatedLength(mAttrEnum); + return mIsAnimValItem ? length->GetAnimValue(svg) + : length->GetBaseValue(svg); + } + + if (nsCOMPtr lengthList = do_QueryInterface(mOwner)) { + float value = InternalItem().GetValueInPixels(lengthList->Element(), + lengthList->Axis()); + if (!std::isfinite(value)) { + aRv.Throw(NS_ERROR_FAILURE); + } + return value; + } + + if (SVGLength::IsAbsoluteUnit(mUnit)) { + return SVGLength(mValue, mUnit).GetValueInPixels(nullptr, 0); + } + + // else [SVGWG issue] Can't convert this length's value to user units + // ReportToConsole + aRv.Throw(NS_ERROR_FAILURE); + return 0.0f; +} + +void DOMSVGLength::SetValue(float aUserUnitValue, ErrorResult& aRv) { + if (mIsAnimValItem) { + aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + // If the unit depends on style or layout then we need to flush before + // converting from pixels. + FlushIfNeeded(); + + if (nsCOMPtr svg = do_QueryInterface(mOwner)) { + aRv = svg->GetAnimatedLength(mAttrEnum)->SetBaseValue(aUserUnitValue, svg, + true); + return; + } + + // Although the value passed in is in user units, this method does not turn + // this length into a user unit length. Instead it converts the user unit + // value to this length's current unit and sets that, leaving this length's + // unit as it is. + + if (nsCOMPtr lengthList = do_QueryInterface(mOwner)) { + SVGLength& internalItem = InternalItem(); + if (internalItem.GetValueInPixels(lengthList->Element(), + lengthList->Axis()) == aUserUnitValue) { + return; + } + float uuPerUnit = internalItem.GetPixelsPerUnit( + SVGElementMetrics(lengthList->Element()), lengthList->Axis()); + if (uuPerUnit > 0) { + float newValue = aUserUnitValue / uuPerUnit; + if (std::isfinite(newValue)) { + AutoChangeLengthListNotifier notifier(this); + internalItem.SetValueAndUnit(newValue, internalItem.GetUnit()); + return; + } + } + } else if (SVGLength::IsAbsoluteUnit(mUnit)) { + mValue = aUserUnitValue * SVGLength::GetAbsUnitsPerAbsUnit( + mUnit, SVGLength_Binding::SVG_LENGTHTYPE_PX); + return; + } + // else [SVGWG issue] Can't convert user unit value to this length's unit + // ReportToConsole + aRv.Throw(NS_ERROR_FAILURE); +} + +float DOMSVGLength::ValueInSpecifiedUnits() { + if (mIsAnimValItem) { + Element()->FlushAnimations(); // May make HasOwner() == false + } + if (nsCOMPtr svg = do_QueryInterface(mOwner)) { + SVGAnimatedLength* length = svg->GetAnimatedLength(mAttrEnum); + return mIsAnimValItem ? length->GetAnimValInSpecifiedUnits() + : length->GetBaseValInSpecifiedUnits(); + } + + return HasOwner() ? InternalItem().GetValueInCurrentUnits() : mValue; +} + +void DOMSVGLength::SetValueInSpecifiedUnits(float aValue, ErrorResult& aRv) { + if (mIsAnimValItem) { + aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + if (nsCOMPtr svg = do_QueryInterface(mOwner)) { + svg->GetAnimatedLength(mAttrEnum)->SetBaseValueInSpecifiedUnits(aValue, svg, + true); + return; + } + + if (HasOwner()) { + SVGLength& internalItem = InternalItem(); + if (internalItem.GetValueInCurrentUnits() == aValue) { + return; + } + AutoChangeLengthListNotifier notifier(this); + internalItem.SetValueInCurrentUnits(aValue); + return; + } + mValue = aValue; +} + +void DOMSVGLength::SetValueAsString(const nsAString& aValue, ErrorResult& aRv) { + if (mIsAnimValItem) { + aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + if (nsCOMPtr svg = do_QueryInterface(mOwner)) { + aRv = svg->GetAnimatedLength(mAttrEnum)->SetBaseValueString(aValue, svg, + true); + return; + } + + SVGLength value; + if (!value.SetValueFromString(aValue)) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return; + } + if (HasOwner()) { + SVGLength& internalItem = InternalItem(); + if (internalItem == value) { + return; + } + AutoChangeLengthListNotifier notifier(this); + internalItem = value; + return; + } + mValue = value.GetValueInCurrentUnits(); + mUnit = value.GetUnit(); +} + +void DOMSVGLength::GetValueAsString(nsAString& aValue) { + if (mIsAnimValItem) { + Element()->FlushAnimations(); // May make HasOwner() == false + } + + if (nsCOMPtr svg = do_QueryInterface(mOwner)) { + SVGAnimatedLength* length = svg->GetAnimatedLength(mAttrEnum); + if (mIsAnimValItem) { + length->GetAnimValueString(aValue); + } else { + length->GetBaseValueString(aValue); + } + return; + } + if (HasOwner()) { + InternalItem().GetValueAsString(aValue); + return; + } + SVGLength(mValue, mUnit).GetValueAsString(aValue); +} + +void DOMSVGLength::NewValueSpecifiedUnits(uint16_t aUnit, float aValue, + ErrorResult& aRv) { + if (mIsAnimValItem) { + aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + if (nsCOMPtr svg = do_QueryInterface(mOwner)) { + svg->GetAnimatedLength(mAttrEnum)->NewValueSpecifiedUnits(aUnit, aValue, + svg); + return; + } + + if (!SVGLength::IsValidUnitType(aUnit)) { + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return; + } + if (HasOwner()) { + SVGLength& internalItem = InternalItem(); + if (internalItem == SVGLength(aValue, aUnit)) { + return; + } + AutoChangeLengthListNotifier notifier(this); + internalItem.SetValueAndUnit(aValue, uint8_t(aUnit)); + return; + } + mUnit = uint8_t(aUnit); + mValue = aValue; +} + +void DOMSVGLength::ConvertToSpecifiedUnits(uint16_t aUnit, ErrorResult& aRv) { + if (mIsAnimValItem) { + aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + if (nsCOMPtr svg = do_QueryInterface(mOwner)) { + svg->GetAnimatedLength(mAttrEnum)->ConvertToSpecifiedUnits(aUnit, svg); + return; + } + + if (!SVGLength::IsValidUnitType(aUnit)) { + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return; + } + + float val; + if (nsCOMPtr lengthList = do_QueryInterface(mOwner)) { + SVGLength& length = InternalItem(); + if (length.GetUnit() == aUnit) { + return; + } + val = length.GetValueInSpecifiedUnit(aUnit, lengthList->Element(), + lengthList->Axis()); + } else { + if (mUnit == aUnit) { + return; + } + val = SVGLength(mValue, mUnit).GetValueInSpecifiedUnit(aUnit, nullptr, 0); + } + if (std::isfinite(val)) { + if (HasOwner()) { + AutoChangeLengthListNotifier notifier(this); + InternalItem().SetValueAndUnit(val, aUnit); + } else { + mValue = val; + mUnit = aUnit; + } + return; + } + // else [SVGWG issue] Can't convert unit + // ReportToConsole + aRv.Throw(NS_ERROR_FAILURE); +} + +JSObject* DOMSVGLength::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return SVGLength_Binding::Wrap(aCx, this, aGivenProto); +} + +void DOMSVGLength::InsertingIntoList(DOMSVGLengthList* aList, uint8_t aAttrEnum, + uint32_t aListIndex, bool aIsAnimValItem) { + NS_ASSERTION(!HasOwner(), "Inserting item that is already in a list"); + + mOwner = aList; + mAttrEnum = aAttrEnum; + mListIndex = aListIndex; + mIsAnimValItem = aIsAnimValItem; + + MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGLength!"); +} + +void DOMSVGLength::RemovingFromList() { + mValue = InternalItem().GetValueInCurrentUnits(); + mUnit = InternalItem().GetUnit(); + mOwner = nullptr; + mIsAnimValItem = false; +} + +SVGLength DOMSVGLength::ToSVGLength() { + if (nsCOMPtr svg = do_QueryInterface(mOwner)) { + SVGAnimatedLength* length = svg->GetAnimatedLength(mAttrEnum); + if (mIsAnimValItem) { + return SVGLength(length->GetAnimValInSpecifiedUnits(), + length->GetAnimUnitType()); + } + return SVGLength(length->GetBaseValInSpecifiedUnits(), + length->GetBaseUnitType()); + } + return HasOwner() ? InternalItem() : SVGLength(mValue, mUnit); +} + +bool DOMSVGLength::IsAnimating() const { + if (nsCOMPtr lengthList = do_QueryInterface(mOwner)) { + return lengthList->IsAnimating(); + } + nsCOMPtr svg = do_QueryInterface(mOwner); + return svg && svg->GetAnimatedLength(mAttrEnum)->IsAnimated(); +} + +SVGElement* DOMSVGLength::Element() { + if (nsCOMPtr lengthList = do_QueryInterface(mOwner)) { + return lengthList->Element(); + } + nsCOMPtr svg = do_QueryInterface(mOwner); + return svg; +} + +SVGLength& DOMSVGLength::InternalItem() { + nsCOMPtr lengthList = do_QueryInterface(mOwner); + SVGAnimatedLengthList* alist = + lengthList->Element()->GetAnimatedLengthList(mAttrEnum); + return mIsAnimValItem && alist->mAnimVal ? (*alist->mAnimVal)[mListIndex] + : alist->mBaseVal[mListIndex]; +} + +void DOMSVGLength::FlushIfNeeded() { + auto MaybeFlush = [](uint16_t aUnitType, SVGElement* aSVGElement) { + FlushType flushType; + if (SVGLength::IsPercentageUnit(aUnitType)) { + flushType = FlushType::Layout; + } else if (SVGLength::IsFontRelativeUnit(aUnitType)) { + flushType = FlushType::Style; + } else { + return; + } + if (auto* currentDoc = aSVGElement->GetComposedDoc()) { + currentDoc->FlushPendingNotifications(flushType); + } + }; + + if (nsCOMPtr svg = do_QueryInterface(mOwner)) { + if (mIsAnimValItem) { + MaybeFlush(svg->GetAnimatedLength(mAttrEnum)->GetAnimUnitType(), svg); + } else { + MaybeFlush(svg->GetAnimatedLength(mAttrEnum)->GetBaseUnitType(), svg); + } + } + if (nsCOMPtr lengthList = do_QueryInterface(mOwner)) { + MaybeFlush(InternalItem().GetUnit(), lengthList->Element()); + } +} + +#ifdef DEBUG +bool DOMSVGLength::IndexIsValid() { + nsCOMPtr lengthList = do_QueryInterface(mOwner); + SVGAnimatedLengthList* alist = + lengthList->Element()->GetAnimatedLengthList(mAttrEnum); + return (mIsAnimValItem && mListIndex < alist->GetAnimValue().Length()) || + (!mIsAnimValItem && mListIndex < alist->GetBaseValue().Length()); +} +#endif + +} // namespace mozilla::dom -- cgit v1.2.3