Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions Libraries/LibWeb/Rust/src/layout/layout_node_arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ pub(crate) struct IntrinsicInlineSizeMeasurement {

#[derive(Default)]
struct IntrinsicSizeMaps {
// This is a subtree fact and shares the intrinsic cache's epoch so descendant changes invalidate it.
inline_size_depends_on_block_size: Option<bool>,
min_content_inline_size: HashMap<IntrinsicSizeCacheKey, IntrinsicInlineSizeMeasurement>,
max_content_inline_size: HashMap<IntrinsicSizeCacheKey, IntrinsicInlineSizeMeasurement>,
min_content_block_size: HashMap<IntrinsicSizeCacheKey, CssPixels>,
Expand Down Expand Up @@ -643,6 +645,37 @@ impl LayoutNodeArena {
.copied()
}

pub(crate) fn intrinsic_inline_size_depends_on_block_size(
&self,
data: &NodeData,
compute: impl FnOnce() -> bool,
) -> bool {
if data.intrinsic_cache_epoch == u16::MAX {
return compute();
}

let (index, metadata) = self.slot_for_data(std::ptr::from_ref(data));
{
let caches = self.intrinsic_size_caches.borrow();
if let Some(slot) = caches.get(index as usize)
&& slot.generation == metadata.generation
&& slot.epoch == data.intrinsic_cache_epoch
&& let Some(value) = slot
.sizes
.as_ref()
.and_then(|sizes| sizes.inline_size_depends_on_block_size)
{
return value;
}
}

let value = compute();
self.with_intrinsic_size_maps_mut(data, |maps| {
maps.inline_size_depends_on_block_size = Some(value);
});
value
}

pub(crate) fn intrinsic_inline_size_measurement_cache_put(
&self,
data: &NodeData,
Expand Down Expand Up @@ -1424,6 +1457,7 @@ mod tests {
has_last_baseline: true,
last_baseline: CssPixels::from_raw(128),
};
let dependency_computations = Cell::new(0);

// SAFETY: The allocation remains live until it is explicitly freed below.
let first_data = unsafe { &mut *first.data };
Expand All @@ -1446,6 +1480,12 @@ mod tests {
),
Some(inline_measurement)
);
assert!(arena.intrinsic_inline_size_depends_on_block_size(first_data, || {
dependency_computations.set(dependency_computations.get() + 1);
true
}));
assert!(arena.intrinsic_inline_size_depends_on_block_size(first_data, || false));
assert_eq!(dependency_computations.get(), 1);

first_data.intrinsic_cache_epoch += 1;
assert_eq!(
Expand All @@ -1460,6 +1500,11 @@ mod tests {
),
None
);
assert!(!arena.intrinsic_inline_size_depends_on_block_size(first_data, || {
dependency_computations.set(dependency_computations.get() + 1);
false
}));
assert_eq!(dependency_computations.get(), 2);
arena.free(first.slot, first.generation);

let second = arena.allocate();
Expand Down
70 changes: 67 additions & 3 deletions Libraries/LibWeb/Rust/src/layout/sizing_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,58 @@ impl SizingContext {
!self.callbacks.first_child(node).is_invalid()
}

fn block_size_for_intrinsic_inline_measurement_cache_key(
&self,
root: Node,
block_size: Option<CssPixels>,
) -> Option<CssPixels> {
let block_size = block_size?;
// OPTIMIZATION: A definite block size only distinguishes intrinsic inline measurements when a cross-axis
// dependency can transfer it back into the inline axis. Reuse the common horizontal-flow measurement across
// assigned block sizes, while preserving distinct entries for orthogonal flows and aspect-ratio transfers.
let depends_on_block_size =
self.callbacks
.arena()
.intrinsic_inline_size_depends_on_block_size(self.callbacks.node_data(root), || {
let mut pending = vec![root];
while let Some(node) = pending.pop() {
let facts = self.facts(node);
if !facts.is_text_node() {
let style = self.style(node);
if style.writing_mode() != writing_mode::HORIZONTAL_TB {
return true;
}
if style.display().is_flex_inside()
&& matches!(
style.flex_direction(),
flex_direction::COLUMN | flex_direction::COLUMN_REVERSE
)
&& style.flex_wrap() != flex_wrap::NOWRAP
{
return true;
}
if facts.has_preferred_aspect_ratio()
&& (style.height().contains_percentage()
|| style.min_height().contains_percentage()
|| style.max_height().contains_percentage()
|| (style.height().is_auto()
&& (has_flag(facts.data(), NodeFlag::IsFlexItem)
|| has_flag(facts.data(), NodeFlag::IsGridItem))))
{
return true;
}
}
let mut child = self.first_child(node);
while !child.is_invalid() {
pending.push(child);
child = self.next_sibling(child);
}
}
false
});
depends_on_block_size.then_some(block_size)
}

fn content_block_size_from_aspect_ratio(&self, node: Node, content_inline_size: CssPixels) -> CssPixels {
let style = self.style(node);
let used = self.used(node);
Expand Down Expand Up @@ -1454,7 +1506,11 @@ impl SizingContext {
if let Some(cached) = self.intrinsic_inline_measurement_cache_get(
node,
IntrinsicSizeCacheKind::MinContentInline,
cache_key(None, block_size, constraints),
cache_key(
None,
self.block_size_for_intrinsic_inline_measurement_cache_key(node, block_size),
constraints,
),
) {
return cached.automatic_content_inline_size;
}
Expand All @@ -1473,7 +1529,11 @@ impl SizingContext {
self.intrinsic_inline_measurement_cache_get(
node,
IntrinsicSizeCacheKind::MaxContentInline,
cache_key(None, block_size, constraints),
cache_key(
None,
self.block_size_for_intrinsic_inline_measurement_cache_key(node, block_size),
constraints,
),
)?
.min_content_inline_size_from_max_content_layout
}
Expand Down Expand Up @@ -1671,7 +1731,11 @@ impl SizingContext {
IntrinsicSizeCacheKind::MaxContentInline => (SizeConstraint::MaxContent, AvailableSize::MaxContent),
_ => unreachable!("inline measurement cache kind must use the inline axis"),
};
let key = cache_key(None, block_size, constraints);
let key = cache_key(
None,
self.block_size_for_intrinsic_inline_measurement_cache_key(node, block_size),
constraints,
);
if let Some(cached) = self.intrinsic_inline_measurement_cache_get(node, kind, key) {
return cached.automatic_content_inline_size;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
width: 90
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!DOCTYPE html>
<script src="../include.js"></script>
<style>
.outer {
display: flex;
flex-direction: column;
height: 100px;
width: max-content;
}

.inner {
display: flex;
flex-direction: column;
flex-wrap: wrap;
}

.item {
flex: none;
height: 60px;
}

.first { width: 20px; }
.second { width: 30px; }
.third { width: 40px; }
</style>
<div class="outer">
<div class="inner">
<div class="item first"></div>
<div class="item second"></div>
<div class="item third"></div>
</div>
</div>
<script>
test(() => {
const inner = document.querySelector(".inner");
println(`width: ${inner.getBoundingClientRect().width}`);
});
</script>
Loading