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
1 change: 1 addition & 0 deletions Libraries/LibWeb/Layout/TreeBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,7 @@ RustFFI::FfiDomTreeBuilderCallbacks LayoutTreeBuildBridge::make_ffi_dom_tree_bui
.layout_node_is_attached = existing_layout_node && existing_layout_node->parent(),
.is_svg_container = node.is_svg_container(),
.requires_svg_container = node.requires_svg_container(),
.is_svg_foreign_object = node.is_svg_foreign_object_element(),
}; },
.request_top_layer_zone_rebuild = [](void* node_pointer) {
VERIFY(node_pointer);
Expand Down
49 changes: 19 additions & 30 deletions Libraries/LibWeb/Rust/src/layout/block_formatting_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2232,36 +2232,6 @@ impl<'pass> BlockFormattingContext<'pass> {
}
}

pub(crate) fn place_floats_after_run(&self) {
let floats = self.floats.borrow();
for &floating_box in floats.iter() {
// SAFETY: Float records retain stable state-owned used-values pointers.
let used = floating_box.used_values;
let content_block_offset =
floating_box.top_margin_edge + used.margin_top.get() + used.border_box_top(false);
let inline_offset = if floating_box.side == FloatSide::Left {
// Left-side floats: offset_from_edge is from left edge (0) to left content edge of floating_box.
floating_box.offset_from_edge
} else {
// Right-side floats: offset_from_edge is from right edge (float_containing_block_inline_size) to the left content edge of floating_box.
let float_containing_block_inline_size = match used.inline_size_constraint.get() {
SizeConstraint::MinContent => CssPixels::default(),
// Preserve the MaxContent saturation quirk from the C++ fixed-point subtraction.
SizeConstraint::MaxContent => CssPixels::from_raw(i32::MAX),
SizeConstraint::None => floating_box.percentage_basis_inline_size.unwrap_or_default(),
};
float_containing_block_inline_size - floating_box.offset_from_edge
};
self.place_child(
floating_box.box_,
FfiCssPixelPoint {
x: inline_offset,
y: content_block_offset,
},
);
}
}

pub(crate) fn layout_interrupting_block_inside_inline_context(
&self,
run: &FormattingContextRun<'pass>,
Expand Down Expand Up @@ -2516,6 +2486,25 @@ impl<'pass> BlockFormattingContext<'pass> {
block_size: block_container_used.content_block_size.get(),
},
);

let inline_offset = if side == FloatSide::Left {
floating_box.offset_from_edge
} else {
let float_containing_block_inline_size = match self.used(node).inline_size_constraint.get() {
SizeConstraint::MinContent => CssPixels::default(),
// Preserve the MaxContent saturation quirk from the C++ fixed-point subtraction.
SizeConstraint::MaxContent => CssPixels::from_raw(i32::MAX),
SizeConstraint::None => floating_box.percentage_basis_inline_size.unwrap_or_default(),
};
float_containing_block_inline_size - floating_box.offset_from_edge
};
self.place_child(
node,
FfiCssPixelPoint {
x: inline_offset,
y: content_block_offset,
},
);
}

