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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user