summaryrefslogtreecommitdiffstats
path: root/dom/svg/DOMSVGLength.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /dom/svg/DOMSVGLength.cpp
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/svg/DOMSVGLength.cpp')
-rw-r--r--dom/svg/DOMSVGLength.cpp495
1 files changed, 495 insertions, 0 deletions
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<SVGAnimatedLength, DOMSVGLength>
+ 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<DOMSVGLengthList> 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<SVGElement> svg = do_QueryInterface(mOwner)) {
+ auto& table = mIsAnimValItem ? sAnimSVGLengthTearOffTable
+ : sBaseSVGLengthTearOffTable;
+ table.RemoveTearoff(svg->GetAnimatedLength(mAttrEnum));
+ }
+}
+
+already_AddRefed<DOMSVGLength> DOMSVGLength::GetTearOff(SVGAnimatedLength* aVal,
+ SVGElement* aSVGElement,
+ bool aAnimVal) {
+ auto& table =
+ aAnimVal ? sAnimSVGLengthTearOffTable : sBaseSVGLengthTearOffTable;
+ RefPtr<DOMSVGLength> 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<SVGElement> 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<SVGElement> 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<SVGElement> svg = do_QueryInterface(mOwner)) {
+ SVGAnimatedLength* length = svg->GetAnimatedLength(mAttrEnum);
+ return mIsAnimValItem ? length->GetAnimValue(svg)
+ : length->GetBaseValue(svg);
+ }
+
+ if (nsCOMPtr<DOMSVGLengthList> 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<SVGElement> 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<DOMSVGLengthList> 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<SVGElement> 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<SVGElement> 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<SVGElement> 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<SVGElement> 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<SVGElement> 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<SVGElement> 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<DOMSVGLengthList> 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<JSObject*> 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<SVGElement> 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<DOMSVGLengthList> lengthList = do_QueryInterface(mOwner)) {
+ return lengthList->IsAnimating();
+ }
+ nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner);
+ return svg && svg->GetAnimatedLength(mAttrEnum)->IsAnimated();
+}
+
+SVGElement* DOMSVGLength::Element() {
+ if (nsCOMPtr<DOMSVGLengthList> lengthList = do_QueryInterface(mOwner)) {
+ return lengthList->Element();
+ }
+ nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner);
+ return svg;
+}
+
+SVGLength& DOMSVGLength::InternalItem() {
+ nsCOMPtr<DOMSVGLengthList> 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<SVGElement> svg = do_QueryInterface(mOwner)) {
+ if (mIsAnimValItem) {
+ MaybeFlush(svg->GetAnimatedLength(mAttrEnum)->GetAnimUnitType(), svg);
+ } else {
+ MaybeFlush(svg->GetAnimatedLength(mAttrEnum)->GetBaseUnitType(), svg);
+ }
+ }
+ if (nsCOMPtr<DOMSVGLengthList> lengthList = do_QueryInterface(mOwner)) {
+ MaybeFlush(InternalItem().GetUnit(), lengthList->Element());
+ }
+}
+
+#ifdef DEBUG
+bool DOMSVGLength::IndexIsValid() {
+ nsCOMPtr<DOMSVGLengthList> 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