fn layout_inline_children(
Expand Down
4 changes: 3 additions & 1 deletion Libraries/LibWeb/Rust/src/layout/formatting_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1488,7 +1488,6 @@ fn run_formatting_context<'pass>(
run.state.used_values(&run.callbacks, run.box_),
context.derived_baselines_of_root_box(),
);
context.place_floats_after_run();
result
}
FormattingContextImplementation::Flex(context) => {
Expand Down Expand Up @@ -1817,6 +1816,7 @@ pub unsafe extern "C" fn rust_layout_run_root_layout(
if !first_child.is_invalid() && state.node_facts(&callbacks, first_child).is_svg_svg_box() {
viewport_used.set_content_inline_size(viewport_inline_size);
viewport_used.set_content_block_size(viewport_block_size);
place_child(&state, &callbacks, root, FfiCssPixelPoint::default());
state.create_used_values(&callbacks, first_child, root_constraints);
root_for_layout = first_child;
}
Expand All @@ -1842,6 +1842,7 @@ pub unsafe extern "C" fn rust_layout_run_root_layout(
input,
None,
);
place_child(&state, &callbacks, root_for_layout, FfiCssPixelPoint::default());
run_abspos_layout_pass(state_ref, callbacks, should_collect_devtools_layout_data);
state.commit_replacing(root, std::ptr::null_mut(), &callbacks, sink);
});
Expand Down Expand Up @@ -1885,6 +1886,7 @@ pub unsafe extern "C" fn rust_layout_compute_subtree_layout(
let viewport_used = state.create_used_values(&callbacks, viewport, viewport_constraints);
viewport_used.set_content_inline_size(viewport_inline_size);
viewport_used.set_content_block_size(viewport_block_size);
place_child(&state, &callbacks, viewport, FfiCssPixelPoint::default());
}
let input = LayoutInput::new(
AvailableSpace {
Expand Down
1 change: 1 addition & 0 deletions Libraries/LibWeb/Rust/src/layout/inline_level_iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ impl<'iterator, 'context, 'pass> InlineLevelIteratorGenerator<'iterator, 'contex
let facts = self.context().facts(self.next_node);
if facts.is_inline()
&& facts.has_box_model_metrics()
&& !facts.is_break_node()
&& self.context().style(self.next_node).display().is_flow_inside()
&& !self.is_out_of_flow(self.next_node)
&& !facts.is_atomic_inline()
Expand Down
28 changes: 25 additions & 3 deletions Libraries/LibWeb/Rust/src/layout/tree_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ pub struct FfiPrincipalNodeEntryFacts {
pub layout_node_is_attached: bool,
pub is_svg_container: bool,
pub requires_svg_container: bool,
pub is_svg_foreign_object: bool,
}

#[derive(Clone, Copy)]
Expand Down Expand Up @@ -263,6 +264,7 @@ pub(crate) enum TopLayerEntryDecision {
pub(crate) enum SvgEntryDecision {
Continue,
EnterSvgRoot,
EnterForeignContent,
Skip,
}

Expand Down Expand Up @@ -556,6 +558,8 @@ pub(crate) fn principal_node_entry_decision(
SvgEntryDecision::EnterSvgRoot
} else if facts.requires_svg_container && !context.has_svg_root {
SvgEntryDecision::Skip
} else if facts.is_svg_foreign_object {
SvgEntryDecision::EnterForeignContent
} else {
SvgEntryDecision::Continue
};
Expand Down Expand Up @@ -895,6 +899,8 @@ fn update_svg_resource(
prior_context_value: bool,
) {
context.layout_svg_mask_or_clip_path = true;
let prior_has_svg_root = context.has_svg_root;
context.has_svg_root = true;
state.ancestor_stack.push(layout_node);

if !ancestor_stack_contains_element_layout_node(host, state, resource) {
Expand All @@ -906,6 +912,7 @@ fn update_svg_resource(
}

assert!(state.ancestor_stack.pop().is_some());
context.has_svg_root = prior_has_svg_root;
context.layout_svg_mask_or_clip_path = prior_context_value;
}

Expand Down Expand Up @@ -1348,8 +1355,10 @@ fn update_principal_node_after_entry(
let dom_node = update.dom_node;

let prior_has_svg_root = update.context.has_svg_root;
if entry_decision.svg == SvgEntryDecision::EnterSvgRoot {
update.context.has_svg_root = true;
match entry_decision.svg {
SvgEntryDecision::EnterSvgRoot => update.context.has_svg_root = true,
SvgEntryDecision::EnterForeignContent => update.context.has_svg_root = false,
SvgEntryDecision::Continue | SvgEntryDecision::Skip => {}
}

let (has_layout_node, handled_display_contents) = if entry_decision.svg == SvgEntryDecision::Skip {
Expand Down Expand Up @@ -1504,7 +1513,10 @@ fn update_principal_node_after_entry(
}
}

if entry_decision.svg == SvgEntryDecision::EnterSvgRoot {
if matches!(
entry_decision.svg,
SvgEntryDecision::EnterSvgRoot | SvgEntryDecision::EnterForeignContent
) {
context.has_svg_root = prior_has_svg_root;
}
}
Expand Down Expand Up @@ -3457,6 +3469,7 @@ mod tests {
layout_node_is_attached: true,
is_svg_container: false,
requires_svg_container: false,
is_svg_foreign_object: false,
};
let mut context = TreeBuilderContext::default();
let decision = principal_node_entry_decision(facts, &context);
Expand All @@ -3480,6 +3493,15 @@ mod tests {
let decision = principal_node_entry_decision(facts, &context);
assert!(decision.should_create_layout_node);
assert_eq!(decision.svg, SvgEntryDecision::EnterSvgRoot);

facts.is_svg_container = false;
facts.is_svg_foreign_object = true;
context.has_svg_root = true;
let decision = principal_node_entry_decision(facts, &context);
assert_eq!(decision.svg, SvgEntryDecision::EnterForeignContent);
context.has_svg_root = false;
let decision = principal_node_entry_decision(facts, &context);
assert_eq!(decision.svg, SvgEntryDecision::Skip);
}

#[test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] [BFC] children
BlockContainer <html> at [1,1] [0+1+0 798 0+1+0] [0+1+0 62.8125 0+1+0] [BFC] children: not-inline
BlockContainer <body> at [2,2] [0+1+0 796 0+1+0] [0+1+0 60.8125 0+1+0] children: inline
frag 0 from BlockContainer start: 0, length: 0, rect: [3,3 30x30] baseline: 32
frag 1 from BlockContainer start: 0, length: 0, rect: [4,35 30x30] baseline: 32
frag 1 from BlockContainer start: 0, length: 0, rect: [3,35 30x30] baseline: 32
BlockContainer <div.clump> at [3,3] inline-block [0+1+0 30 0+1+0] [0+1+0 30 0+1+0] [BFC] children: not-inline
BreakNode <br> (not painted)
BlockContainer <div.clump> at [4,35] inline-block [0+1+0 30 0+1+0] [0+1+0 30 0+1+0] [BFC] children: not-inline
BlockContainer <div.clump> at [3,35] inline-block [0+1+0 30 0+1+0] [0+1+0 30 0+1+0] [BFC] children: not-inline

ViewportPaintable (Viewport<#document>) [0,0 800x600]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x64.8125]
PaintableWithLines (BlockContainer<BODY>) [1,1 798x62.8125]
PaintableWithLines (BlockContainer<DIV>.clump) [2,2 32x32]
PaintableWithLines (BlockContainer<DIV>.clump) [3,34 32x32]
PaintableWithLines (BlockContainer<DIV>.clump) [2,34 32x32]

SC for Viewport<#document> [0,0 800x600] (z-index: auto)
SC for BlockContainer<HTML> [1,1 798x62.8125] (z-index: auto)
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] [BFC] children: not-inline
BlockContainer <html> at [0,0] [0+0+0 800 0+0+0] [0+0+0 116 0+0+0] [BFC] children: not-inline
BlockContainer <body> at [8,8] [8+0+0 784 0+0+8] [8+0+0 100 0+0+8] children: inline
frag 0 from SVGSVGBox start: 0, length: 0, rect: [8,8 100x100] baseline: 100
SVGSVGBox <svg> at [8,8] [0+0+0 100 0+0+0] [0+0+0 100 0+0+0] [SVG] children: not-inline
SVGForeignObjectBox <foreignObject> at [8,8] [0+0+0 80 0+0+0] [0+0+0 80 0+0+0] [BFC] children: not-inline
BlockContainer <div> at [8,8] [0+0+0 80 0+0+0] [0+0+0 0 0+0+0] children: not-inline
BlockContainer <(anonymous)> at [8,8] [0+0+0 80 0+0+0] [0+0+0 20 0+0+0] children: inline
frag 0 from SVGSVGBox start: 0, length: 0, rect: [8,8 20x20] baseline: 20
SVGSVGBox <svg> at [8,8] [0+0+0 20 0+0+0] [0+0+0 20 0+0+0] [SVG] children: not-inline
SVGGeometryBox <rect> at [8,8] [0+0+0 20 0+0+0] [0+0+0 20 0+0+0] children: not-inline
TextNode <#text> (not painted)

ViewportPaintable (Viewport<#document>) [0,0 800x600]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x116]
PaintableWithLines (BlockContainer<BODY>) [8,8 784x100]
SVGSVGPaintable (SVGSVGBox<svg>) [8,8 100x100]
SVGForeignObjectPaintable (SVGForeignObjectBox<foreignObject>) [8,8 80x80]
PaintableWithLines (BlockContainer<DIV>) [8,8 80x0]
PaintableWithLines (BlockContainer(anonymous)) [8,8 80x20]
SVGSVGPaintable (SVGSVGBox<svg>) [8,8 20x20]
SVGPathPaintable (SVGGeometryBox<rect>) [8,8 20x20]

SC for Viewport<#document> [0,0 800x600] (z-index: auto)
SC for BlockContainer<HTML> [0,0 800x116] (z-index: auto)
SC for SVGForeignObjectBox<foreignObject> [8,8 80x80] (z-index: auto)
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!DOCTYPE html><body><script>
// SVG graphics elements inside foreignObject content have no SVG rendering
// context and must not get layout boxes; a nested <svg> re-enters one. The
// shape is only expressible via createElementNS (HTML parsing treats
// foreignObject as an integration point).
const SVG = "http://www.w3.org/2000/svg";
const svg = document.createElementNS(SVG, "svg");
svg.setAttribute("width", "100");
svg.setAttribute("height", "100");
const foreign = document.createElementNS(SVG, "foreignObject");
foreign.setAttribute("width", "80");
foreign.setAttribute("height", "80");
const div = document.createElement("div");
const orphanGroup = document.createElementNS(SVG, "g");
const orphanRect = document.createElementNS(SVG, "rect");
orphanRect.setAttribute("width", "10");
orphanRect.setAttribute("height", "10");
orphanGroup.appendChild(orphanRect);
const nestedSvg = document.createElementNS(SVG, "svg");
nestedSvg.setAttribute("width", "20");
nestedSvg.setAttribute("height", "20");
const nestedRect = document.createElementNS(SVG, "rect");
nestedRect.setAttribute("width", "20");
nestedRect.setAttribute("height", "20");
nestedSvg.appendChild(nestedRect);
foreign.appendChild(div);
foreign.appendChild(orphanGroup);
foreign.appendChild(nestedSvg);
svg.appendChild(foreign);
document.body.appendChild(svg);
</script>