mirror of
https://github.com/lexmount/moli.git
synced 2026-10-08 00:01:00 +00:00
fix(svg): expose standalone value factories
This commit is contained in:
@@ -9,6 +9,7 @@ const DOM_RECT_WIDTH_SLOT: &str = "__moliDomRectWidth";
|
||||
const DOM_RECT_HEIGHT_SLOT: &str = "__moliDomRectHeight";
|
||||
const DOM_RECT_BRAND_SLOT: &str = "__moliDomRectBrand";
|
||||
const DOM_RECT_MUTABLE_BRAND_SLOT: &str = "__moliDomRectMutableBrand";
|
||||
const DOM_RECT_RESTRICTED_NUMBER_SLOT: &str = "__moliDomRectRestrictedNumber";
|
||||
|
||||
#[derive(WebApiObject)]
|
||||
#[webapi(interface = "DOMRect")]
|
||||
@@ -298,6 +299,20 @@ pub(crate) fn build_dom_rect_object<'s>(
|
||||
.expect("DOMRect declaration should bind")
|
||||
}
|
||||
|
||||
pub(in crate::context_bootstrap) fn build_svg_rect_object<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
) -> v8::Local<'s, v8::Object> {
|
||||
let object = build_dom_rect_object(scope, 0.0, 0.0, 0.0, 0.0);
|
||||
let restricted = v8::Boolean::new(scope, true);
|
||||
set_private_value(
|
||||
scope,
|
||||
object,
|
||||
DOM_RECT_RESTRICTED_NUMBER_SLOT,
|
||||
restricted.into(),
|
||||
);
|
||||
object
|
||||
}
|
||||
|
||||
fn build_dom_rect_readonly_object<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
x: f64,
|
||||
@@ -462,15 +477,24 @@ fn dom_rect_setter_callback<'s>(
|
||||
throw_type_error(scope, "Illegal invocation");
|
||||
return;
|
||||
}
|
||||
let value = match webidl::convert::<webidl::UnrestrictedDouble>(
|
||||
scope,
|
||||
args.get(0),
|
||||
webidl::Context::member("DOMRect", slot),
|
||||
) {
|
||||
Ok(value) => value.0,
|
||||
Err(error) => {
|
||||
webidl::throw_error(scope, &error);
|
||||
return;
|
||||
let context = webidl::Context::member("DOMRect", slot);
|
||||
let restricted = get_private_value(scope, args.this(), DOM_RECT_RESTRICTED_NUMBER_SLOT)
|
||||
.is_some_and(|value| value.boolean_value(scope));
|
||||
let value = if restricted {
|
||||
match webidl::convert::<webidl::Double>(scope, args.get(0), context) {
|
||||
Ok(value) => value.0,
|
||||
Err(error) => {
|
||||
webidl::throw_error(scope, &error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match webidl::convert::<webidl::UnrestrictedDouble>(scope, args.get(0), context) {
|
||||
Ok(value) => value.0,
|
||||
Err(error) => {
|
||||
webidl::throw_error(scope, &error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
set_private_value(
|
||||
|
||||
@@ -14,6 +14,7 @@ const DOM_POINT_Z_SLOT: &str = "__moliDomPointZ";
|
||||
const DOM_POINT_W_SLOT: &str = "__moliDomPointW";
|
||||
const DOM_POINT_BRAND_SLOT: &str = "__moliDomPointBrand";
|
||||
const DOM_POINT_MUTABLE_BRAND_SLOT: &str = "__moliDomPointMutableBrand";
|
||||
const DOM_POINT_RESTRICTED_NUMBER_SLOT: &str = "__moliDomPointRestrictedNumber";
|
||||
|
||||
const DOM_MATRIX_M11_SLOT: &str = "__moliDomMatrixM11";
|
||||
const DOM_MATRIX_M12_SLOT: &str = "__moliDomMatrixM12";
|
||||
@@ -820,6 +821,20 @@ pub(in crate::context_bootstrap) fn build_dom_point_object<'s>(
|
||||
.expect("DOMPoint declaration should bind")
|
||||
}
|
||||
|
||||
pub(in crate::context_bootstrap) fn build_svg_point_object<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
) -> v8::Local<'s, v8::Object> {
|
||||
let object = build_dom_point_object(scope, 0.0, 0.0, 0.0, 1.0);
|
||||
let restricted = v8::Boolean::new(scope, true);
|
||||
set_private_value(
|
||||
scope,
|
||||
object,
|
||||
DOM_POINT_RESTRICTED_NUMBER_SLOT,
|
||||
restricted.into(),
|
||||
);
|
||||
object
|
||||
}
|
||||
|
||||
fn build_dom_point_readonly_object<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
x: f64,
|
||||
@@ -931,12 +946,22 @@ fn dom_point_setter_callback<'s>(
|
||||
throw_type_error(scope, "Illegal invocation");
|
||||
return;
|
||||
}
|
||||
let Some(value) = geometry_number_value(
|
||||
scope,
|
||||
args.get(0),
|
||||
webidl::Context::member("DOMPoint", slot),
|
||||
) else {
|
||||
return;
|
||||
let context = webidl::Context::member("DOMPoint", slot);
|
||||
let value = if get_private_value(scope, args.this(), DOM_POINT_RESTRICTED_NUMBER_SLOT)
|
||||
.is_some_and(|value| value.boolean_value(scope))
|
||||
{
|
||||
match webidl::convert::<webidl::Double>(scope, args.get(0), context) {
|
||||
Ok(value) => value.0,
|
||||
Err(error) => {
|
||||
webidl::throw_error(scope, &error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let Some(value) = geometry_number_value(scope, args.get(0), context) else {
|
||||
return;
|
||||
};
|
||||
value
|
||||
};
|
||||
set_private_value(
|
||||
scope,
|
||||
|
||||
@@ -771,6 +771,20 @@ struct SvgSvgElementTemplateMethodsDeclaration {
|
||||
)]
|
||||
deselect_all: (),
|
||||
|
||||
#[webapi(
|
||||
method = "createSVGNumber",
|
||||
length = 0,
|
||||
callback = svg_svg_element_create_number_callback
|
||||
)]
|
||||
create_svg_number: (),
|
||||
|
||||
#[webapi(
|
||||
method = "createSVGLength",
|
||||
length = 0,
|
||||
callback = svg_svg_element_create_length_callback
|
||||
)]
|
||||
create_svg_length: (),
|
||||
|
||||
#[webapi(
|
||||
method = "createSVGAngle",
|
||||
length = 0,
|
||||
@@ -778,6 +792,13 @@ struct SvgSvgElementTemplateMethodsDeclaration {
|
||||
)]
|
||||
create_svg_angle: (),
|
||||
|
||||
#[webapi(
|
||||
method = "createSVGPoint",
|
||||
length = 0,
|
||||
callback = svg_svg_element_create_point_callback
|
||||
)]
|
||||
create_svg_point: (),
|
||||
|
||||
#[webapi(
|
||||
method = "createSVGMatrix",
|
||||
length = 0,
|
||||
@@ -785,6 +806,13 @@ struct SvgSvgElementTemplateMethodsDeclaration {
|
||||
)]
|
||||
create_svg_matrix: (),
|
||||
|
||||
#[webapi(
|
||||
method = "createSVGRect",
|
||||
length = 0,
|
||||
callback = svg_svg_element_create_rect_callback
|
||||
)]
|
||||
create_svg_rect: (),
|
||||
|
||||
#[webapi(
|
||||
method = "createSVGTransform",
|
||||
length = 0,
|
||||
|
||||
@@ -1150,7 +1150,7 @@ pub(super) fn svg_length_setter<'s>(
|
||||
}
|
||||
match name {
|
||||
"value" | "valueInSpecifiedUnits" => {
|
||||
let value = match webidl::convert::<webidl::UnrestrictedDouble>(
|
||||
let value = match webidl::convert::<webidl::Double>(
|
||||
scope,
|
||||
args.get(0),
|
||||
webidl::Context::member("SVGLength", "value"),
|
||||
@@ -1177,7 +1177,10 @@ pub(super) fn svg_length_setter<'s>(
|
||||
return;
|
||||
}
|
||||
};
|
||||
let parsed = parse_svg_length_value(&string_value).unwrap_or_default();
|
||||
let Some(parsed) = parse_svg_length_value(&string_value) else {
|
||||
throw_dom_exception(scope, "SyntaxError", 12, "Invalid SVG length value.");
|
||||
return;
|
||||
};
|
||||
set_svg_length_parsed_value(scope, args.this(), parsed);
|
||||
reflect_svg_length_to_owner_attribute(scope, args.this());
|
||||
reflect_svg_value_list_item_to_owner_list(scope, args.this(), SvgListKind::Length);
|
||||
@@ -1218,7 +1221,7 @@ pub(super) fn svg_number_setter<'s>(
|
||||
) {
|
||||
return;
|
||||
}
|
||||
let value = match webidl::convert::<webidl::UnrestrictedDouble>(
|
||||
let value = match webidl::convert::<webidl::Double>(
|
||||
scope,
|
||||
args.get(0),
|
||||
webidl::Context::member("SVGNumber", "value"),
|
||||
@@ -2497,20 +2500,92 @@ pub(super) fn svg_transform_set_matrix_callback<'s>(
|
||||
rv.set_undefined();
|
||||
}
|
||||
|
||||
pub(super) fn svg_svg_element_create_matrix_callback<'s>(
|
||||
fn require_svg_svg_element_receiver<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
_args: v8::FunctionCallbackArguments<'s>,
|
||||
receiver: v8::Local<'s, v8::Object>,
|
||||
member: &str,
|
||||
) -> bool {
|
||||
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!("SVGSVGElement.{member} called on incompatible receiver."),
|
||||
);
|
||||
return false;
|
||||
};
|
||||
let is_svg_element = unsafe { &*runtime_ptr }
|
||||
.dom_host()
|
||||
.node(handle)
|
||||
.and_then(|node| node.as_element())
|
||||
.is_some_and(|element| element.is_svg_element("svg"));
|
||||
if !is_svg_element {
|
||||
webidl::throw_type_error(
|
||||
scope,
|
||||
&format!("SVGSVGElement.{member} called on incompatible receiver."),
|
||||
);
|
||||
}
|
||||
is_svg_element
|
||||
}
|
||||
|
||||
pub(super) fn svg_svg_element_create_number_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
rv.set(super::super::geometry_runtime::build_dom_matrix_identity_object(scope).into());
|
||||
if require_svg_svg_element_receiver(scope, args.this(), "createSVGNumber") {
|
||||
rv.set(build_svg_number(scope, 0.0).into());
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn svg_svg_element_create_length_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
if require_svg_svg_element_receiver(scope, args.this(), "createSVGLength") {
|
||||
rv.set(build_svg_length(scope, 0.0).into());
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn svg_svg_element_create_matrix_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
if require_svg_svg_element_receiver(scope, args.this(), "createSVGMatrix") {
|
||||
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>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
rv.set(build_svg_angle(scope).into());
|
||||
if require_svg_svg_element_receiver(scope, args.this(), "createSVGAngle") {
|
||||
rv.set(build_svg_angle(scope).into());
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn svg_svg_element_create_point_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
if require_svg_svg_element_receiver(scope, args.this(), "createSVGPoint") {
|
||||
rv.set(super::super::geometry_runtime::build_svg_point_object(scope).into());
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn svg_svg_element_create_rect_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
if require_svg_svg_element_receiver(scope, args.this(), "createSVGRect") {
|
||||
rv.set(super::super::dom_rect::build_svg_rect_object(scope).into());
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn svg_svg_element_deselect_all_callback<'s>(
|
||||
@@ -2565,9 +2640,12 @@ pub(super) fn svg_svg_element_deselect_all_callback<'s>(
|
||||
|
||||
pub(super) fn svg_svg_element_create_transform_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
_args: v8::FunctionCallbackArguments<'s>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
if !require_svg_svg_element_receiver(scope, args.this(), "createSVGTransform") {
|
||||
return;
|
||||
}
|
||||
rv.set(
|
||||
build_svg_transform(scope, SvgTransform::matrix(SvgMatrixComponents::identity())).into(),
|
||||
);
|
||||
@@ -2578,6 +2656,9 @@ pub(super) fn svg_svg_element_create_transform_from_matrix_callback<'s>(
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
if !require_svg_svg_element_receiver(scope, args.this(), "createSVGTransformFromMatrix") {
|
||||
return;
|
||||
}
|
||||
let Some(components) =
|
||||
svg_dom_matrix_2d_init_arg(scope, &args, "SVGSVGElement.createSVGTransformFromMatrix")
|
||||
else {
|
||||
@@ -2881,12 +2962,31 @@ pub(super) fn svg_length_new_value_specified_units_callback<'s>(
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
if !require_svg_receiver(
|
||||
scope,
|
||||
args.this(),
|
||||
SVG_LENGTH_UNIT_TYPE_SLOT,
|
||||
"SVGLength",
|
||||
"newValueSpecifiedUnits",
|
||||
) {
|
||||
return;
|
||||
}
|
||||
let Some(parsed) = webidl::parse_args::<SvgLengthNewValueSpecifiedUnitsArgs>(scope, &args)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
if !svg_length_unit_type_is_supported(parsed.unit_type as u32) {
|
||||
throw_dom_exception(
|
||||
scope,
|
||||
"NotSupportedError",
|
||||
9,
|
||||
"The SVG length unit type is not supported.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
set_svg_length_numeric_value(scope, args.this(), parsed.value, parsed.unit_type as u32);
|
||||
reflect_svg_length_to_owner_attribute(scope, args.this());
|
||||
reflect_svg_value_list_item_to_owner_list(scope, args.this(), SvgListKind::Length);
|
||||
rv.set_undefined();
|
||||
}
|
||||
|
||||
@@ -2935,16 +3035,39 @@ pub(super) fn svg_length_convert_to_specified_units_callback<'s>(
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
if !require_svg_receiver(
|
||||
scope,
|
||||
args.this(),
|
||||
SVG_LENGTH_UNIT_TYPE_SLOT,
|
||||
"SVGLength",
|
||||
"convertToSpecifiedUnits",
|
||||
) {
|
||||
return;
|
||||
}
|
||||
let Some(parsed) = webidl::parse_args::<SvgLengthConvertToSpecifiedUnitsArgs>(scope, &args)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
if !svg_length_unit_type_is_supported(parsed.unit_type as u32) {
|
||||
throw_dom_exception(
|
||||
scope,
|
||||
"NotSupportedError",
|
||||
9,
|
||||
"The SVG length unit type is not supported.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
let value = svg_length_number_slot(scope, args.this(), SVG_LENGTH_VALUE_SLOT).unwrap_or(0.0);
|
||||
set_svg_length_numeric_value(scope, args.this(), value, parsed.unit_type as u32);
|
||||
reflect_svg_length_to_owner_attribute(scope, args.this());
|
||||
reflect_svg_value_list_item_to_owner_list(scope, args.this(), SvgListKind::Length);
|
||||
rv.set_undefined();
|
||||
}
|
||||
|
||||
fn svg_length_unit_type_is_supported(unit_type: u32) -> bool {
|
||||
(SVG_LENGTH_TYPE_NUMBER..=SVG_LENGTH_TYPE_PC).contains(&unit_type)
|
||||
}
|
||||
|
||||
pub(super) fn svg_angle_convert_to_specified_units_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
|
||||
@@ -287,7 +287,7 @@ struct SvgMatrixRotateFromVectorArgs {
|
||||
struct SvgLengthNewValueSpecifiedUnitsArgs {
|
||||
#[webidl(required, converter = "unsigned_short")]
|
||||
unit_type: u16,
|
||||
#[webidl(required)]
|
||||
#[webidl(required, converter = "double")]
|
||||
value: f64,
|
||||
}
|
||||
|
||||
|
||||
@@ -2315,6 +2315,139 @@ fn svg_marker_orient_angle_is_a_live_animated_angle() {
|
||||
assert_eq!(result, "ok");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn svg_svg_element_value_factories_create_typed_objects() {
|
||||
let mut vm = new_parsed_test_vm(
|
||||
"https://svg-value-factories.test/",
|
||||
"<!doctype html><html><body></body></html>",
|
||||
);
|
||||
|
||||
let result = vm
|
||||
.eval(
|
||||
r#"
|
||||
(() => {
|
||||
const assert = (condition, message) => {
|
||||
if (!condition) throw new Error(message);
|
||||
};
|
||||
const rejectsTypeError = callback => {
|
||||
try {
|
||||
callback();
|
||||
return false;
|
||||
} catch (error) {
|
||||
return error instanceof TypeError;
|
||||
}
|
||||
};
|
||||
const rejectsDom = (name, callback) => {
|
||||
try {
|
||||
callback();
|
||||
return false;
|
||||
} catch (error) {
|
||||
return error.name === name;
|
||||
}
|
||||
};
|
||||
const ns = "http://www.w3.org/2000/svg";
|
||||
const svg = document.createElementNS(ns, "svg");
|
||||
const wrongReceiver = document.createElementNS(ns, "rect");
|
||||
|
||||
const factoryNames = [
|
||||
"createSVGNumber",
|
||||
"createSVGLength",
|
||||
"createSVGAngle",
|
||||
"createSVGPoint",
|
||||
"createSVGMatrix",
|
||||
"createSVGRect",
|
||||
"createSVGTransform",
|
||||
"createSVGTransformFromMatrix",
|
||||
];
|
||||
for (const name of factoryNames) {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(SVGSVGElement.prototype, name);
|
||||
assert(typeof descriptor.value === "function", `${name} method`);
|
||||
assert(descriptor.value.length === 0, `${name} arity`);
|
||||
assert(descriptor.enumerable && descriptor.configurable && descriptor.writable,
|
||||
`${name} descriptor flags`);
|
||||
assert(rejectsTypeError(() => descriptor.value.call(wrongReceiver)),
|
||||
`${name} receiver brand`);
|
||||
}
|
||||
|
||||
const number = svg.createSVGNumber();
|
||||
const length = svg.createSVGLength();
|
||||
const point = svg.createSVGPoint();
|
||||
const rect = svg.createSVGRect();
|
||||
assert(number instanceof SVGNumber && number.value === 0, "SVGNumber result");
|
||||
assert(length instanceof SVGLength && length.value === 0 &&
|
||||
length.unitType === SVGLength.SVG_LENGTHTYPE_NUMBER, "SVGLength result");
|
||||
assert(point instanceof DOMPoint && point instanceof SVGPoint,
|
||||
"SVGPoint result interface");
|
||||
assert(point.x === 0 && point.y === 0 && point.z === 0 && point.w === 1,
|
||||
"SVGPoint defaults");
|
||||
assert(rect instanceof DOMRect && rect instanceof SVGRect,
|
||||
"SVGRect result interface");
|
||||
assert(rect.x === 0 && rect.y === 0 && rect.width === 0 && rect.height === 0,
|
||||
"SVGRect defaults");
|
||||
assert(svg.createSVGNumber() !== number && svg.createSVGLength() !== length &&
|
||||
svg.createSVGPoint() !== point && svg.createSVGRect() !== rect,
|
||||
"factories return new objects");
|
||||
|
||||
number.value = 2;
|
||||
assert(number.value === 2, "SVGNumber assignment");
|
||||
assert(rejectsTypeError(() => { number.value = NaN; }),
|
||||
"SVGNumber rejects NaN");
|
||||
assert(number.value === 2, "SVGNumber preserves rejected assignment");
|
||||
|
||||
length.valueAsString = "2px";
|
||||
assert(length.value === 2 && length.valueAsString === "2px",
|
||||
"SVGLength assignment");
|
||||
assert(rejectsDom("NotSupportedError", () => {
|
||||
length.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_UNKNOWN);
|
||||
}), "SVGLength rejects unsupported unit");
|
||||
assert(rejectsDom("SyntaxError", () => { length.valueAsString = "10deg"; }),
|
||||
"SVGLength rejects invalid syntax");
|
||||
assert(rejectsTypeError(() => { length.value = NaN; }),
|
||||
"SVGLength rejects NaN");
|
||||
assert(length.value === 2 && length.valueAsString === "2px",
|
||||
"SVGLength preserves rejected assignments");
|
||||
|
||||
point.x = 100;
|
||||
point.y = 200;
|
||||
assert(point.x === 100 && point.y === 200, "SVGPoint assignment");
|
||||
for (const invalid of [point, NaN, Infinity]) {
|
||||
assert(rejectsTypeError(() => { point.x = invalid; }),
|
||||
"SVGPoint rejects non-finite values");
|
||||
assert(point.x === 100, "SVGPoint preserves rejected assignment");
|
||||
}
|
||||
point.y = null;
|
||||
assert(point.y === 0, "SVGPoint converts null");
|
||||
|
||||
Object.assign(rect, { x: 100, y: 200, width: 300, height: 400 });
|
||||
assert(rect.x === 100 && rect.y === 200 && rect.width === 300 &&
|
||||
rect.height === 400, "SVGRect assignment");
|
||||
for (const [property, invalid] of [
|
||||
["x", rect],
|
||||
["y", "aString"],
|
||||
["width", svg],
|
||||
["height", NaN],
|
||||
]) {
|
||||
assert(rejectsTypeError(() => { rect[property] = invalid; }),
|
||||
`SVGRect rejects invalid ${property}`);
|
||||
}
|
||||
rect.y = null;
|
||||
assert(rect.y === 0, "SVGRect converts null");
|
||||
|
||||
const regularPoint = new DOMPoint();
|
||||
const regularRect = new DOMRect();
|
||||
regularPoint.x = NaN;
|
||||
regularRect.x = Infinity;
|
||||
assert(Number.isNaN(regularPoint.x) && regularRect.x === Infinity,
|
||||
"regular geometry objects remain unrestricted");
|
||||
return "ok";
|
||||
})()
|
||||
"#,
|
||||
)
|
||||
.expect("SVG value factory probe should evaluate");
|
||||
|
||||
assert_eq!(result, "ok");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn svg_string_lists_reflect_conditional_processing_attributes() {
|
||||
let mut vm = new_parsed_test_vm(
|
||||
|
||||
@@ -55,11 +55,16 @@ impl SvgLength {
|
||||
}
|
||||
|
||||
pub fn parse_length(raw: &str) -> Option<SvgLength> {
|
||||
raw.trim().parse::<SvgTypesLength>().ok().map(svg_length)
|
||||
raw.trim()
|
||||
.to_ascii_lowercase()
|
||||
.parse::<SvgTypesLength>()
|
||||
.ok()
|
||||
.map(svg_length)
|
||||
}
|
||||
|
||||
pub fn parse_length_list(raw: &str) -> Option<Vec<SvgLength>> {
|
||||
LengthListParser::from(raw)
|
||||
let normalized = raw.to_ascii_lowercase();
|
||||
LengthListParser::from(normalized.as_str())
|
||||
.map(|length| length.ok().map(svg_length))
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -360,6 +360,7 @@ mod tests {
|
||||
assert_close(length.value, 1.5);
|
||||
assert_eq!(length.unit, SvgLengthUnit::Ems);
|
||||
assert_eq!(length.serialize(), "1.5em");
|
||||
assert_eq!(parse_length("1pX").unwrap().unit, SvgLengthUnit::Px);
|
||||
assert_eq!(serialize_number(3.0), "3");
|
||||
assert_eq!(serialize_number(3.25), "3.25");
|
||||
assert!(parse_length("1 px").is_none());
|
||||
|
||||
Reference in New Issue
Block a user