mirror of
https://github.com/lexmount/moli.git
synced 2026-09-30 00:01:36 +00:00
fix(svg): implement marker orientation angle APIs
This commit is contained in:
@@ -103,6 +103,7 @@ pub fn svg_element_interface_name(local_name: &str) -> &'static str {
|
||||
"image" => "SVGImageElement",
|
||||
"line" => "SVGLineElement",
|
||||
"linearGradient" => "SVGLinearGradientElement",
|
||||
"marker" => "SVGMarkerElement",
|
||||
"mask" => "SVGMaskElement",
|
||||
"metadata" => "SVGMetadataElement",
|
||||
"path" => "SVGPathElement",
|
||||
|
||||
@@ -277,6 +277,7 @@ fn svg_element_interface_name_specializes_standard_elements() {
|
||||
"SVGLinearGradientElement"
|
||||
);
|
||||
assert_eq!(svg_element_interface_name("metadata"), "SVGMetadataElement");
|
||||
assert_eq!(svg_element_interface_name("marker"), "SVGMarkerElement");
|
||||
assert_eq!(svg_element_interface_name("mask"), "SVGMaskElement");
|
||||
assert_eq!(svg_element_interface_name("path"), "SVGPathElement");
|
||||
assert_eq!(svg_element_interface_name("pattern"), "SVGPatternElement");
|
||||
|
||||
@@ -179,6 +179,7 @@ const NODE_BRIDGE_DESCRIPTORS: &[BridgeDescriptor] = &[
|
||||
ELEMENT_GROUPS,
|
||||
),
|
||||
descriptor(web_api_interfaces::SVGMaskElement::DESCRIPTOR, ELEMENT_GROUPS),
|
||||
descriptor(web_api_interfaces::SVGMarkerElement::DESCRIPTOR, ELEMENT_GROUPS),
|
||||
specialized_descriptor(
|
||||
web_api_interfaces::SVGPathElement::DESCRIPTOR,
|
||||
ELEMENT_GROUPS,
|
||||
|
||||
@@ -1725,10 +1725,12 @@ pub(crate) fn finish_context_bootstrap(
|
||||
("XPathEvaluator", "XPathEvaluator"),
|
||||
("XPathResult", "XPathResult"),
|
||||
("SVGLength", "SVGLength"),
|
||||
("SVGAngle", "SVGAngle"),
|
||||
("SVGNumber", "SVGNumber"),
|
||||
("SVGRect", "SVGRect"),
|
||||
("SVGAnimatedString", "SVGAnimatedString"),
|
||||
("SVGAnimatedLength", "SVGAnimatedLength"),
|
||||
("SVGAnimatedAngle", "SVGAnimatedAngle"),
|
||||
("SVGLengthList", "SVGLengthList"),
|
||||
("SVGAnimatedLengthList", "SVGAnimatedLengthList"),
|
||||
("SVGAnimatedNumber", "SVGAnimatedNumber"),
|
||||
|
||||
@@ -88,6 +88,10 @@ const CONSTRUCTOR_SPECS_BEFORE_STREAMS: &[ConstructorSpec] = &[
|
||||
interface: web_api_interfaces::SVGLength::DESCRIPTOR,
|
||||
kind: ConstructorKind::Illegal,
|
||||
},
|
||||
ConstructorSpec {
|
||||
interface: web_api_interfaces::SVGAngle::DESCRIPTOR,
|
||||
kind: ConstructorKind::Illegal,
|
||||
},
|
||||
ConstructorSpec {
|
||||
interface: web_api_interfaces::SVGNumber::DESCRIPTOR,
|
||||
kind: ConstructorKind::Illegal,
|
||||
@@ -104,6 +108,10 @@ const CONSTRUCTOR_SPECS_BEFORE_STREAMS: &[ConstructorSpec] = &[
|
||||
interface: web_api_interfaces::SVGAnimatedLength::DESCRIPTOR,
|
||||
kind: ConstructorKind::Illegal,
|
||||
},
|
||||
ConstructorSpec {
|
||||
interface: web_api_interfaces::SVGAnimatedAngle::DESCRIPTOR,
|
||||
kind: ConstructorKind::Illegal,
|
||||
},
|
||||
ConstructorSpec {
|
||||
interface: web_api_interfaces::SVGLengthList::DESCRIPTOR,
|
||||
kind: ConstructorKind::Illegal,
|
||||
|
||||
@@ -58,6 +58,42 @@ struct SvgLengthTemplateMethodsDeclaration {
|
||||
convert_to_specified_units: (),
|
||||
}
|
||||
|
||||
#[derive(WebApiFunctionTemplate)]
|
||||
#[webapi(interface = web_api_interfaces::SVGAngle, enumerable)]
|
||||
struct SvgAngleTemplateMethodsDeclaration {
|
||||
#[webapi(constant = "SVG_ANGLETYPE_UNKNOWN", value = SVG_ANGLE_TYPE_UNKNOWN)]
|
||||
angle_type_unknown: (),
|
||||
|
||||
#[webapi(
|
||||
constant = "SVG_ANGLETYPE_UNSPECIFIED",
|
||||
value = SVG_ANGLE_TYPE_UNSPECIFIED
|
||||
)]
|
||||
angle_type_unspecified: (),
|
||||
|
||||
#[webapi(constant = "SVG_ANGLETYPE_DEG", value = SVG_ANGLE_TYPE_DEG)]
|
||||
angle_type_deg: (),
|
||||
|
||||
#[webapi(constant = "SVG_ANGLETYPE_RAD", value = SVG_ANGLE_TYPE_RAD)]
|
||||
angle_type_rad: (),
|
||||
|
||||
#[webapi(constant = "SVG_ANGLETYPE_GRAD", value = SVG_ANGLE_TYPE_GRAD)]
|
||||
angle_type_grad: (),
|
||||
|
||||
#[webapi(
|
||||
method = "newValueSpecifiedUnits",
|
||||
length = 2,
|
||||
callback = svg_angle_new_value_specified_units_callback
|
||||
)]
|
||||
new_value_specified_units: (),
|
||||
|
||||
#[webapi(
|
||||
method = "convertToSpecifiedUnits",
|
||||
length = 1,
|
||||
callback = svg_angle_convert_to_specified_units_callback
|
||||
)]
|
||||
convert_to_specified_units: (),
|
||||
}
|
||||
|
||||
#[derive(WebApiFunctionTemplate)]
|
||||
#[webapi(interface = web_api_interfaces::SVGLengthList, enumerable)]
|
||||
struct SvgLengthListTemplateMethodsDeclaration {
|
||||
@@ -525,6 +561,66 @@ struct SvgGradientElementTemplateConstantsDeclaration {
|
||||
spread_method_repeat: (),
|
||||
}
|
||||
|
||||
#[derive(WebApiFunctionTemplate)]
|
||||
#[webapi(interface = web_api_interfaces::SVGMarkerElement, enumerable)]
|
||||
struct SvgMarkerElementTemplateMethodsDeclaration {
|
||||
#[webapi(
|
||||
constant = "SVG_MARKERUNITS_UNKNOWN",
|
||||
value = SVG_MARKER_UNITS_UNKNOWN
|
||||
)]
|
||||
marker_units_unknown: (),
|
||||
|
||||
#[webapi(
|
||||
constant = "SVG_MARKERUNITS_USERSPACEONUSE",
|
||||
value = SVG_MARKER_UNITS_USER_SPACE_ON_USE
|
||||
)]
|
||||
marker_units_user_space_on_use: (),
|
||||
|
||||
#[webapi(
|
||||
constant = "SVG_MARKERUNITS_STROKEWIDTH",
|
||||
value = SVG_MARKER_UNITS_STROKE_WIDTH
|
||||
)]
|
||||
marker_units_stroke_width: (),
|
||||
|
||||
#[webapi(
|
||||
constant = "SVG_MARKER_ORIENT_UNKNOWN",
|
||||
value = SVG_MARKER_ORIENT_UNKNOWN
|
||||
)]
|
||||
marker_orient_unknown: (),
|
||||
|
||||
#[webapi(
|
||||
constant = "SVG_MARKER_ORIENT_AUTO",
|
||||
value = SVG_MARKER_ORIENT_AUTO
|
||||
)]
|
||||
marker_orient_auto: (),
|
||||
|
||||
#[webapi(
|
||||
constant = "SVG_MARKER_ORIENT_ANGLE",
|
||||
value = SVG_MARKER_ORIENT_ANGLE
|
||||
)]
|
||||
marker_orient_angle: (),
|
||||
|
||||
#[webapi(
|
||||
constant = "SVG_MARKER_ORIENT_AUTO_START_REVERSE",
|
||||
value = SVG_MARKER_ORIENT_AUTO_START_REVERSE
|
||||
)]
|
||||
marker_orient_auto_start_reverse: (),
|
||||
|
||||
#[webapi(
|
||||
method = "setOrientToAuto",
|
||||
length = 0,
|
||||
callback = svg_marker_set_orient_to_auto_callback
|
||||
)]
|
||||
set_orient_to_auto: (),
|
||||
|
||||
#[webapi(
|
||||
method = "setOrientToAngle",
|
||||
length = 1,
|
||||
callback = svg_marker_set_orient_to_angle_callback
|
||||
)]
|
||||
set_orient_to_angle: (),
|
||||
}
|
||||
|
||||
#[derive(WebApiFunctionTemplate)]
|
||||
#[webapi(interface = web_api_interfaces::SVGSVGElement, enumerable)]
|
||||
struct SvgSvgElementTemplateMethodsDeclaration {
|
||||
@@ -542,6 +638,13 @@ struct SvgSvgElementTemplateMethodsDeclaration {
|
||||
)]
|
||||
deselect_all: (),
|
||||
|
||||
#[webapi(
|
||||
method = "createSVGAngle",
|
||||
length = 0,
|
||||
callback = svg_svg_element_create_angle_callback
|
||||
)]
|
||||
create_svg_angle: (),
|
||||
|
||||
#[webapi(
|
||||
method = "createSVGMatrix",
|
||||
length = 0,
|
||||
@@ -599,6 +702,41 @@ struct SvgLengthTemplateAccessorsDeclaration {
|
||||
value_as_string: (),
|
||||
}
|
||||
|
||||
#[derive(WebApiFunctionTemplate)]
|
||||
#[webapi(interface = web_api_interfaces::SVGAngle, enumerable)]
|
||||
struct SvgAngleTemplateAccessorsDeclaration {
|
||||
#[webapi(
|
||||
accessor_property = "unitType",
|
||||
getter = svg_angle_getter,
|
||||
data = callback_data_index_value(scope, 0)
|
||||
)]
|
||||
unit_type: (),
|
||||
|
||||
#[webapi(
|
||||
accessor_property = "value",
|
||||
getter = svg_angle_getter,
|
||||
setter = svg_angle_setter,
|
||||
data = callback_data_index_value(scope, 1)
|
||||
)]
|
||||
value: (),
|
||||
|
||||
#[webapi(
|
||||
accessor_property = "valueInSpecifiedUnits",
|
||||
getter = svg_angle_getter,
|
||||
setter = svg_angle_setter,
|
||||
data = callback_data_index_value(scope, 2)
|
||||
)]
|
||||
value_in_specified_units: (),
|
||||
|
||||
#[webapi(
|
||||
accessor_property = "valueAsString",
|
||||
getter = svg_angle_getter,
|
||||
setter = svg_angle_setter,
|
||||
data = callback_data_index_value(scope, 3)
|
||||
)]
|
||||
value_as_string: (),
|
||||
}
|
||||
|
||||
#[derive(WebApiFunctionTemplate)]
|
||||
#[webapi(interface = web_api_interfaces::SVGAnimatedString, enumerable)]
|
||||
struct SvgAnimatedStringTemplateAccessorsDeclaration {
|
||||
@@ -655,6 +793,24 @@ struct SvgAnimatedLengthTemplateAccessorsDeclaration {
|
||||
anim_val: (),
|
||||
}
|
||||
|
||||
#[derive(WebApiFunctionTemplate)]
|
||||
#[webapi(interface = web_api_interfaces::SVGAnimatedAngle, enumerable)]
|
||||
struct SvgAnimatedAngleTemplateAccessorsDeclaration {
|
||||
#[webapi(
|
||||
accessor_property = "baseVal",
|
||||
getter = svg_animated_angle_getter,
|
||||
data = callback_data_index_value(scope, 0)
|
||||
)]
|
||||
base_val: (),
|
||||
|
||||
#[webapi(
|
||||
accessor_property = "animVal",
|
||||
getter = svg_animated_angle_getter,
|
||||
data = callback_data_index_value(scope, 1)
|
||||
)]
|
||||
anim_val: (),
|
||||
}
|
||||
|
||||
#[derive(WebApiFunctionTemplate)]
|
||||
#[webapi(interface = web_api_interfaces::SVGNumber, enumerable)]
|
||||
struct SvgNumberTemplateAccessorsDeclaration {
|
||||
@@ -1140,6 +1296,30 @@ struct SvgGraphicsBoxElementPrototypeAccessorsDeclaration {
|
||||
height: (),
|
||||
}
|
||||
|
||||
#[derive(WebApiFunctionTemplate)]
|
||||
#[webapi(interface = web_api_interfaces::SVGMarkerElement, enumerable)]
|
||||
struct SvgMarkerElementPrototypeAccessorsDeclaration {
|
||||
#[webapi(
|
||||
accessor_property = "markerUnits",
|
||||
getter = svg_element_animated_enumeration_getter,
|
||||
data = callback_data_index_value(scope, 10)
|
||||
)]
|
||||
marker_units: (),
|
||||
|
||||
#[webapi(
|
||||
accessor_property = "orientType",
|
||||
getter = svg_element_animated_enumeration_getter,
|
||||
data = callback_data_index_value(scope, 11)
|
||||
)]
|
||||
orient_type: (),
|
||||
|
||||
#[webapi(
|
||||
accessor_property = "orientAngle",
|
||||
getter = svg_marker_orient_angle_getter
|
||||
)]
|
||||
orient_angle: (),
|
||||
}
|
||||
|
||||
#[derive(WebApiFunctionTemplate)]
|
||||
#[webapi(name = "SVGElement", enumerable)]
|
||||
struct SvgElementPrototypeAccessorsDeclaration {
|
||||
@@ -1173,6 +1353,16 @@ pub(super) fn install_svg_length_bindings<'s>(
|
||||
SvgLengthTemplateMethodsDeclaration::initialize_prototype_template(scope, proto);
|
||||
}
|
||||
|
||||
pub(super) fn install_svg_angle_bindings<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_, ()>,
|
||||
template: v8::Local<'s, v8::FunctionTemplate>,
|
||||
) {
|
||||
let proto = template.prototype_template(scope);
|
||||
SvgAngleTemplateMethodsDeclaration::initialize_template(scope, template);
|
||||
SvgAngleTemplateAccessorsDeclaration::initialize_prototype_template(scope, proto);
|
||||
SvgAngleTemplateMethodsDeclaration::initialize_prototype_template(scope, proto);
|
||||
}
|
||||
|
||||
pub(super) fn install_svg_animated_length_bindings<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_, ()>,
|
||||
template: v8::Local<'s, v8::FunctionTemplate>,
|
||||
@@ -1181,6 +1371,14 @@ pub(super) fn install_svg_animated_length_bindings<'s>(
|
||||
SvgAnimatedLengthTemplateAccessorsDeclaration::initialize_prototype_template(scope, proto);
|
||||
}
|
||||
|
||||
pub(super) fn install_svg_animated_angle_bindings<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_, ()>,
|
||||
template: v8::Local<'s, v8::FunctionTemplate>,
|
||||
) {
|
||||
let proto = template.prototype_template(scope);
|
||||
SvgAnimatedAngleTemplateAccessorsDeclaration::initialize_prototype_template(scope, proto);
|
||||
}
|
||||
|
||||
pub(super) fn install_svg_number_bindings<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_, ()>,
|
||||
template: v8::Local<'s, v8::FunctionTemplate>,
|
||||
@@ -1359,6 +1557,15 @@ pub(super) fn install_svg_text_content_element_bindings<'s>(
|
||||
SvgTextContentElementTemplateMethodsDeclaration::initialize_prototype_template(scope, proto);
|
||||
}
|
||||
|
||||
pub(super) fn install_svg_marker_element_bindings<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_, ()>,
|
||||
template: v8::Local<'s, v8::FunctionTemplate>,
|
||||
) {
|
||||
let proto = template.prototype_template(scope);
|
||||
SvgMarkerElementTemplateMethodsDeclaration::initialize_template(scope, template);
|
||||
SvgMarkerElementTemplateMethodsDeclaration::initialize_prototype_template(scope, proto);
|
||||
}
|
||||
|
||||
pub(super) fn install_svg_svg_element_bindings(
|
||||
scope: &mut v8::PinScope<'_, '_, ()>,
|
||||
template: v8::Local<'_, v8::FunctionTemplate>,
|
||||
@@ -1452,6 +1659,11 @@ pub(super) fn install_svg_element_accessor_bindings<'s>(
|
||||
scope, prototype,
|
||||
);
|
||||
}
|
||||
"SVGMarkerElement" => {
|
||||
SvgMarkerElementPrototypeAccessorsDeclaration::initialize_prototype_template(
|
||||
scope, prototype,
|
||||
);
|
||||
}
|
||||
"SVGElement" => {
|
||||
SvgElementPrototypeAccessorsDeclaration::initialize_prototype_template(
|
||||
scope, prototype,
|
||||
|
||||
@@ -118,6 +118,18 @@ struct SvgAnimatedLengthObjectDeclaration<'scope> {
|
||||
anim_val: v8::Local<'scope, v8::Object>,
|
||||
}
|
||||
|
||||
#[derive(WebApiObject)]
|
||||
#[webapi(
|
||||
interface = web_api_interfaces::SVGAnimatedAngle,
|
||||
fallback_to_string_tag = "SVGAnimatedAngle"
|
||||
)]
|
||||
struct SvgAnimatedAngleObjectDeclaration<'scope> {
|
||||
#[webapi(slot = SVG_ANIMATED_ANGLE_BASE_VAL_SLOT)]
|
||||
base_val: v8::Local<'scope, v8::Object>,
|
||||
#[webapi(slot = SVG_ANIMATED_ANGLE_ANIM_VAL_SLOT)]
|
||||
anim_val: v8::Local<'scope, v8::Object>,
|
||||
}
|
||||
|
||||
#[derive(WebApiObject)]
|
||||
#[webapi(interface = web_api_interfaces::SVGLength, fallback_to_string_tag = "SVGLength")]
|
||||
struct SvgLengthObjectDeclaration {
|
||||
@@ -129,6 +141,21 @@ struct SvgLengthObjectDeclaration {
|
||||
value_as_string: String,
|
||||
}
|
||||
|
||||
#[derive(WebApiObject)]
|
||||
#[webapi(interface = web_api_interfaces::SVGAngle, fallback_to_string_tag = "SVGAngle")]
|
||||
struct SvgAngleObjectDeclaration {
|
||||
#[webapi(slot = SVG_ANGLE_UNIT_TYPE_SLOT)]
|
||||
unit_type: u32,
|
||||
#[webapi(slot = SVG_ANGLE_VALUE_SLOT)]
|
||||
value: f64,
|
||||
#[webapi(slot = SVG_ANGLE_VALUE_IN_SPECIFIED_UNITS_SLOT)]
|
||||
value_in_specified_units: f64,
|
||||
#[webapi(slot = SVG_ANGLE_VALUE_AS_STRING_SLOT)]
|
||||
value_as_string: String,
|
||||
#[webapi(slot = SVG_ANGLE_READ_ONLY_SLOT)]
|
||||
read_only: bool,
|
||||
}
|
||||
|
||||
#[derive(WebApiObject)]
|
||||
#[webapi(
|
||||
interface = web_api_interfaces::SVGAnimatedLengthList,
|
||||
@@ -633,6 +660,21 @@ pub(super) fn parse_svg_animated_enumeration(
|
||||
(SvgAnimatedEnumerationKind::LengthAdjust, "spacingAndGlyphs") => {
|
||||
Some(SVG_LENGTH_ADJUST_SPACING_AND_GLYPHS)
|
||||
}
|
||||
(SvgAnimatedEnumerationKind::MarkerUnits, "userSpaceOnUse") => {
|
||||
Some(SVG_MARKER_UNITS_USER_SPACE_ON_USE)
|
||||
}
|
||||
(SvgAnimatedEnumerationKind::MarkerUnits, "strokeWidth") => {
|
||||
Some(SVG_MARKER_UNITS_STROKE_WIDTH)
|
||||
}
|
||||
(SvgAnimatedEnumerationKind::MarkerOrient, "auto") => Some(SVG_MARKER_ORIENT_AUTO),
|
||||
(SvgAnimatedEnumerationKind::MarkerOrient, "auto-start-reverse") => {
|
||||
Some(SVG_MARKER_ORIENT_AUTO_START_REVERSE)
|
||||
}
|
||||
(SvgAnimatedEnumerationKind::MarkerOrient, value)
|
||||
if parse_svg_angle_value(value).is_some() =>
|
||||
{
|
||||
Some(SVG_MARKER_ORIENT_ANGLE)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -655,6 +697,17 @@ pub(super) fn serialize_svg_animated_enumeration(
|
||||
(SvgAnimatedEnumerationKind::LengthAdjust, SVG_LENGTH_ADJUST_SPACING_AND_GLYPHS) => {
|
||||
Some("spacingAndGlyphs")
|
||||
}
|
||||
(SvgAnimatedEnumerationKind::MarkerUnits, SVG_MARKER_UNITS_USER_SPACE_ON_USE) => {
|
||||
Some("userSpaceOnUse")
|
||||
}
|
||||
(SvgAnimatedEnumerationKind::MarkerUnits, SVG_MARKER_UNITS_STROKE_WIDTH) => {
|
||||
Some("strokeWidth")
|
||||
}
|
||||
(SvgAnimatedEnumerationKind::MarkerOrient, SVG_MARKER_ORIENT_AUTO) => Some("auto"),
|
||||
(SvgAnimatedEnumerationKind::MarkerOrient, SVG_MARKER_ORIENT_ANGLE) => Some("0"),
|
||||
(SvgAnimatedEnumerationKind::MarkerOrient, SVG_MARKER_ORIENT_AUTO_START_REVERSE) => {
|
||||
Some("auto-start-reverse")
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -919,6 +972,44 @@ pub(super) fn build_svg_animated_length_for_attribute<'s>(
|
||||
.expect("SVGAnimatedLength declaration should bind")
|
||||
}
|
||||
|
||||
pub(super) fn build_svg_animated_angle_for_attribute<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
owner: v8::Local<'s, v8::Object>,
|
||||
attribute: &str,
|
||||
) -> v8::Local<'s, v8::Object> {
|
||||
let parsed = svg_owner_attribute_value(scope, owner, attribute)
|
||||
.as_deref()
|
||||
.and_then(parse_svg_orient_angle_value)
|
||||
.unwrap_or_default();
|
||||
let base_val = build_svg_angle_from_parsed(scope, &parsed, false);
|
||||
set_svg_angle_owner_attribute(scope, base_val, owner, attribute);
|
||||
let anim_val = build_svg_angle_from_parsed(scope, &parsed, true);
|
||||
set_svg_angle_owner_attribute(scope, anim_val, owner, attribute);
|
||||
SvgAnimatedAngleObjectDeclaration::new(base_val, anim_val)
|
||||
.bind(scope)
|
||||
.expect("SVGAnimatedAngle declaration should bind")
|
||||
}
|
||||
|
||||
pub(super) fn build_svg_angle<'s>(scope: &mut v8::PinScope<'s, '_>) -> v8::Local<'s, v8::Object> {
|
||||
build_svg_angle_from_parsed(scope, &SvgParsedAngle::default(), false)
|
||||
}
|
||||
|
||||
pub(super) fn build_svg_angle_from_parsed<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
parsed: &SvgParsedAngle,
|
||||
read_only: bool,
|
||||
) -> v8::Local<'s, v8::Object> {
|
||||
SvgAngleObjectDeclaration::new(
|
||||
parsed.unit_type,
|
||||
parsed.value,
|
||||
parsed.value_in_specified_units,
|
||||
parsed.value_as_string.clone(),
|
||||
read_only,
|
||||
)
|
||||
.bind(scope)
|
||||
.expect("SVGAngle declaration should bind")
|
||||
}
|
||||
|
||||
pub(super) fn build_svg_length<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
value: f64,
|
||||
@@ -2150,6 +2241,309 @@ pub(super) fn svg_animated_length_attribute_slot(name: &str) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) struct SvgParsedAngle {
|
||||
value: f64,
|
||||
value_in_specified_units: f64,
|
||||
unit_type: u32,
|
||||
value_as_string: String,
|
||||
}
|
||||
|
||||
impl Default for SvgParsedAngle {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
value: 0.0,
|
||||
value_in_specified_units: 0.0,
|
||||
unit_type: SVG_ANGLE_TYPE_UNSPECIFIED,
|
||||
value_as_string: "0".to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn parse_svg_orient_angle_value(raw: &str) -> Option<SvgParsedAngle> {
|
||||
if matches!(
|
||||
raw.trim().to_ascii_lowercase().as_str(),
|
||||
"auto" | "auto-start-reverse"
|
||||
) {
|
||||
return Some(SvgParsedAngle::default());
|
||||
}
|
||||
parse_svg_angle_value(raw)
|
||||
}
|
||||
|
||||
pub(super) fn parse_svg_angle_value(raw: &str) -> Option<SvgParsedAngle> {
|
||||
let raw = raw.trim();
|
||||
if raw.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let lowercase = raw.to_ascii_lowercase();
|
||||
let (number_raw, unit_type, degrees_per_unit) = if lowercase.ends_with("deg") {
|
||||
(&raw[..raw.len() - 3], SVG_ANGLE_TYPE_DEG, 1.0)
|
||||
} else if lowercase.ends_with("grad") {
|
||||
(&raw[..raw.len() - 4], SVG_ANGLE_TYPE_GRAD, 0.9)
|
||||
} else if lowercase.ends_with("rad") {
|
||||
(
|
||||
&raw[..raw.len() - 3],
|
||||
SVG_ANGLE_TYPE_RAD,
|
||||
180.0 / std::f64::consts::PI,
|
||||
)
|
||||
} else if lowercase.ends_with("turn") {
|
||||
(&raw[..raw.len() - 4], SVG_ANGLE_TYPE_UNKNOWN, 360.0)
|
||||
} else {
|
||||
(raw, SVG_ANGLE_TYPE_UNSPECIFIED, 1.0)
|
||||
};
|
||||
let value_in_specified_units = moli_css_parse::parse_number(number_raw.trim())?;
|
||||
Some(SvgParsedAngle {
|
||||
value: value_in_specified_units * degrees_per_unit,
|
||||
value_in_specified_units,
|
||||
unit_type,
|
||||
value_as_string: raw.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn svg_angle_number_slot<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
object: v8::Local<'s, v8::Object>,
|
||||
slot: &str,
|
||||
) -> Option<f64> {
|
||||
get_private_value(scope, object, slot)?.number_value(scope)
|
||||
}
|
||||
|
||||
pub(super) fn svg_angle_string_slot<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
object: v8::Local<'s, v8::Object>,
|
||||
slot: &str,
|
||||
) -> Option<String> {
|
||||
get_private_value(scope, object, slot)
|
||||
.and_then(|value| value.to_string(scope))
|
||||
.map(|value| value.to_rust_string_lossy(scope))
|
||||
}
|
||||
|
||||
pub(super) fn svg_angle_is_read_only<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
object: v8::Local<'s, v8::Object>,
|
||||
) -> bool {
|
||||
get_private_value(scope, object, SVG_ANGLE_READ_ONLY_SLOT).is_some_and(|value| value.is_true())
|
||||
}
|
||||
|
||||
pub(super) fn set_svg_angle_parsed_value(
|
||||
scope: &mut v8::PinScope<'_, '_>,
|
||||
object: v8::Local<'_, v8::Object>,
|
||||
parsed: &SvgParsedAngle,
|
||||
) {
|
||||
set_private_value(
|
||||
scope,
|
||||
object,
|
||||
SVG_ANGLE_UNIT_TYPE_SLOT,
|
||||
v8::Integer::new_from_unsigned(scope, parsed.unit_type).into(),
|
||||
);
|
||||
set_private_value(
|
||||
scope,
|
||||
object,
|
||||
SVG_ANGLE_VALUE_SLOT,
|
||||
v8::Number::new(scope, parsed.value).into(),
|
||||
);
|
||||
set_private_value(
|
||||
scope,
|
||||
object,
|
||||
SVG_ANGLE_VALUE_IN_SPECIFIED_UNITS_SLOT,
|
||||
v8::Number::new(scope, parsed.value_in_specified_units).into(),
|
||||
);
|
||||
set_private_value(
|
||||
scope,
|
||||
object,
|
||||
SVG_ANGLE_VALUE_AS_STRING_SLOT,
|
||||
v8_string(scope, &parsed.value_as_string)
|
||||
.unwrap_or_else(|| v8str(scope, "0"))
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
fn svg_angle_current_unit<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
object: v8::Local<'s, v8::Object>,
|
||||
) -> (u32, bool) {
|
||||
let unit_type = svg_angle_number_slot(scope, object, SVG_ANGLE_UNIT_TYPE_SLOT)
|
||||
.unwrap_or(SVG_ANGLE_TYPE_UNSPECIFIED as f64) as u32;
|
||||
let is_turn = unit_type == SVG_ANGLE_TYPE_UNKNOWN
|
||||
&& svg_angle_string_slot(scope, object, SVG_ANGLE_VALUE_AS_STRING_SLOT)
|
||||
.is_some_and(|value| value.trim().to_ascii_lowercase().ends_with("turn"));
|
||||
(unit_type, is_turn)
|
||||
}
|
||||
|
||||
fn svg_angle_degrees_per_unit(unit_type: u32, is_turn: bool) -> f64 {
|
||||
match unit_type {
|
||||
SVG_ANGLE_TYPE_RAD => 180.0 / std::f64::consts::PI,
|
||||
SVG_ANGLE_TYPE_GRAD => 0.9,
|
||||
SVG_ANGLE_TYPE_UNKNOWN if is_turn => 360.0,
|
||||
_ => 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
fn svg_angle_unit_suffix(unit_type: u32, is_turn: bool) -> &'static str {
|
||||
match unit_type {
|
||||
SVG_ANGLE_TYPE_DEG => "deg",
|
||||
SVG_ANGLE_TYPE_RAD => "rad",
|
||||
SVG_ANGLE_TYPE_GRAD => "grad",
|
||||
SVG_ANGLE_TYPE_UNKNOWN if is_turn => "turn",
|
||||
_ => "",
|
||||
}
|
||||
}
|
||||
|
||||
fn svg_angle_from_specified_value(
|
||||
value_in_specified_units: f64,
|
||||
unit_type: u32,
|
||||
is_turn: bool,
|
||||
) -> SvgParsedAngle {
|
||||
SvgParsedAngle {
|
||||
value: value_in_specified_units * svg_angle_degrees_per_unit(unit_type, is_turn),
|
||||
value_in_specified_units,
|
||||
unit_type,
|
||||
value_as_string: format!(
|
||||
"{}{}",
|
||||
svg_geometry::serialize_number(value_in_specified_units),
|
||||
svg_angle_unit_suffix(unit_type, is_turn)
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn set_svg_angle_value_degrees<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
object: v8::Local<'s, v8::Object>,
|
||||
value: f64,
|
||||
) {
|
||||
let (unit_type, is_turn) = svg_angle_current_unit(scope, object);
|
||||
let specified = value / svg_angle_degrees_per_unit(unit_type, is_turn);
|
||||
let parsed = svg_angle_from_specified_value(specified, unit_type, is_turn);
|
||||
set_svg_angle_parsed_value(scope, object, &parsed);
|
||||
}
|
||||
|
||||
pub(super) fn set_svg_angle_value_in_specified_units<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
object: v8::Local<'s, v8::Object>,
|
||||
value: f64,
|
||||
) {
|
||||
let (unit_type, is_turn) = svg_angle_current_unit(scope, object);
|
||||
let parsed = svg_angle_from_specified_value(value, unit_type, is_turn);
|
||||
set_svg_angle_parsed_value(scope, object, &parsed);
|
||||
}
|
||||
|
||||
pub(super) fn set_svg_angle_new_value<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
object: v8::Local<'s, v8::Object>,
|
||||
unit_type: u32,
|
||||
value: f64,
|
||||
) -> bool {
|
||||
if !(SVG_ANGLE_TYPE_UNSPECIFIED..=SVG_ANGLE_TYPE_GRAD).contains(&unit_type) {
|
||||
return false;
|
||||
}
|
||||
let parsed = svg_angle_from_specified_value(value, unit_type, false);
|
||||
set_svg_angle_parsed_value(scope, object, &parsed);
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) fn convert_svg_angle_to_unit<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
object: v8::Local<'s, v8::Object>,
|
||||
unit_type: u32,
|
||||
) -> bool {
|
||||
if !(SVG_ANGLE_TYPE_UNSPECIFIED..=SVG_ANGLE_TYPE_GRAD).contains(&unit_type) {
|
||||
return false;
|
||||
}
|
||||
let value = svg_angle_number_slot(scope, object, SVG_ANGLE_VALUE_SLOT).unwrap_or(0.0);
|
||||
let specified = value / svg_angle_degrees_per_unit(unit_type, false);
|
||||
let parsed = svg_angle_from_specified_value(specified, unit_type, false);
|
||||
set_svg_angle_parsed_value(scope, object, &parsed);
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) fn set_svg_angle_owner_attribute<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
angle: v8::Local<'s, v8::Object>,
|
||||
owner: v8::Local<'s, v8::Object>,
|
||||
attribute: &str,
|
||||
) {
|
||||
set_private_value(scope, angle, SVG_ANGLE_OWNER_ELEMENT_SLOT, owner.into());
|
||||
set_private_value(
|
||||
scope,
|
||||
angle,
|
||||
SVG_ANGLE_OWNER_ATTRIBUTE_SLOT,
|
||||
v8_string(scope, attribute)
|
||||
.unwrap_or_else(|| v8str(scope, ""))
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn sync_svg_angle_from_owner_attribute<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
angle: v8::Local<'s, v8::Object>,
|
||||
) {
|
||||
let Some(owner) = get_private_value(scope, angle, SVG_ANGLE_OWNER_ELEMENT_SLOT)
|
||||
.and_then(|value| v8::Local::<v8::Object>::try_from(value).ok())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Some(attribute) = svg_angle_string_slot(scope, angle, SVG_ANGLE_OWNER_ATTRIBUTE_SLOT)
|
||||
.filter(|value| !value.is_empty())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let parsed = svg_owner_attribute_value(scope, owner, &attribute)
|
||||
.as_deref()
|
||||
.and_then(parse_svg_orient_angle_value)
|
||||
.unwrap_or_default();
|
||||
set_svg_angle_parsed_value(scope, angle, &parsed);
|
||||
}
|
||||
|
||||
pub(super) fn sync_svg_animated_angle_from_owner_attribute<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
animated: v8::Local<'s, v8::Object>,
|
||||
owner: v8::Local<'s, v8::Object>,
|
||||
attribute: &str,
|
||||
) {
|
||||
let parsed = svg_owner_attribute_value(scope, owner, attribute)
|
||||
.as_deref()
|
||||
.and_then(parse_svg_orient_angle_value)
|
||||
.unwrap_or_default();
|
||||
if let Some(base_val) = get_private_value(scope, animated, SVG_ANIMATED_ANGLE_BASE_VAL_SLOT)
|
||||
.and_then(|value| v8::Local::<v8::Object>::try_from(value).ok())
|
||||
{
|
||||
set_svg_angle_parsed_value(scope, base_val, &parsed);
|
||||
set_svg_angle_owner_attribute(scope, base_val, owner, attribute);
|
||||
}
|
||||
if let Some(anim_val) = get_private_value(scope, animated, SVG_ANIMATED_ANGLE_ANIM_VAL_SLOT)
|
||||
.and_then(|value| v8::Local::<v8::Object>::try_from(value).ok())
|
||||
{
|
||||
set_svg_angle_parsed_value(scope, anim_val, &parsed);
|
||||
set_svg_angle_owner_attribute(scope, anim_val, owner, attribute);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn reflect_svg_angle_to_owner_attribute<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
angle: v8::Local<'s, v8::Object>,
|
||||
) {
|
||||
let Some(owner) = get_private_value(scope, angle, SVG_ANGLE_OWNER_ELEMENT_SLOT)
|
||||
.and_then(|value| v8::Local::<v8::Object>::try_from(value).ok())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Some(attribute) = svg_angle_string_slot(scope, angle, SVG_ANGLE_OWNER_ATTRIBUTE_SLOT)
|
||||
.filter(|value| !value.is_empty())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Some(value) = svg_angle_string_slot(scope, angle, SVG_ANGLE_VALUE_AS_STRING_SLOT) else {
|
||||
return;
|
||||
};
|
||||
let Ok((runtime_ptr, handle)) =
|
||||
crate::native_bridge::node_runtime_and_handle_from_object(scope, owner)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let runtime = unsafe { &mut *runtime_ptr };
|
||||
let _ = runtime.set_attribute(scope, runtime_ptr, handle, &attribute, &value);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(super) struct SvgParsedLength {
|
||||
value: f64,
|
||||
|
||||
@@ -388,6 +388,112 @@ fn svg_element_animated_length_getter<'s>(
|
||||
rv.set(value.into());
|
||||
}
|
||||
|
||||
fn svg_marker_runtime_and_handle<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
receiver: v8::Local<'s, v8::Object>,
|
||||
member: &str,
|
||||
) -> Option<(
|
||||
*mut crate::native_bridge::JsContextHost,
|
||||
crate::document_runtime::DomHandle,
|
||||
)> {
|
||||
let Ok((runtime_ptr, handle)) =
|
||||
crate::native_bridge::node_runtime_and_handle_from_object_or_detached(scope, receiver)
|
||||
else {
|
||||
webidl::throw_type_error(
|
||||
scope,
|
||||
&format!("SVGMarkerElement.{member} called on incompatible receiver."),
|
||||
);
|
||||
return None;
|
||||
};
|
||||
let is_marker = unsafe { &*runtime_ptr }
|
||||
.dom_host()
|
||||
.node(handle)
|
||||
.and_then(|node| node.as_element())
|
||||
.is_some_and(|element| element.is_svg_element("marker"));
|
||||
if !is_marker {
|
||||
webidl::throw_type_error(
|
||||
scope,
|
||||
&format!("SVGMarkerElement.{member} called on incompatible receiver."),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
Some((runtime_ptr, handle))
|
||||
}
|
||||
|
||||
pub(super) fn svg_marker_orient_angle_getter<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
let owner = args.this();
|
||||
if svg_marker_runtime_and_handle(scope, owner, "orientAngle").is_none() {
|
||||
return;
|
||||
}
|
||||
if let Some(value) = get_private_value(scope, owner, SVG_MARKER_ORIENT_ANGLE_SLOT) {
|
||||
if let Ok(animated) = v8::Local::<v8::Object>::try_from(value) {
|
||||
sync_svg_animated_angle_from_owner_attribute(scope, animated, owner, "orient");
|
||||
}
|
||||
rv.set(value);
|
||||
return;
|
||||
}
|
||||
let value = build_svg_animated_angle_for_attribute(scope, owner, "orient");
|
||||
set_private_value(scope, owner, SVG_MARKER_ORIENT_ANGLE_SLOT, value.into());
|
||||
rv.set(value.into());
|
||||
}
|
||||
|
||||
fn set_svg_marker_orient_attribute<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
marker: v8::Local<'s, v8::Object>,
|
||||
member: &str,
|
||||
value: &str,
|
||||
) -> bool {
|
||||
let Some((runtime_ptr, handle)) = svg_marker_runtime_and_handle(scope, marker, member) else {
|
||||
return false;
|
||||
};
|
||||
let runtime = unsafe { &mut *runtime_ptr };
|
||||
let _ = runtime.set_attribute(scope, runtime_ptr, handle, "orient", value);
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) fn svg_marker_set_orient_to_auto_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
if set_svg_marker_orient_attribute(scope, args.this(), "setOrientToAuto", "auto") {
|
||||
rv.set_undefined();
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn svg_marker_set_orient_to_angle_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
let Ok(angle) = v8::Local::<v8::Object>::try_from(args.get(0)) else {
|
||||
webidl::throw_type_error(
|
||||
scope,
|
||||
"SVGMarkerElement.setOrientToAngle requires an SVGAngle.",
|
||||
);
|
||||
return;
|
||||
};
|
||||
if !require_svg_receiver(
|
||||
scope,
|
||||
angle,
|
||||
SVG_ANGLE_UNIT_TYPE_SLOT,
|
||||
"SVGAngle",
|
||||
"setOrientToAngle argument",
|
||||
) {
|
||||
return;
|
||||
}
|
||||
sync_svg_angle_from_owner_attribute(scope, angle);
|
||||
let value = svg_angle_string_slot(scope, angle, SVG_ANGLE_VALUE_AS_STRING_SLOT)
|
||||
.unwrap_or_else(|| "0".to_owned());
|
||||
if set_svg_marker_orient_attribute(scope, args.this(), "setOrientToAngle", &value) {
|
||||
rv.set_undefined();
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn svg_graphics_transform_getter<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
@@ -791,6 +897,42 @@ pub(super) fn svg_animated_length_getter<'s>(
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn svg_animated_angle_getter<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
let Some(name) = callback_data_item(
|
||||
scope,
|
||||
&args,
|
||||
SVG_ANIMATED_ACCESSOR_NAMES,
|
||||
"SVGAnimatedAngle attributes",
|
||||
) else {
|
||||
rv.set_undefined();
|
||||
return;
|
||||
};
|
||||
if !require_svg_receiver(
|
||||
scope,
|
||||
args.this(),
|
||||
SVG_ANIMATED_ANGLE_BASE_VAL_SLOT,
|
||||
"SVGAnimatedAngle",
|
||||
&format!("{name} getter"),
|
||||
) {
|
||||
return;
|
||||
}
|
||||
let slot = match name {
|
||||
"baseVal" => SVG_ANIMATED_ANGLE_BASE_VAL_SLOT,
|
||||
"animVal" => SVG_ANIMATED_ANGLE_ANIM_VAL_SLOT,
|
||||
_ => {
|
||||
rv.set_undefined();
|
||||
return;
|
||||
}
|
||||
};
|
||||
rv.set(
|
||||
get_private_value(scope, args.this(), slot).unwrap_or_else(|| v8::undefined(scope).into()),
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn svg_animated_length_list_getter<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
@@ -864,6 +1006,129 @@ pub(super) fn svg_length_getter<'s>(
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn svg_angle_getter<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
let Some(name) = callback_data_item(
|
||||
scope,
|
||||
&args,
|
||||
SVG_LENGTH_ACCESSOR_NAMES,
|
||||
"SVGAngle attributes",
|
||||
) else {
|
||||
rv.set_undefined();
|
||||
return;
|
||||
};
|
||||
if !require_svg_receiver(
|
||||
scope,
|
||||
args.this(),
|
||||
SVG_ANGLE_UNIT_TYPE_SLOT,
|
||||
"SVGAngle",
|
||||
&format!("{name} getter"),
|
||||
) {
|
||||
return;
|
||||
}
|
||||
sync_svg_angle_from_owner_attribute(scope, args.this());
|
||||
match name {
|
||||
"unitType" => {
|
||||
let value = svg_angle_number_slot(scope, args.this(), SVG_ANGLE_UNIT_TYPE_SLOT)
|
||||
.unwrap_or(SVG_ANGLE_TYPE_UNSPECIFIED as f64);
|
||||
rv.set(v8::Integer::new_from_unsigned(scope, value as u32).into());
|
||||
}
|
||||
"value" => {
|
||||
let value =
|
||||
svg_angle_number_slot(scope, args.this(), SVG_ANGLE_VALUE_SLOT).unwrap_or(0.0);
|
||||
rv.set(v8::Number::new(scope, value).into());
|
||||
}
|
||||
"valueInSpecifiedUnits" => {
|
||||
let value =
|
||||
svg_angle_number_slot(scope, args.this(), SVG_ANGLE_VALUE_IN_SPECIFIED_UNITS_SLOT)
|
||||
.unwrap_or(0.0);
|
||||
rv.set(v8::Number::new(scope, value).into());
|
||||
}
|
||||
"valueAsString" => rv.set(
|
||||
get_private_value(scope, args.this(), SVG_ANGLE_VALUE_AS_STRING_SLOT)
|
||||
.unwrap_or_else(|| v8str(scope, "0").into()),
|
||||
),
|
||||
_ => rv.set_undefined(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn svg_angle_setter<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
_rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
let Some(name) = callback_data_item(
|
||||
scope,
|
||||
&args,
|
||||
SVG_LENGTH_ACCESSOR_NAMES,
|
||||
"SVGAngle attributes",
|
||||
) else {
|
||||
return;
|
||||
};
|
||||
if !require_svg_receiver(
|
||||
scope,
|
||||
args.this(),
|
||||
SVG_ANGLE_UNIT_TYPE_SLOT,
|
||||
"SVGAngle",
|
||||
&format!("{name} setter"),
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if svg_angle_is_read_only(scope, args.this()) {
|
||||
throw_dom_exception(
|
||||
scope,
|
||||
"NoModificationAllowedError",
|
||||
7,
|
||||
"The SVG angle is read-only.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
sync_svg_angle_from_owner_attribute(scope, args.this());
|
||||
match name {
|
||||
"value" | "valueInSpecifiedUnits" => {
|
||||
let value = match webidl::convert::<webidl::Double>(
|
||||
scope,
|
||||
args.get(0),
|
||||
webidl::Context::member("SVGAngle", name),
|
||||
) {
|
||||
Ok(value) => value.0,
|
||||
Err(error) => {
|
||||
webidl::throw_error(scope, &error);
|
||||
return;
|
||||
}
|
||||
};
|
||||
if name == "value" {
|
||||
set_svg_angle_value_degrees(scope, args.this(), value);
|
||||
} else {
|
||||
set_svg_angle_value_in_specified_units(scope, args.this(), value);
|
||||
}
|
||||
}
|
||||
"valueAsString" => {
|
||||
let value = match webidl::convert::<webidl::DomString>(
|
||||
scope,
|
||||
args.get(0),
|
||||
webidl::Context::member("SVGAngle", "valueAsString"),
|
||||
) {
|
||||
Ok(value) => value.0,
|
||||
Err(error) => {
|
||||
webidl::throw_error(scope, &error);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let Some(parsed) = parse_svg_angle_value(&value) else {
|
||||
throw_dom_exception(scope, "SyntaxError", 12, "Invalid SVG angle value.");
|
||||
return;
|
||||
};
|
||||
set_svg_angle_parsed_value(scope, args.this(), &parsed);
|
||||
}
|
||||
_ => return,
|
||||
}
|
||||
reflect_svg_angle_to_owner_attribute(scope, args.this());
|
||||
}
|
||||
|
||||
pub(super) fn svg_length_setter<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
@@ -2118,6 +2383,14 @@ pub(super) fn svg_svg_element_create_matrix_callback<'s>(
|
||||
rv.set(super::super::geometry_runtime::build_dom_matrix_identity_object(scope).into());
|
||||
}
|
||||
|
||||
pub(super) fn svg_svg_element_create_angle_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
_args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
rv.set(build_svg_angle(scope).into());
|
||||
}
|
||||
|
||||
pub(super) fn svg_svg_element_deselect_all_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
@@ -2483,6 +2756,46 @@ pub(super) fn svg_length_new_value_specified_units_callback<'s>(
|
||||
rv.set_undefined();
|
||||
}
|
||||
|
||||
pub(super) fn svg_angle_new_value_specified_units_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
if !require_svg_receiver(
|
||||
scope,
|
||||
args.this(),
|
||||
SVG_ANGLE_UNIT_TYPE_SLOT,
|
||||
"SVGAngle",
|
||||
"newValueSpecifiedUnits",
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if svg_angle_is_read_only(scope, args.this()) {
|
||||
throw_dom_exception(
|
||||
scope,
|
||||
"NoModificationAllowedError",
|
||||
7,
|
||||
"The SVG angle is read-only.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
let Some(parsed) = webidl::parse_args::<SvgAngleNewValueSpecifiedUnitsArgs>(scope, &args)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
if !set_svg_angle_new_value(scope, args.this(), parsed.unit_type as u32, parsed.value) {
|
||||
throw_dom_exception(
|
||||
scope,
|
||||
"NotSupportedError",
|
||||
9,
|
||||
"The SVG angle unit type is not supported.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
reflect_svg_angle_to_owner_attribute(scope, args.this());
|
||||
rv.set_undefined();
|
||||
}
|
||||
|
||||
pub(super) fn svg_length_convert_to_specified_units_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
@@ -2498,6 +2811,47 @@ pub(super) fn svg_length_convert_to_specified_units_callback<'s>(
|
||||
rv.set_undefined();
|
||||
}
|
||||
|
||||
pub(super) fn svg_angle_convert_to_specified_units_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
if !require_svg_receiver(
|
||||
scope,
|
||||
args.this(),
|
||||
SVG_ANGLE_UNIT_TYPE_SLOT,
|
||||
"SVGAngle",
|
||||
"convertToSpecifiedUnits",
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if svg_angle_is_read_only(scope, args.this()) {
|
||||
throw_dom_exception(
|
||||
scope,
|
||||
"NoModificationAllowedError",
|
||||
7,
|
||||
"The SVG angle is read-only.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
sync_svg_angle_from_owner_attribute(scope, args.this());
|
||||
let Some(parsed) = webidl::parse_args::<SvgAngleConvertToSpecifiedUnitsArgs>(scope, &args)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
if !convert_svg_angle_to_unit(scope, args.this(), parsed.unit_type as u32) {
|
||||
throw_dom_exception(
|
||||
scope,
|
||||
"NotSupportedError",
|
||||
9,
|
||||
"The SVG angle unit type is not supported.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
reflect_svg_angle_to_owner_attribute(scope, args.this());
|
||||
rv.set_undefined();
|
||||
}
|
||||
|
||||
pub(super) fn svg_graphics_get_bbox_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
|
||||
@@ -38,6 +38,18 @@ const SVG_ANIMATED_BOOLEAN_INITIAL_VALUE_SLOT: &str = "__moliSvgAnimatedBooleanI
|
||||
const SVG_FE_CONVOLVE_MATRIX_PRESERVE_ALPHA_SLOT: &str = "__moliSvgFeConvolveMatrixPreserveAlpha";
|
||||
const SVG_ANIMATED_LENGTH_BASE_VAL_SLOT: &str = "__moliSvgAnimatedLengthBaseVal";
|
||||
const SVG_ANIMATED_LENGTH_ANIM_VAL_SLOT: &str = "__moliSvgAnimatedLengthAnimVal";
|
||||
const SVG_MARKER_ORIENT_ANGLE_SLOT: &str = "__moliSvgMarkerOrientAngle";
|
||||
const SVG_MARKER_UNITS_SLOT: &str = "__moliSvgMarkerUnits";
|
||||
const SVG_MARKER_ORIENT_TYPE_SLOT: &str = "__moliSvgMarkerOrientType";
|
||||
const SVG_ANIMATED_ANGLE_BASE_VAL_SLOT: &str = "__moliSvgAnimatedAngleBaseVal";
|
||||
const SVG_ANIMATED_ANGLE_ANIM_VAL_SLOT: &str = "__moliSvgAnimatedAngleAnimVal";
|
||||
const SVG_ANGLE_OWNER_ELEMENT_SLOT: &str = "__moliSvgAngleOwnerElement";
|
||||
const SVG_ANGLE_OWNER_ATTRIBUTE_SLOT: &str = "__moliSvgAngleOwnerAttribute";
|
||||
const SVG_ANGLE_UNIT_TYPE_SLOT: &str = "__moliSvgAngleUnitType";
|
||||
const SVG_ANGLE_VALUE_SLOT: &str = "__moliSvgAngleValue";
|
||||
const SVG_ANGLE_VALUE_IN_SPECIFIED_UNITS_SLOT: &str = "__moliSvgAngleValueInSpecifiedUnits";
|
||||
const SVG_ANGLE_VALUE_AS_STRING_SLOT: &str = "__moliSvgAngleValueAsString";
|
||||
const SVG_ANGLE_READ_ONLY_SLOT: &str = "__moliSvgAngleReadOnly";
|
||||
const SVG_ANIMATED_LENGTH_LIST_BASE_VAL_SLOT: &str = "__moliSvgAnimatedLengthListBaseVal";
|
||||
const SVG_ANIMATED_LENGTH_LIST_ANIM_VAL_SLOT: &str = "__moliSvgAnimatedLengthListAnimVal";
|
||||
const SVG_LENGTH_LIST_ITEMS_SLOT: &str = "__moliSvgLengthListItems";
|
||||
@@ -120,6 +132,8 @@ enum SvgAnimatedEnumerationKind {
|
||||
UnitType,
|
||||
SpreadMethod,
|
||||
LengthAdjust,
|
||||
MarkerUnits,
|
||||
MarkerOrient,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -247,6 +261,22 @@ struct SvgLengthConvertToSpecifiedUnitsArgs {
|
||||
unit_type: u16,
|
||||
}
|
||||
|
||||
#[derive(webidl::WebIdlArgs)]
|
||||
#[webidl(prefix = "SVG angle newValueSpecifiedUnits")]
|
||||
struct SvgAngleNewValueSpecifiedUnitsArgs {
|
||||
#[webidl(required, converter = "unsigned_short")]
|
||||
unit_type: u16,
|
||||
#[webidl(required, converter = "double")]
|
||||
value: f64,
|
||||
}
|
||||
|
||||
#[derive(webidl::WebIdlArgs)]
|
||||
#[webidl(prefix = "SVG angle convertToSpecifiedUnits")]
|
||||
struct SvgAngleConvertToSpecifiedUnitsArgs {
|
||||
#[webidl(required, converter = "unsigned_short")]
|
||||
unit_type: u16,
|
||||
}
|
||||
|
||||
#[derive(webidl::WebIdlArgs)]
|
||||
#[webidl(prefix = "SVGGeometryElement.getPointAtLength")]
|
||||
struct SvgGeometryPointAtLengthArgs {
|
||||
@@ -282,6 +312,21 @@ const SVG_LENGTH_TYPE_IN: u32 = 8;
|
||||
const SVG_LENGTH_TYPE_PT: u32 = 9;
|
||||
const SVG_LENGTH_TYPE_PC: u32 = 10;
|
||||
|
||||
const SVG_ANGLE_TYPE_UNKNOWN: u32 = 0;
|
||||
const SVG_ANGLE_TYPE_UNSPECIFIED: u32 = 1;
|
||||
const SVG_ANGLE_TYPE_DEG: u32 = 2;
|
||||
const SVG_ANGLE_TYPE_RAD: u32 = 3;
|
||||
const SVG_ANGLE_TYPE_GRAD: u32 = 4;
|
||||
|
||||
const SVG_MARKER_UNITS_UNKNOWN: u32 = 0;
|
||||
const SVG_MARKER_UNITS_USER_SPACE_ON_USE: u32 = 1;
|
||||
const SVG_MARKER_UNITS_STROKE_WIDTH: u32 = 2;
|
||||
|
||||
const SVG_MARKER_ORIENT_UNKNOWN: u32 = 0;
|
||||
const SVG_MARKER_ORIENT_AUTO: u32 = 1;
|
||||
const SVG_MARKER_ORIENT_ANGLE: u32 = 2;
|
||||
const SVG_MARKER_ORIENT_AUTO_START_REVERSE: u32 = 3;
|
||||
|
||||
const SVG_TRANSFORM_TYPE_UNKNOWN: u32 = 0;
|
||||
const SVG_TRANSFORM_TYPE_MATRIX: u32 = 1;
|
||||
const SVG_TRANSFORM_TYPE_TRANSLATE: u32 = 2;
|
||||
@@ -374,6 +419,20 @@ const SVG_ANIMATED_ENUMERATION_PROPERTIES: &[SvgAnimatedEnumerationProperty] = &
|
||||
initial_value: SVG_LENGTH_ADJUST_SPACING,
|
||||
kind: SvgAnimatedEnumerationKind::LengthAdjust,
|
||||
},
|
||||
SvgAnimatedEnumerationProperty {
|
||||
index: 10,
|
||||
attribute: "markerUnits",
|
||||
cache_slot: SVG_MARKER_UNITS_SLOT,
|
||||
initial_value: SVG_MARKER_UNITS_STROKE_WIDTH,
|
||||
kind: SvgAnimatedEnumerationKind::MarkerUnits,
|
||||
},
|
||||
SvgAnimatedEnumerationProperty {
|
||||
index: 11,
|
||||
attribute: "orient",
|
||||
cache_slot: SVG_MARKER_ORIENT_TYPE_SLOT,
|
||||
initial_value: SVG_MARKER_ORIENT_ANGLE,
|
||||
kind: SvgAnimatedEnumerationKind::MarkerOrient,
|
||||
},
|
||||
];
|
||||
|
||||
pub(in crate::context_bootstrap) fn install_svg_template_bindings<'s>(
|
||||
@@ -384,11 +443,13 @@ pub(in crate::context_bootstrap) fn install_svg_template_bindings<'s>(
|
||||
bindings::install_svg_element_accessor_bindings(scope, template, name);
|
||||
match name {
|
||||
"SVGLength" => bindings::install_svg_length_bindings(scope, template),
|
||||
"SVGAngle" => bindings::install_svg_angle_bindings(scope, template),
|
||||
"SVGNumber" => bindings::install_svg_number_bindings(scope, template),
|
||||
"SVGRect" => rect::install_bindings(scope, template),
|
||||
"SVGAnimatedString" => bindings::install_svg_animated_string_bindings(scope, template),
|
||||
"SVGAnimatedBoolean" => bindings::install_svg_animated_boolean_bindings(scope, template),
|
||||
"SVGAnimatedLength" => bindings::install_svg_animated_length_bindings(scope, template),
|
||||
"SVGAnimatedAngle" => bindings::install_svg_animated_angle_bindings(scope, template),
|
||||
"SVGLengthList" => bindings::install_svg_length_list_bindings(scope, template),
|
||||
"SVGAnimatedLengthList" => {
|
||||
bindings::install_svg_animated_length_list_bindings(scope, template)
|
||||
@@ -415,6 +476,7 @@ pub(in crate::context_bootstrap) fn install_svg_template_bindings<'s>(
|
||||
bindings::install_svg_text_content_element_bindings(scope, template)
|
||||
}
|
||||
"SVGGradientElement" => bindings::install_svg_gradient_element_bindings(scope, template),
|
||||
"SVGMarkerElement" => bindings::install_svg_marker_element_bindings(scope, template),
|
||||
"SVGSVGElement" => bindings::install_svg_svg_element_bindings(scope, template),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -1968,6 +1968,132 @@ fn svg_animated_boolean_reflects_preserve_alpha() {
|
||||
assert_eq!(result, "ok");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn svg_marker_orient_angle_is_a_live_animated_angle() {
|
||||
let mut vm = new_parsed_test_vm(
|
||||
"https://svg-animated-angle.test/",
|
||||
"<!doctype html><html><body></body></html>",
|
||||
);
|
||||
|
||||
let result = vm
|
||||
.eval(
|
||||
r#"
|
||||
(() => {
|
||||
const assert = (condition, message) => {
|
||||
if (!condition) throw new Error(message);
|
||||
};
|
||||
const close = (actual, expected) => Math.abs(actual - expected) < 1e-9;
|
||||
const ns = "http://www.w3.org/2000/svg";
|
||||
const marker = document.createElementNS(ns, "marker");
|
||||
|
||||
for (const constructor of [SVGMarkerElement, SVGAnimatedAngle, SVGAngle]) {
|
||||
assert(typeof constructor === "function", `${constructor.name} constructor`);
|
||||
let illegalConstructor = false;
|
||||
try {
|
||||
new constructor();
|
||||
} catch (error) {
|
||||
illegalConstructor = error instanceof TypeError;
|
||||
}
|
||||
assert(illegalConstructor, `${constructor.name} illegal constructor`);
|
||||
}
|
||||
assert(marker instanceof SVGMarkerElement, "marker interface");
|
||||
assert(Object.getPrototypeOf(SVGMarkerElement.prototype) === SVGElement.prototype,
|
||||
"marker prototype parent");
|
||||
|
||||
const markerDescriptor = Object.getOwnPropertyDescriptor(
|
||||
SVGMarkerElement.prototype,
|
||||
"orientAngle",
|
||||
);
|
||||
assert(typeof markerDescriptor.get === "function" &&
|
||||
markerDescriptor.set === undefined, "orientAngle readonly descriptor");
|
||||
assert(markerDescriptor.enumerable && markerDescriptor.configurable,
|
||||
"orientAngle descriptor flags");
|
||||
|
||||
const animated = marker.orientAngle;
|
||||
const base = animated.baseVal;
|
||||
const anim = animated.animVal;
|
||||
assert(animated instanceof SVGAnimatedAngle, "animated angle interface");
|
||||
assert(base instanceof SVGAngle && anim instanceof SVGAngle, "angle interfaces");
|
||||
assert(base !== anim, "base and animated values are distinct");
|
||||
assert(marker.orientAngle === animated, "orientAngle SameObject");
|
||||
assert(base.value === 0 && anim.value === 0, "initial values");
|
||||
assert(base.unitType === SVGAngle.SVG_ANGLETYPE_UNSPECIFIED, "initial unit");
|
||||
assert(!marker.hasAttribute("orient"), "getter does not create attribute");
|
||||
assert(marker.orientType instanceof SVGAnimatedEnumeration, "orientType interface");
|
||||
assert(marker.orientType.baseVal === SVGMarkerElement.SVG_MARKER_ORIENT_ANGLE,
|
||||
"initial orientType");
|
||||
assert(marker.markerUnits instanceof SVGAnimatedEnumeration,
|
||||
"markerUnits interface");
|
||||
assert(marker.markerUnits.baseVal ===
|
||||
SVGMarkerElement.SVG_MARKERUNITS_STROKEWIDTH, "initial markerUnits");
|
||||
marker.markerUnits.baseVal = SVGMarkerElement.SVG_MARKERUNITS_USERSPACEONUSE;
|
||||
assert(marker.getAttribute("markerUnits") === "userSpaceOnUse",
|
||||
"markerUnits reflection");
|
||||
|
||||
base.value = 100;
|
||||
assert(base.value === 100 && marker.orientAngle.baseVal.value === 100,
|
||||
"cached base value is live");
|
||||
assert(marker.getAttribute("orient") === "100", "value reflects to orient");
|
||||
marker.orientAngle.baseVal = -1;
|
||||
assert(marker.orientAngle.baseVal === base && base.value === 100,
|
||||
"baseVal assignment is ignored");
|
||||
|
||||
marker.setAttribute("orient", "1.5707963267948966rad");
|
||||
assert(close(base.value, 90), "content attribute converts to degrees");
|
||||
assert(base.unitType === SVGAngle.SVG_ANGLETYPE_RAD, "content attribute unit");
|
||||
assert(close(base.valueInSpecifiedUnits, Math.PI / 2), "specified value");
|
||||
assert(anim.unitType === SVGAngle.SVG_ANGLETYPE_RAD && close(anim.value, 90),
|
||||
"animVal tracks content attribute");
|
||||
assert(marker.orientType.baseVal === SVGMarkerElement.SVG_MARKER_ORIENT_ANGLE,
|
||||
"angle orientType");
|
||||
|
||||
marker.setOrientToAuto();
|
||||
assert(marker.getAttribute("orient") === "auto", "setOrientToAuto reflection");
|
||||
assert(marker.orientType.baseVal === SVGMarkerElement.SVG_MARKER_ORIENT_AUTO,
|
||||
"automatic orientType");
|
||||
assert(base.value === 0 && base.unitType === SVGAngle.SVG_ANGLETYPE_UNSPECIFIED,
|
||||
"automatic orient angle");
|
||||
|
||||
marker.setAttribute("orient", "400grad");
|
||||
assert(base.value === 360 && base.unitType === SVGAngle.SVG_ANGLETYPE_GRAD,
|
||||
"grad content attribute");
|
||||
|
||||
const svg = document.createElementNS(ns, "svg");
|
||||
const standalone = svg.createSVGAngle();
|
||||
assert(standalone instanceof SVGAngle, "createSVGAngle result");
|
||||
standalone.newValueSpecifiedUnits(SVGAngle.SVG_ANGLETYPE_RAD, Math.PI);
|
||||
assert(close(standalone.value, 180), "newValueSpecifiedUnits conversion");
|
||||
standalone.convertToSpecifiedUnits(SVGAngle.SVG_ANGLETYPE_GRAD);
|
||||
assert(close(standalone.valueInSpecifiedUnits, 200), "unit conversion");
|
||||
assert(standalone.valueAsString === "200grad", "unit serialization");
|
||||
marker.setOrientToAngle(standalone);
|
||||
assert(marker.getAttribute("orient") === "200grad", "setOrientToAngle reflection");
|
||||
assert(marker.orientAngle.baseVal.value === 180, "setOrientToAngle value");
|
||||
|
||||
let readonlyAnim = false;
|
||||
try {
|
||||
anim.value = 1;
|
||||
} catch (error) {
|
||||
readonlyAnim = error.name === "NoModificationAllowedError";
|
||||
}
|
||||
assert(readonlyAnim, "animVal is read-only");
|
||||
|
||||
let incompatibleReceiver = false;
|
||||
try {
|
||||
markerDescriptor.get.call(document.createElementNS(ns, "rect"));
|
||||
} catch (error) {
|
||||
incompatibleReceiver = error instanceof TypeError;
|
||||
}
|
||||
assert(incompatibleReceiver, "marker receiver brand");
|
||||
return "ok";
|
||||
})()
|
||||
"#,
|
||||
)
|
||||
.expect("SVG animated angle probe should evaluate");
|
||||
|
||||
assert_eq!(result, "ok");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn svg_string_lists_reflect_conditional_processing_attributes() {
|
||||
let mut vm = new_parsed_test_vm(
|
||||
|
||||
@@ -372,6 +372,9 @@ interfaces! {
|
||||
SVGAnimatedLengthList;
|
||||
SVGAnimatedBoolean;
|
||||
SVGFEConvolveMatrixElement: SVGElement;
|
||||
SVGAngle;
|
||||
SVGAnimatedAngle;
|
||||
SVGMarkerElement: SVGElement;
|
||||
SVGAnimatedNumber;
|
||||
SVGAnimatedNumberList;
|
||||
SVGAnimatedTransformList;
|
||||
|
||||
Reference in New Issue
Block a user