diff --git a/moli-renderer-v8/src/native_bridge/element/styles/declaration/values/computed_properties.rs b/moli-renderer-v8/src/native_bridge/element/styles/declaration/values/computed_properties.rs index 4a46349585..e5f4835b0f 100644 --- a/moli-renderer-v8/src/native_bridge/element/styles/declaration/values/computed_properties.rs +++ b/moli-renderer-v8/src/native_bridge/element/styles/declaration/values/computed_properties.rs @@ -1,5 +1,7 @@ use super::*; +mod background; + pub(in crate::native_bridge::element::styles) fn style_property_value_with_context( runtime: &JsContextHost, handle: DomHandle, @@ -105,6 +107,9 @@ pub(super) fn computed_style_property_value_after_style_update( if property == "font-variant" { return computed_font_variant_shorthand_value(runtime, handle, resolution); } + if property == "background" { + return background::computed_shorthand(runtime, handle, resolution); + } if property == "mask" { return computed_mask_shorthand_value(runtime, handle, resolution); } diff --git a/moli-renderer-v8/src/native_bridge/element/styles/declaration/values/computed_properties/background.rs b/moli-renderer-v8/src/native_bridge/element/styles/declaration/values/computed_properties/background.rs new file mode 100644 index 0000000000..927af2f2e7 --- /dev/null +++ b/moli-renderer-v8/src/native_bridge/element/styles/declaration/values/computed_properties/background.rs @@ -0,0 +1,67 @@ +use super::*; + +/// CSSOM serializes the coordinated background layers, including initial +/// components. Specified-value shorthand serialization would omit those parts. +pub(super) fn computed_shorthand( + runtime: &JsContextHost, + handle: DomHandle, + resolution: StyleResolutionContext<'_>, +) -> String { + let [ + Some(images), + Some(repeats), + Some(attachments), + Some(positions_x), + Some(positions_y), + Some(sizes), + Some(origins), + Some(clips), + ] = [ + "background-image", + "background-repeat", + "background-attachment", + "background-position-x", + "background-position-y", + "background-size", + "background-origin", + "background-clip", + ] + .map(|property| { + top_level_comma_separated_component_values( + &resolution.computed_property(runtime, handle, property), + ) + .filter(|values| !values.is_empty()) + }) + else { + return String::new(); + }; + let color = resolution.computed_property(runtime, handle, "background-color"); + images + .iter() + .enumerate() + .map(|(index, image)| { + // The image list controls the layer count. Other lists repeat or + // truncate to that count, and only the final layer carries color. + let color = if index + 1 == images.len() { + format!("{color} ") + } else { + String::new() + }; + format!( + "{color}{image} {} {} {} {} / {} {} {}", + layer_value(&repeats, index), + layer_value(&attachments, index), + layer_value(&positions_x, index), + layer_value(&positions_y, index), + layer_value(&sizes, index), + layer_value(&origins, index), + layer_value(&clips, index), + ) + }) + .collect::>() + .join(", ") +} + +fn layer_value(values: &[String], index: usize) -> &str { + &values[index % values.len()] +} diff --git a/moli-renderer-v8/src/script_vm/tests/dom_xhr/computed_style.rs b/moli-renderer-v8/src/script_vm/tests/dom_xhr/computed_style.rs index 854b983bdc..4d936e5573 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_xhr/computed_style.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_xhr/computed_style.rs @@ -58,6 +58,7 @@ mod advanced_style_values; mod computed_style_access; mod content_and_invalidation; mod cross_document_and_animations; +mod css_computed_background; mod css_computed_map_enumeration; mod nested_document_invalidation; mod properties_and_selectors; diff --git a/moli-renderer-v8/src/script_vm/tests/dom_xhr/computed_style/css_computed_background.js b/moli-renderer-v8/src/script_vm/tests/dom_xhr/computed_style/css_computed_background.js new file mode 100644 index 0000000000..e1ad052488 --- /dev/null +++ b/moli-renderer-v8/src/script_vm/tests/dom_xhr/computed_style/css_computed_background.js @@ -0,0 +1,33 @@ +(() => { + const check = (ok, message) => {if (!ok) throw new Error(message);}; + const cases = [ + ['', 'rgba(0, 0, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box'], + ['background:blue', 'rgb(0, 0, 255) none repeat scroll 0% 0% / auto padding-box border-box'], + ['color:red;background:currentcolor', 'rgb(255, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box'], + ['background:blue content-box', 'rgb(0, 0, 255) none repeat scroll 0% 0% / auto content-box content-box'], + ['background:blue;background-repeat:no-repeat,repeat', 'rgb(0, 0, 255) none no-repeat scroll 0% 0% / auto padding-box border-box'], + ['background-image:none,none;background-color:blue', 'none repeat scroll 0% 0% / auto padding-box border-box, rgb(0, 0, 255) none repeat scroll 0% 0% / auto padding-box border-box'], + ['background-image:none,none;background-size:10px,20px,30px', 'none repeat scroll 0% 0% / 10px auto padding-box border-box, rgba(0, 0, 0, 0) none repeat scroll 0% 0% / 20px auto padding-box border-box'], + ['background:blue;background-position-x:10%;background-position-y:20%', 'rgb(0, 0, 255) none repeat scroll 10% 20% / auto padding-box border-box'], + ['background-image:linear-gradient(red,blue),linear-gradient(black,white);background-repeat:no-repeat', 'linear-gradient(rgb(255, 0, 0), rgb(0, 0, 255)) no-repeat scroll 0% 0% / auto padding-box border-box, rgba(0, 0, 0, 0) linear-gradient(rgb(0, 0, 0), rgb(255, 255, 255)) no-repeat scroll 0% 0% / auto padding-box border-box'], + ]; + for (const [text, expected] of cases) { + const element = document.createElement('div'); + element.style.cssText = text; + document.body.append(element); + const style = getComputedStyle(element); + check(style.background === expected, text + ': ' + style.background); + element.remove(); + } + const element = document.createElement('div'); + element.style.background = 'blue'; document.body.append(element); + const computed = getComputedStyle(element); + const previous = computed.background; + element.style.backgroundColor = 'red'; + check(String(previous).startsWith('rgb(0, 0, 255) '), 'retained value is immutable'); + check(computed.background.startsWith('rgb(255, 0, 0) '), 'retained declaration observes mutations'); + check(computed.backgroundImage === 'none', 'longhand value preserved'); + element.remove(); + check(computed.background === '', 'inactive owner is empty'); + return true; +})() diff --git a/moli-renderer-v8/src/script_vm/tests/dom_xhr/computed_style/css_computed_background.rs b/moli-renderer-v8/src/script_vm/tests/dom_xhr/computed_style/css_computed_background.rs new file mode 100644 index 0000000000..05b4c85aab --- /dev/null +++ b/moli-renderer-v8/src/script_vm/tests/dom_xhr/computed_style/css_computed_background.rs @@ -0,0 +1,10 @@ +use super::*; + +#[test] +fn css_computed_background_preserves_coordinated_layers() { + let mut vm = new_parsed_test_vm("https://computed-background.test/", ""); + assert_eq!( + vm.eval(include_str!("css_computed_background.js")).unwrap(), + "true" + ); +}