Bug 1340422 - Part 7: Notify style system when SMIL animation changes d attribute. r=emilio

So we update d property in the style system as well. This makes sure we
have the correct computed style and the correct rendering result.

Differential Revision: https://phabricator.services.mozilla.com/D115570
This commit is contained in:
Boris Chiou
2021-06-09 21:17:23 +00:00
parent c74f5d831c
commit 1b22d5731b
8 changed files with 186 additions and 4 deletions

View File

@@ -1120,6 +1120,24 @@ bool SVGElement::UpdateDeclarationBlockFromLength(
return true;
}
/* static */
bool SVGElement::UpdateDeclarationBlockFromPath(
DeclarationBlock& aBlock, const SVGAnimatedPathSegList& aPath,
ValToUse aValToUse) {
aBlock.AssertMutable();
const SVGPathData& pathData =
aValToUse == ValToUse::Anim ? aPath.GetAnimValue() : aPath.GetBaseValue();
// SVGPathData::mData is fallible but rust binding accepts nsTArray only, so
// we need to point to one or the other. Fortunately, fallible and infallible
// array types can be implicitly converted provided they are const.
const nsTArray<float>& asInFallibleArray = pathData.RawData();
Servo_DeclarationBlock_SetPathValue(aBlock.Raw(), eCSSProperty_d,
&asInFallibleArray);
return true;
}
//------------------------------------------------------------------------
// Helper class: MappedAttrParser, for parsing values of mapped attributes
@@ -1754,14 +1772,20 @@ void SVGElement::DidChangePathSegList(const nsAttrValue& aEmptyOrOldValue,
}
void SVGElement::DidAnimatePathSegList() {
MOZ_ASSERT(GetPathDataAttrName(), "Animating non-existent path data?");
nsStaticAtom* name = GetPathDataAttrName();
MOZ_ASSERT(name, "Animating non-existent path data?");
ClearAnyCachedPath();
nsIFrame* frame = GetPrimaryFrame();
// Notify style we have to update the d property because of SMIL animation.
if (StaticPrefs::layout_css_d_property_enabled() && name == nsGkAtoms::d) {
SMILOverrideStyle()->SetSMILValue(nsCSSPropertyID::eCSSProperty_d,
*GetAnimPathSegList());
return;
}
if (frame) {
frame->AttributeChanged(kNameSpaceID_None, GetPathDataAttrName(),
if (nsIFrame* frame = GetPrimaryFrame()) {
frame->AttributeChanged(kNameSpaceID_None, name,
MutationEvent_Binding::SMIL);
}
}

View File

@@ -200,6 +200,9 @@ class SVGElement : public SVGElementBase // nsIContent
nsCSSPropertyID aPropId,
const SVGAnimatedLength& aLength,
ValToUse aValToUse);
static bool UpdateDeclarationBlockFromPath(
DeclarationBlock& aBlock, const SVGAnimatedPathSegList& aPath,
ValToUse aValToUse);
nsAttrValue WillChangeLength(uint8_t aAttrEnum,
const mozAutoDocUpdate& aProofOfUpdate);

View File

@@ -123,6 +123,8 @@ class SVGPathData {
*/
uint32_t Length() const { return mData.Length(); }
const nsTArray<float>& RawData() const { return mData; }
const float& operator[](uint32_t aIndex) const { return mData[aIndex]; }
// Used by SMILCompositor to check if the cached base val is out of date

View File

@@ -13,6 +13,7 @@ headers = [
"mozilla/dom/KeyframeEffectBinding.h",
"mozilla/dom/MediaList.h",
"mozilla/dom/ShadowRoot.h",
"mozilla/dom/SVGPathSegBinding.h",
"mozilla/ipc/ByteBuf.h",
"mozilla/AnimationPropertySegment.h",
"mozilla/ComputedTiming.h",
@@ -184,6 +185,7 @@ whitelist-vars = [
"GECKO_IS_NIGHTLY",
"mozilla::detail::gGkAtoms",
"mozilla::detail::kGkAtomsArrayOffset",
"mozilla::dom::SVGPathSeg_Binding::PATHSEG_.*",
"mozilla::profiler::detail::RacyFeatures::sActiveAndFeatures",
]
# TODO(emilio): A bunch of types here can go away once we generate bindings and

View File

@@ -178,6 +178,14 @@ nsresult nsDOMCSSAttributeDeclaration::SetSMILValue(
});
}
nsresult nsDOMCSSAttributeDeclaration::SetSMILValue(
const nsCSSPropertyID /*aPropID*/, const SVGAnimatedPathSegList& aPath) {
return SetSMILValueHelper([&aPath](DeclarationBlock& aDecl) {
return SVGElement::UpdateDeclarationBlockFromPath(
aDecl, aPath, SVGElement::ValToUse::Anim);
});
}
void nsDOMCSSAttributeDeclaration::SetPropertyValue(
const nsCSSPropertyID aPropID, const nsACString& aValue,
nsIPrincipal* aSubjectPrincipal, ErrorResult& aRv) {

View File

@@ -19,6 +19,7 @@ namespace mozilla {
class SMILValue;
class SVGAnimatedLength;
class SVGAnimatedPathSegList;
namespace dom {
class DomGroup;
@@ -51,6 +52,8 @@ class nsDOMCSSAttributeDeclaration final : public nsDOMCSSDeclaration {
nsresult SetSMILValue(const nsCSSPropertyID aPropID, const SMILValue& aValue);
nsresult SetSMILValue(const nsCSSPropertyID aPropID,
const SVGAnimatedLength& aLength);
nsresult SetSMILValue(const nsCSSPropertyID,
const mozilla::SVGAnimatedPathSegList& aPath);
void SetPropertyValue(const nsCSSPropertyID aPropID, const nsACString& aValue,
nsIPrincipal* aSubjectPrincipal,

View File

@@ -60,6 +60,111 @@ impl SVGPathData {
SVGPathData(crate::ArcSlice::from_iter(result.into_iter()))
}
// FIXME: Bug 1714238, we may drop this once we use the same data structure for both SVG and
// CSS.
/// Decode the svg path raw data from Gecko.
#[cfg(feature = "gecko")]
pub fn decode_from_f32_array(path: &[f32]) -> Result<Self, ()> {
use crate::gecko_bindings::structs::dom::SVGPathSeg_Binding::*;
let mut result: Vec<PathCommand> = Vec::new();
let mut i: usize = 0;
while i < path.len() {
// See EncodeType() and DecodeType() in SVGPathSegUtils.h.
// We are using reinterpret_cast<> to encode and decode between u32 and f32, so here we
// use to_bits() to decode the type.
let seg_type = path[i].to_bits() as u16;
i = i + 1;
match seg_type {
PATHSEG_CLOSEPATH => result.push(PathCommand::ClosePath),
PATHSEG_MOVETO_ABS | PATHSEG_MOVETO_REL => {
debug_assert!(i + 1 < path.len());
result.push(PathCommand::MoveTo {
point: CoordPair::new(path[i], path[i + 1]),
absolute: IsAbsolute::new(seg_type == PATHSEG_MOVETO_ABS),
});
i = i + 2;
}
PATHSEG_LINETO_ABS | PATHSEG_LINETO_REL => {
debug_assert!(i + 1 < path.len());
result.push(PathCommand::LineTo {
point: CoordPair::new(path[i], path[i + 1]),
absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_ABS),
});
i = i + 2;
}
PATHSEG_CURVETO_CUBIC_ABS | PATHSEG_CURVETO_CUBIC_REL => {
debug_assert!(i + 5 < path.len());
result.push(PathCommand::CurveTo {
control1: CoordPair::new(path[i], path[i + 1]),
control2: CoordPair::new(path[i + 2], path[i + 3]),
point: CoordPair::new(path[i + 4], path[i + 5]),
absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_CUBIC_ABS),
});
i = i + 6;
}
PATHSEG_CURVETO_QUADRATIC_ABS | PATHSEG_CURVETO_QUADRATIC_REL => {
debug_assert!(i + 3 < path.len());
result.push(PathCommand::QuadBezierCurveTo {
control1: CoordPair::new(path[i], path[i + 1]),
point: CoordPair::new(path[i + 2], path[i + 3]),
absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_QUADRATIC_ABS),
});
i = i + 4;
}
PATHSEG_ARC_ABS | PATHSEG_ARC_REL => {
debug_assert!(i + 6 < path.len());
result.push(PathCommand::EllipticalArc {
rx: path[i],
ry: path[i + 1],
angle: path[i + 2],
large_arc_flag: ArcFlag(path[i + 3] != 0.0f32),
sweep_flag: ArcFlag(path[i + 4] != 0.0f32),
point: CoordPair::new(path[i + 5], path[i + 6]),
absolute: IsAbsolute::new(seg_type == PATHSEG_ARC_ABS),
});
i = i + 7;
}
PATHSEG_LINETO_HORIZONTAL_ABS | PATHSEG_LINETO_HORIZONTAL_REL => {
debug_assert!(i < path.len());
result.push(PathCommand::HorizontalLineTo {
x: path[i],
absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_HORIZONTAL_ABS),
});
i = i + 1;
}
PATHSEG_LINETO_VERTICAL_ABS | PATHSEG_LINETO_VERTICAL_REL => {
debug_assert!(i < path.len());
result.push(PathCommand::VerticalLineTo {
y: path[i],
absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_VERTICAL_ABS),
});
i = i + 1;
}
PATHSEG_CURVETO_CUBIC_SMOOTH_ABS | PATHSEG_CURVETO_CUBIC_SMOOTH_REL => {
debug_assert!(i + 3 < path.len());
result.push(PathCommand::SmoothCurveTo {
control2: CoordPair::new(path[i], path[i + 1]),
point: CoordPair::new(path[i + 2], path[i + 3]),
absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS),
});
i = i + 4;
}
PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS | PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL => {
debug_assert!(i + 1 < path.len());
result.push(PathCommand::SmoothQuadBezierCurveTo {
point: CoordPair::new(path[i], path[i + 1]),
absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS),
});
i = i + 2;
}
PATHSEG_UNKNOWN | _ => return Err(()),
}
}
Ok(SVGPathData(crate::ArcSlice::from_iter(result.into_iter())))
}
}
impl ToCss for SVGPathData {
@@ -506,6 +611,16 @@ impl IsAbsolute {
pub fn is_yes(&self) -> bool {
*self == IsAbsolute::Yes
}
/// Return Yes if value is true. Otherwise, return No.
#[inline]
fn new(value: bool) -> Self {
if value {
IsAbsolute::Yes
} else {
IsAbsolute::No
}
}
}
/// The path coord type.

View File

@@ -5153,6 +5153,31 @@ pub extern "C" fn Servo_DeclarationBlock_SetLengthValue(
})
}
#[no_mangle]
pub extern "C" fn Servo_DeclarationBlock_SetPathValue(
declarations: &RawServoDeclarationBlock,
property: nsCSSPropertyID,
path: &nsTArray<f32>,
) {
use style::properties::PropertyDeclaration;
use style::values::specified::DProperty;
// 1. Decode the path data from SVG.
let path = match specified::SVGPathData::decode_from_f32_array(path) {
Ok(p) => p,
Err(()) => return,
};
// 2. Set decoded path into style.
let long = get_longhand_from_id!(property);
let prop = match_wrap_declared! { long,
D => if path.0.is_empty() { DProperty::None } else { DProperty::Path(path) },
};
write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
decls.push(prop, Importance::Normal);
})
}
#[no_mangle]
pub extern "C" fn Servo_DeclarationBlock_SetNumberValue(
declarations: &RawServoDeclarationBlock,