Skip to content

Commit 0167bcc

Browse files
committed
LibWeb: Invalidate viewport-relative lengths nested in math functions
Style invalidation on viewport resize only touches elements whose computed values recorded a viewport metric dependency. Lengths resolved by the Rust calc simplification had no way to report that dependency back, so `calc(50vh - 10px)`, `min(50vh, ...)`, `clamp()` and anything reaching them through var() kept their stale pixel values after a resize until something else dirtied the element's style. On sites like YouTube that made a window resize appear to do nothing until a later unrelated invalidation moved everything at once. Carry the resolution context's tracking flag across the FFI boundary so the Rust length absolutization records viewport (and viewport-dependent font metric) resolutions the same way the C++ path does. Adds a test resizing an iframe and checking each construct picks up the new viewport height.
1 parent 27d11da commit 0167bcc

6 files changed

Lines changed: 65 additions & 0 deletions

File tree

Libraries/LibWeb/CSS/Length.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ class WEB_API Length {
7878
m_did_resolve_viewport_relative_length = &did_resolve_viewport_relative_length;
7979
}
8080

81+
[[nodiscard]] bool* viewport_metric_dependency_flag() const
82+
{
83+
return m_did_resolve_viewport_relative_length;
84+
}
85+
8186
void record_viewport_relative_length_resolution() const
8287
{
8388
if (m_did_resolve_viewport_relative_length)

Libraries/LibWeb/CSS/Rust/src/animation.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5238,6 +5238,7 @@ fn animation_length_resolution_context(
52385238
root_font_metrics: font_metrics(&animation_context.root_font_metrics),
52395239
font_metrics_depend_on_viewport_metrics: animation_context.font_metrics_depend_on_viewport_metrics,
52405240
root_font_metrics_depend_on_viewport_metrics: animation_context.root_font_metrics_depend_on_viewport_metrics,
5241+
resolved_viewport_relative_length: std::ptr::null_mut(),
52415242
})
52425243
}
52435244

Libraries/LibWeb/CSS/Rust/src/style_compute.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,19 @@ pub struct FfiLengthResolutionContext {
5353
pub root_font_metrics: FfiFontMetrics,
5454
pub font_metrics_depend_on_viewport_metrics: bool,
5555
pub root_font_metrics_depend_on_viewport_metrics: bool,
56+
/// Optional flag owned by Length::ResolutionContext, set to true whenever a
57+
/// resolution here consumed viewport metrics. Callers that report the
58+
/// dependency through their return value may leave this null.
59+
pub resolved_viewport_relative_length: *mut bool,
60+
}
61+
62+
/// Records a viewport metric dependency on the context's tracking flag, if one is set.
63+
fn record_viewport_relative_length_resolution(context: &FfiLengthResolutionContext) {
64+
if context.resolved_viewport_relative_length.is_null() {
65+
return;
66+
}
67+
// SAFETY: The flag belongs to a Length::ResolutionContext that outlives this call.
68+
unsafe { *context.resolved_viewport_relative_length = true };
5669
}
5770

5871
/// Result of absolutizing a length.
@@ -251,6 +264,9 @@ fn absolutize_length(value: f64, unit: usize, context: &FfiLengthResolutionConte
251264
} else {
252265
context.font_metrics_depend_on_viewport_metrics
253266
};
267+
if depends_on_viewport {
268+
record_viewport_relative_length_resolution(context);
269+
}
254270
FfiAbsolutizedLength {
255271
handled: true,
256272
changed: true,
@@ -259,6 +275,7 @@ fn absolutize_length(value: f64, unit: usize, context: &FfiLengthResolutionConte
259275
}
260276
}
261277
LengthUnitKind::ViewportRelative { axis } => {
278+
record_viewport_relative_length_resolution(context);
262279
let basis = match axis {
263280
ViewportAxis::Width => context.viewport_width,
264281
ViewportAxis::Height => context.viewport_height,
@@ -3840,6 +3857,7 @@ mod tests {
38403857
},
38413858
font_metrics_depend_on_viewport_metrics: false,
38423859
root_font_metrics_depend_on_viewport_metrics: true,
3860+
resolved_viewport_relative_length: std::ptr::null_mut(),
38433861
}
38443862
}
38453863

Libraries/LibWeb/CSS/StyleComputeFFI.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ inline ComputedValuesFFI::FfiLengthResolutionContext to_ffi_length_resolution_co
3434
.root_font_metrics = to_ffi_font_metrics(context.root_font_metrics),
3535
.font_metrics_depend_on_viewport_metrics = context.font_metrics_depend_on_viewport_metrics,
3636
.root_font_metrics_depend_on_viewport_metrics = context.root_font_metrics_depend_on_viewport_metrics,
37+
.resolved_viewport_relative_length = context.viewport_metric_dependency_flag(),
3738
};
3839
}
3940

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
before resize: plain=200 calc=190 min=200 clamp=200 var=190
2+
after resize: plain=100 calc=90 min=100 clamp=100 var=90
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<!DOCTYPE html>
2+
<meta charset="utf-8">
3+
<script src="../include.js"></script>
4+
<iframe id="frame" width="400" height="400" srcdoc="
5+
<style>
6+
html, body { margin: 0 }
7+
div { background: gray }
8+
#plain { height: 50vh }
9+
#calc { height: calc(50vh - 10px) }
10+
#min { height: min(50vh, 4000px) }
11+
#clamp { height: clamp(10px, 50vh, 4000px) }
12+
#var { --h: calc(50vh - 10px); height: var(--h) }
13+
</style>
14+
<div id=plain></div><div id=calc></div><div id=min></div><div id=clamp></div><div id=var></div>
15+
"></iframe>
16+
<script>
17+
asyncTest(done => {
18+
const frame = document.getElementById("frame");
19+
frame.onload = () => {
20+
const heights = () => {
21+
const document = frame.contentDocument;
22+
return ["plain", "calc", "min", "clamp", "var"]
23+
.map(id => `${id}=${document.getElementById(id).getBoundingClientRect().height}`)
24+
.join(" ");
25+
};
26+
27+
println(`before resize: ${heights()}`);
28+
29+
// Shrinking the iframe resizes the child navigable's viewport. Every viewport-relative
30+
// length must be recomputed, including the ones nested inside math functions.
31+
frame.height = 200;
32+
document.body.offsetWidth;
33+
34+
println(`after resize: ${heights()}`);
35+
done();
36+
};
37+
});
38+
</script>

0 commit comments

Comments
 (0)