mirror of
https://github.com/servo/servo.git
synced 2025-08-07 06:25:32 +01:00
Auto merge of #25207 - servo:replaced, r=nox
min/max-width/height, replaced elements
This commit is contained in:
commit
6cf618ae9f
21 changed files with 701 additions and 440 deletions
|
@ -17,7 +17,6 @@ use std::sync::Arc;
|
||||||
use style::dom::TNode;
|
use style::dom::TNode;
|
||||||
use style::properties::ComputedValues;
|
use style::properties::ComputedValues;
|
||||||
use style::selector_parser::PseudoElement;
|
use style::selector_parser::PseudoElement;
|
||||||
use style::values::computed::Length;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum WhichPseudoElement {
|
pub enum WhichPseudoElement {
|
||||||
|
@ -299,7 +298,10 @@ impl Drop for BoxSlot<'_> {
|
||||||
pub(crate) trait NodeExt<'dom>: 'dom + Copy + LayoutNode + Send + Sync {
|
pub(crate) trait NodeExt<'dom>: 'dom + Copy + LayoutNode + Send + Sync {
|
||||||
fn is_element(self) -> bool;
|
fn is_element(self) -> bool;
|
||||||
fn as_text(self) -> Option<String>;
|
fn as_text(self) -> Option<String>;
|
||||||
fn as_image(self) -> Option<(Option<Arc<NetImage>>, Vec2<Length>)>;
|
|
||||||
|
/// Returns the image if it’s loaded, and its size in image pixels
|
||||||
|
/// adjusted for `image_density`.
|
||||||
|
fn as_image(self) -> Option<(Option<Arc<NetImage>>, Vec2<f64>)>;
|
||||||
fn first_child(self) -> Option<Self>;
|
fn first_child(self) -> Option<Self>;
|
||||||
fn next_sibling(self) -> Option<Self>;
|
fn next_sibling(self) -> Option<Self>;
|
||||||
fn parent_node(self) -> Option<Self>;
|
fn parent_node(self) -> Option<Self>;
|
||||||
|
@ -328,7 +330,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_image(self) -> Option<(Option<Arc<NetImage>>, Vec2<Length>)> {
|
fn as_image(self) -> Option<(Option<Arc<NetImage>>, Vec2<f64>)> {
|
||||||
let node = self.to_threadsafe();
|
let node = self.to_threadsafe();
|
||||||
let (resource, metadata) = node.image_data()?;
|
let (resource, metadata) = node.image_data()?;
|
||||||
let (width, height) = resource
|
let (width, height) = resource
|
||||||
|
@ -336,14 +338,14 @@ where
|
||||||
.map(|image| (image.width, image.height))
|
.map(|image| (image.width, image.height))
|
||||||
.or_else(|| metadata.map(|metadata| (metadata.width, metadata.height)))
|
.or_else(|| metadata.map(|metadata| (metadata.width, metadata.height)))
|
||||||
.unwrap_or((0, 0));
|
.unwrap_or((0, 0));
|
||||||
let (mut width, mut height) = (width as f32, height as f32);
|
let (mut width, mut height) = (width as f64, height as f64);
|
||||||
if let Some(density) = node.image_density().filter(|density| *density != 1.) {
|
if let Some(density) = node.image_density().filter(|density| *density != 1.) {
|
||||||
width = (width as f64 / density) as f32;
|
width = width / density;
|
||||||
height = (height as f64 / density) as f32;
|
height = height / density;
|
||||||
}
|
}
|
||||||
let size = Vec2 {
|
let size = Vec2 {
|
||||||
x: Length::new(width),
|
x: width,
|
||||||
y: Length::new(height),
|
y: height,
|
||||||
};
|
};
|
||||||
Some((resource, size))
|
Some((resource, size))
|
||||||
}
|
}
|
||||||
|
|
|
@ -393,7 +393,7 @@ where
|
||||||
style.clone(),
|
style.clone(),
|
||||||
display_inside,
|
display_inside,
|
||||||
contents,
|
contents,
|
||||||
ContentSizesRequest::inline_if(style.inline_size_is_auto()),
|
ContentSizesRequest::inline_if(!style.inline_size_is_length()),
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
|
@ -590,7 +590,7 @@ where
|
||||||
&style,
|
&style,
|
||||||
ContentSizesRequest::inline_if(
|
ContentSizesRequest::inline_if(
|
||||||
max_assign_in_flow_outer_content_sizes_to.is_some() &&
|
max_assign_in_flow_outer_content_sizes_to.is_some() &&
|
||||||
style.inline_size_is_auto(),
|
!style.inline_size_is_length(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if let Some(to) = max_assign_in_flow_outer_content_sizes_to {
|
if let Some(to) = max_assign_in_flow_outer_content_sizes_to {
|
||||||
|
@ -607,7 +607,7 @@ where
|
||||||
} => {
|
} => {
|
||||||
let content_sizes = ContentSizesRequest::inline_if(
|
let content_sizes = ContentSizesRequest::inline_if(
|
||||||
max_assign_in_flow_outer_content_sizes_to.is_some() &&
|
max_assign_in_flow_outer_content_sizes_to.is_some() &&
|
||||||
style.inline_size_is_auto(),
|
!style.inline_size_is_length(),
|
||||||
);
|
);
|
||||||
let contents = IndependentFormattingContext::construct(
|
let contents = IndependentFormattingContext::construct(
|
||||||
context,
|
context,
|
||||||
|
|
|
@ -33,7 +33,7 @@ impl FloatBox {
|
||||||
display_inside: DisplayInside,
|
display_inside: DisplayInside,
|
||||||
contents: Contents<impl NodeExt<'dom>>,
|
contents: Contents<impl NodeExt<'dom>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let content_sizes = ContentSizesRequest::inline_if(style.inline_size_is_auto());
|
let content_sizes = ContentSizesRequest::inline_if(!style.inline_size_is_length());
|
||||||
Self {
|
Self {
|
||||||
contents: IndependentFormattingContext::construct(
|
contents: IndependentFormattingContext::construct(
|
||||||
context,
|
context,
|
||||||
|
|
|
@ -465,9 +465,7 @@ fn layout_atomic<'box_tree>(
|
||||||
|
|
||||||
let fragment = match atomic.as_replaced() {
|
let fragment = match atomic.as_replaced() {
|
||||||
Ok(replaced) => {
|
Ok(replaced) => {
|
||||||
// FIXME: implement https://drafts.csswg.org/css2/visudet.html#inline-replaced-width
|
let size = replaced.used_size_as_if_inline_element(ifc.containing_block, &atomic.style);
|
||||||
// and https://drafts.csswg.org/css2/visudet.html#inline-replaced-height
|
|
||||||
let size = Vec2::zero();
|
|
||||||
let fragments = replaced.make_fragments(&atomic.style, size.clone());
|
let fragments = replaced.make_fragments(&atomic.style, size.clone());
|
||||||
let content_rect = Rect { start_corner, size };
|
let content_rect = Rect { start_corner, size };
|
||||||
BoxFragment {
|
BoxFragment {
|
||||||
|
@ -482,10 +480,29 @@ fn layout_atomic<'box_tree>(
|
||||||
},
|
},
|
||||||
Err(non_replaced) => {
|
Err(non_replaced) => {
|
||||||
let box_size = atomic.style.box_size();
|
let box_size = atomic.style.box_size();
|
||||||
let inline_size = box_size.inline.percentage_relative_to(cbis).auto_is(|| {
|
let max_box_size = atomic
|
||||||
|
.style
|
||||||
|
.max_box_size()
|
||||||
|
.percentages_relative_to(ifc.containing_block);
|
||||||
|
let min_box_size = atomic
|
||||||
|
.style
|
||||||
|
.min_box_size()
|
||||||
|
.percentages_relative_to(ifc.containing_block)
|
||||||
|
.auto_is(Length::zero);
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css2/visudet.html#inlineblock-width
|
||||||
|
let tentative_inline_size =
|
||||||
|
box_size.inline.percentage_relative_to(cbis).auto_is(|| {
|
||||||
let available_size = cbis - pbm.inline_sum();
|
let available_size = cbis - pbm.inline_sum();
|
||||||
atomic.content_sizes.shrink_to_fit(available_size)
|
atomic.content_sizes.shrink_to_fit(available_size)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css2/visudet.html#min-max-widths
|
||||||
|
// In this case “applying the rules above again” with a non-auto inline-size
|
||||||
|
// always results in that size.
|
||||||
|
let inline_size = tentative_inline_size
|
||||||
|
.clamp_between_extremums(min_box_size.inline, max_box_size.inline);
|
||||||
|
|
||||||
let block_size = box_size
|
let block_size = box_size
|
||||||
.block
|
.block
|
||||||
.maybe_percentage_relative_to(ifc.containing_block.block_size.non_auto());
|
.maybe_percentage_relative_to(ifc.containing_block.block_size.non_auto());
|
||||||
|
@ -508,7 +525,16 @@ fn layout_atomic<'box_tree>(
|
||||||
dummy_tree_rank,
|
dummy_tree_rank,
|
||||||
ifc.absolutely_positioned_fragments,
|
ifc.absolutely_positioned_fragments,
|
||||||
);
|
);
|
||||||
let block_size = block_size.auto_is(|| independent_layout.content_block_size);
|
|
||||||
|
// https://drafts.csswg.org/css2/visudet.html#block-root-margin
|
||||||
|
let tentative_block_size = block_size.auto_is(|| independent_layout.content_block_size);
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css2/visudet.html#min-max-heights
|
||||||
|
// In this case “applying the rules above again” with a non-auto block-size
|
||||||
|
// always results in that size.
|
||||||
|
let block_size = tentative_block_size
|
||||||
|
.clamp_between_extremums(min_box_size.block, max_box_size.block);
|
||||||
|
|
||||||
let content_rect = Rect {
|
let content_rect = Rect {
|
||||||
start_corner,
|
start_corner,
|
||||||
size: Vec2 {
|
size: Vec2 {
|
||||||
|
|
|
@ -21,8 +21,7 @@ use rayon_croissant::ParallelIteratorExt;
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use style::computed_values::position::T as Position;
|
use style::computed_values::position::T as Position;
|
||||||
use style::properties::ComputedValues;
|
use style::properties::ComputedValues;
|
||||||
use style::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
|
use style::values::computed::{Length, LengthOrAuto};
|
||||||
use style::values::generics::length::MaxSize;
|
|
||||||
use style::Zero;
|
use style::Zero;
|
||||||
|
|
||||||
mod construct;
|
mod construct;
|
||||||
|
@ -365,10 +364,14 @@ fn layout_in_flow_non_replaced_block_level<'a>(
|
||||||
let pb = &padding + &border;
|
let pb = &padding + &border;
|
||||||
let pb_inline_sum = pb.inline_sum();
|
let pb_inline_sum = pb.inline_sum();
|
||||||
|
|
||||||
let box_size = percent_resolved_box_size(style.box_size(), containing_block);
|
let box_size = style.box_size().percentages_relative_to(containing_block);
|
||||||
let max_box_size = percent_resolved_max_box_size(style.max_box_size(), containing_block);
|
let max_box_size = style
|
||||||
let min_box_size =
|
.max_box_size()
|
||||||
percent_resolved_box_size(style.min_box_size(), containing_block).auto_is(Length::zero);
|
.percentages_relative_to(containing_block);
|
||||||
|
let min_box_size = style
|
||||||
|
.min_box_size()
|
||||||
|
.percentages_relative_to(containing_block)
|
||||||
|
.auto_is(Length::zero);
|
||||||
|
|
||||||
// https://drafts.csswg.org/css2/visudet.html#min-max-widths
|
// https://drafts.csswg.org/css2/visudet.html#min-max-widths
|
||||||
let solve_inline_margins = |inline_size| {
|
let solve_inline_margins = |inline_size| {
|
||||||
|
@ -411,7 +414,7 @@ fn layout_in_flow_non_replaced_block_level<'a>(
|
||||||
// https://drafts.csswg.org/css2/visudet.html#min-max-heights
|
// https://drafts.csswg.org/css2/visudet.html#min-max-heights
|
||||||
let mut block_size = box_size.block;
|
let mut block_size = box_size.block;
|
||||||
if let LengthOrAuto::LengthPercentage(ref mut block_size) = block_size {
|
if let LengthOrAuto::LengthPercentage(ref mut block_size) = block_size {
|
||||||
*block_size = clamp_between_extremums(*block_size, min_box_size.block, max_box_size.block);
|
*block_size = block_size.clamp_between_extremums(min_box_size.block, max_box_size.block);
|
||||||
}
|
}
|
||||||
|
|
||||||
let containing_block_for_children = ContainingBlock {
|
let containing_block_for_children = ContainingBlock {
|
||||||
|
@ -475,11 +478,9 @@ fn layout_in_flow_non_replaced_block_level<'a>(
|
||||||
.collapsed_through;
|
.collapsed_through;
|
||||||
let relative_adjustement = relative_adjustement(style, inline_size, block_size);
|
let relative_adjustement = relative_adjustement(style, inline_size, block_size);
|
||||||
let block_size = block_size.auto_is(|| {
|
let block_size = block_size.auto_is(|| {
|
||||||
clamp_between_extremums(
|
flow_layout
|
||||||
flow_layout.content_block_size,
|
.content_block_size
|
||||||
min_box_size.block,
|
.clamp_between_extremums(min_box_size.block, max_box_size.block)
|
||||||
max_box_size.block,
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
let content_rect = Rect {
|
let content_rect = Rect {
|
||||||
start_corner: Vec2 {
|
start_corner: Vec2 {
|
||||||
|
@ -520,140 +521,20 @@ fn layout_in_flow_replaced_block_level<'a>(
|
||||||
style: &Arc<ComputedValues>,
|
style: &Arc<ComputedValues>,
|
||||||
replaced: &ReplacedContent,
|
replaced: &ReplacedContent,
|
||||||
) -> BoxFragment {
|
) -> BoxFragment {
|
||||||
|
let size = replaced.used_size_as_if_inline_element(containing_block, style);
|
||||||
|
|
||||||
let cbis = containing_block.inline_size;
|
let cbis = containing_block.inline_size;
|
||||||
let padding = style.padding().percentages_relative_to(cbis);
|
let padding = style.padding().percentages_relative_to(cbis);
|
||||||
let border = style.border_width();
|
let border = style.border_width();
|
||||||
let computed_margin = style.margin().percentages_relative_to(cbis);
|
let computed_margin = style.margin().percentages_relative_to(cbis);
|
||||||
let pb = &padding + &border;
|
let pb = &padding + &border;
|
||||||
let mode = style.writing_mode;
|
|
||||||
// FIXME(nox): We shouldn't pretend we always have a fully known intrinsic size.
|
|
||||||
let intrinsic_size = replaced.intrinsic_size.size_to_flow_relative(mode);
|
|
||||||
// FIXME(nox): This can divide by zero.
|
|
||||||
let intrinsic_ratio = intrinsic_size.inline.px() / intrinsic_size.block.px();
|
|
||||||
|
|
||||||
let box_size = percent_resolved_box_size(style.box_size(), containing_block);
|
|
||||||
let min_box_size =
|
|
||||||
percent_resolved_box_size(style.min_box_size(), containing_block).auto_is(Length::zero);
|
|
||||||
let max_box_size = percent_resolved_max_box_size(style.max_box_size(), containing_block);
|
|
||||||
|
|
||||||
let clamp = |inline_size, block_size| {
|
|
||||||
(
|
|
||||||
clamp_between_extremums(inline_size, min_box_size.inline, max_box_size.inline),
|
|
||||||
clamp_between_extremums(block_size, min_box_size.block, max_box_size.block),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
// https://drafts.csswg.org/css2/visudet.html#min-max-widths
|
|
||||||
// https://drafts.csswg.org/css2/visudet.html#min-max-heights
|
|
||||||
let (inline_size, block_size) = match (box_size.inline, box_size.block) {
|
|
||||||
(LengthOrAuto::LengthPercentage(inline), LengthOrAuto::LengthPercentage(block)) => {
|
|
||||||
clamp(inline, block)
|
|
||||||
},
|
|
||||||
(LengthOrAuto::LengthPercentage(inline), LengthOrAuto::Auto) => {
|
|
||||||
clamp(inline, inline / intrinsic_ratio)
|
|
||||||
},
|
|
||||||
(LengthOrAuto::Auto, LengthOrAuto::LengthPercentage(block)) => {
|
|
||||||
clamp(block * intrinsic_ratio, block)
|
|
||||||
},
|
|
||||||
(LengthOrAuto::Auto, LengthOrAuto::Auto) => {
|
|
||||||
enum Violation {
|
|
||||||
None,
|
|
||||||
Below(Length),
|
|
||||||
Above(Length),
|
|
||||||
}
|
|
||||||
let violation = |size, min_size, mut max_size: Option<Length>| {
|
|
||||||
if let Some(max) = max_size.as_mut() {
|
|
||||||
max.max_assign(min_size);
|
|
||||||
}
|
|
||||||
if size < min_size {
|
|
||||||
return Violation::Below(min_size);
|
|
||||||
}
|
|
||||||
match max_size {
|
|
||||||
Some(max_size) if size > max_size => Violation::Above(max_size),
|
|
||||||
_ => Violation::None,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
match (
|
|
||||||
violation(
|
|
||||||
intrinsic_size.inline,
|
|
||||||
min_box_size.inline,
|
|
||||||
max_box_size.inline,
|
|
||||||
),
|
|
||||||
violation(intrinsic_size.block, min_box_size.block, max_box_size.block),
|
|
||||||
) {
|
|
||||||
// Row 1.
|
|
||||||
(Violation::None, Violation::None) => (intrinsic_size.inline, intrinsic_size.block),
|
|
||||||
// Row 2.
|
|
||||||
(Violation::Above(max_inline_size), Violation::None) => {
|
|
||||||
let block_size = (max_inline_size / intrinsic_ratio).max(min_box_size.block);
|
|
||||||
(max_inline_size, block_size)
|
|
||||||
},
|
|
||||||
// Row 3.
|
|
||||||
(Violation::Below(min_inline_size), Violation::None) => {
|
|
||||||
let block_size =
|
|
||||||
clamp_below_max(min_inline_size / intrinsic_ratio, max_box_size.block);
|
|
||||||
(min_inline_size, block_size)
|
|
||||||
},
|
|
||||||
// Row 4.
|
|
||||||
(Violation::None, Violation::Above(max_block_size)) => {
|
|
||||||
let inline_size = (max_block_size * intrinsic_ratio).max(min_box_size.inline);
|
|
||||||
(inline_size, max_block_size)
|
|
||||||
},
|
|
||||||
// Row 5.
|
|
||||||
(Violation::None, Violation::Below(min_block_size)) => {
|
|
||||||
let inline_size =
|
|
||||||
clamp_below_max(min_block_size * intrinsic_ratio, max_box_size.inline);
|
|
||||||
(inline_size, min_block_size)
|
|
||||||
},
|
|
||||||
// Rows 6-7.
|
|
||||||
(Violation::Above(max_inline_size), Violation::Above(max_block_size)) => {
|
|
||||||
if max_inline_size.px() / intrinsic_size.inline.px() <=
|
|
||||||
max_block_size.px() / intrinsic_size.block.px()
|
|
||||||
{
|
|
||||||
// Row 6.
|
|
||||||
let block_size =
|
|
||||||
(max_inline_size / intrinsic_ratio).max(min_box_size.block);
|
|
||||||
(max_inline_size, block_size)
|
|
||||||
} else {
|
|
||||||
// Row 7.
|
|
||||||
let inline_size =
|
|
||||||
(max_block_size * intrinsic_ratio).max(min_box_size.inline);
|
|
||||||
(inline_size, max_block_size)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Rows 8-9.
|
|
||||||
(Violation::Below(min_inline_size), Violation::Below(min_block_size)) => {
|
|
||||||
if min_inline_size.px() / intrinsic_size.inline.px() <=
|
|
||||||
min_block_size.px() / intrinsic_size.block.px()
|
|
||||||
{
|
|
||||||
// Row 8.
|
|
||||||
let inline_size =
|
|
||||||
clamp_below_max(min_block_size * intrinsic_ratio, max_box_size.inline);
|
|
||||||
(inline_size, min_block_size)
|
|
||||||
} else {
|
|
||||||
// Row 9.
|
|
||||||
let block_size =
|
|
||||||
clamp_below_max(min_inline_size / intrinsic_ratio, max_box_size.block);
|
|
||||||
(min_inline_size, block_size)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Row 10.
|
|
||||||
(Violation::Below(min_inline_size), Violation::Above(max_block_size)) => {
|
|
||||||
(min_inline_size, max_block_size)
|
|
||||||
},
|
|
||||||
// Row 11.
|
|
||||||
(Violation::Above(max_inline_size), Violation::Below(min_block_size)) => {
|
|
||||||
(max_inline_size, min_block_size)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let (margin_inline_start, margin_inline_end) = solve_inline_margins_for_in_flow_block_level(
|
let (margin_inline_start, margin_inline_end) = solve_inline_margins_for_in_flow_block_level(
|
||||||
containing_block,
|
containing_block,
|
||||||
pb.inline_sum(),
|
pb.inline_sum(),
|
||||||
computed_margin.inline_start,
|
computed_margin.inline_start,
|
||||||
computed_margin.inline_end,
|
computed_margin.inline_end,
|
||||||
inline_size,
|
size.inline,
|
||||||
);
|
);
|
||||||
let margin = Sides {
|
let margin = Sides {
|
||||||
inline_start: margin_inline_start,
|
inline_start: margin_inline_start,
|
||||||
|
@ -661,15 +542,11 @@ fn layout_in_flow_replaced_block_level<'a>(
|
||||||
block_start: computed_margin.block_start.auto_is(Length::zero),
|
block_start: computed_margin.block_start.auto_is(Length::zero),
|
||||||
block_end: computed_margin.block_end.auto_is(Length::zero),
|
block_end: computed_margin.block_end.auto_is(Length::zero),
|
||||||
};
|
};
|
||||||
let size = Vec2 {
|
|
||||||
block: block_size,
|
|
||||||
inline: inline_size,
|
|
||||||
};
|
|
||||||
let fragments = replaced.make_fragments(style, size.clone());
|
let fragments = replaced.make_fragments(style, size.clone());
|
||||||
let relative_adjustement = relative_adjustement(
|
let relative_adjustement = relative_adjustement(
|
||||||
style,
|
style,
|
||||||
inline_size,
|
size.inline,
|
||||||
LengthOrAuto::LengthPercentage(block_size),
|
LengthOrAuto::LengthPercentage(size.block),
|
||||||
);
|
);
|
||||||
let content_rect = Rect {
|
let content_rect = Rect {
|
||||||
start_corner: Vec2 {
|
start_corner: Vec2 {
|
||||||
|
@ -703,45 +580,3 @@ fn solve_inline_margins_for_in_flow_block_level(
|
||||||
(LengthOrAuto::LengthPercentage(start), _) => (start, inline_margins - start),
|
(LengthOrAuto::LengthPercentage(start), _) => (start, inline_margins - start),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clamp_between_extremums(size: Length, min_size: Length, max_size: Option<Length>) -> Length {
|
|
||||||
clamp_below_max(size, max_size).max(min_size)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clamp_below_max(size: Length, max_size: Option<Length>) -> Length {
|
|
||||||
max_size.map_or(size, |max_size| size.min(max_size))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn percent_resolved_box_size(
|
|
||||||
box_size: Vec2<LengthPercentageOrAuto>,
|
|
||||||
containing_block: &ContainingBlock,
|
|
||||||
) -> Vec2<LengthOrAuto> {
|
|
||||||
Vec2 {
|
|
||||||
inline: box_size
|
|
||||||
.inline
|
|
||||||
.percentage_relative_to(containing_block.inline_size),
|
|
||||||
block: box_size
|
|
||||||
.block
|
|
||||||
.maybe_percentage_relative_to(containing_block.block_size.non_auto()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn percent_resolved_max_box_size(
|
|
||||||
max_box_size: Vec2<MaxSize<LengthPercentage>>,
|
|
||||||
containing_block: &ContainingBlock,
|
|
||||||
) -> Vec2<Option<Length>> {
|
|
||||||
Vec2 {
|
|
||||||
inline: match max_box_size.inline {
|
|
||||||
MaxSize::LengthPercentage(max_inline_size) => {
|
|
||||||
Some(max_inline_size.percentage_relative_to(containing_block.inline_size))
|
|
||||||
},
|
|
||||||
MaxSize::None => None,
|
|
||||||
},
|
|
||||||
block: match max_box_size.block {
|
|
||||||
MaxSize::LengthPercentage(max_block_size) => {
|
|
||||||
max_block_size.maybe_percentage_relative_to(containing_block.block_size.non_auto())
|
|
||||||
},
|
|
||||||
MaxSize::None => None,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -16,12 +16,12 @@ use crate::positioned::AbsolutelyPositionedBox;
|
||||||
use crate::replaced::ReplacedContent;
|
use crate::replaced::ReplacedContent;
|
||||||
use crate::sizing::ContentSizesRequest;
|
use crate::sizing::ContentSizesRequest;
|
||||||
use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside};
|
use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside};
|
||||||
use crate::{ContainingBlock, DefiniteContainingBlock};
|
use crate::DefiniteContainingBlock;
|
||||||
use rayon::iter::{IntoParallelRefIterator, ParallelExtend, ParallelIterator};
|
use rayon::iter::{IntoParallelRefIterator, ParallelExtend, ParallelIterator};
|
||||||
use script_layout_interface::wrapper_traits::LayoutNode;
|
use script_layout_interface::wrapper_traits::LayoutNode;
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use style::properties::ComputedValues;
|
use style::properties::ComputedValues;
|
||||||
use style::values::computed::{Length, LengthOrAuto};
|
use style::values::computed::Length;
|
||||||
use style::Zero;
|
use style::Zero;
|
||||||
use style_traits::CSSPixel;
|
use style_traits::CSSPixel;
|
||||||
|
|
||||||
|
@ -99,31 +99,25 @@ impl BoxTreeRoot {
|
||||||
viewport: geom::Size<CSSPixel>,
|
viewport: geom::Size<CSSPixel>,
|
||||||
) -> FragmentTreeRoot {
|
) -> FragmentTreeRoot {
|
||||||
let style = ComputedValues::initial_values();
|
let style = ComputedValues::initial_values();
|
||||||
let initial_containing_block_size = Vec2 {
|
let initial_containing_block = DefiniteContainingBlock {
|
||||||
|
size: Vec2 {
|
||||||
inline: Length::new(viewport.width),
|
inline: Length::new(viewport.width),
|
||||||
block: Length::new(viewport.height),
|
block: Length::new(viewport.height),
|
||||||
};
|
},
|
||||||
|
|
||||||
let initial_containing_block = ContainingBlock {
|
|
||||||
inline_size: initial_containing_block_size.inline,
|
|
||||||
block_size: LengthOrAuto::LengthPercentage(initial_containing_block_size.block),
|
|
||||||
// FIXME: use the document’s mode:
|
// FIXME: use the document’s mode:
|
||||||
// https://drafts.csswg.org/css-writing-modes/#principal-flow
|
// https://drafts.csswg.org/css-writing-modes/#principal-flow
|
||||||
style,
|
style,
|
||||||
};
|
};
|
||||||
|
|
||||||
let dummy_tree_rank = 0;
|
let dummy_tree_rank = 0;
|
||||||
let mut absolutely_positioned_fragments = vec![];
|
let mut absolutely_positioned_fragments = vec![];
|
||||||
let mut independent_layout = self.0.layout(
|
let mut independent_layout = self.0.layout(
|
||||||
layout_context,
|
layout_context,
|
||||||
&initial_containing_block,
|
&(&initial_containing_block).into(),
|
||||||
dummy_tree_rank,
|
dummy_tree_rank,
|
||||||
&mut absolutely_positioned_fragments,
|
&mut absolutely_positioned_fragments,
|
||||||
);
|
);
|
||||||
|
|
||||||
let initial_containing_block = DefiniteContainingBlock {
|
|
||||||
size: initial_containing_block_size,
|
|
||||||
style,
|
|
||||||
};
|
|
||||||
independent_layout.fragments.par_extend(
|
independent_layout.fragments.par_extend(
|
||||||
absolutely_positioned_fragments
|
absolutely_positioned_fragments
|
||||||
.par_iter()
|
.par_iter()
|
||||||
|
|
|
@ -29,6 +29,8 @@ pub(crate) struct IndependentFormattingContext {
|
||||||
|
|
||||||
pub(crate) struct IndependentLayout {
|
pub(crate) struct IndependentLayout {
|
||||||
pub fragments: Vec<Fragment>,
|
pub fragments: Vec<Fragment>,
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css2/visudet.html#root-height
|
||||||
pub content_block_size: Length,
|
pub content_block_size: Length,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,31 +59,30 @@ impl IndependentFormattingContext {
|
||||||
contents: Contents<impl NodeExt<'dom>>,
|
contents: Contents<impl NodeExt<'dom>>,
|
||||||
content_sizes: ContentSizesRequest,
|
content_sizes: ContentSizesRequest,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
use self::IndependentFormattingContextContents as Contents;
|
match contents.try_into() {
|
||||||
let (contents, content_sizes) = match contents.try_into() {
|
|
||||||
Ok(non_replaced) => match display_inside {
|
Ok(non_replaced) => match display_inside {
|
||||||
DisplayInside::Flow | DisplayInside::FlowRoot => {
|
DisplayInside::Flow | DisplayInside::FlowRoot => {
|
||||||
let (bfc, box_content_sizes) = BlockFormattingContext::construct(
|
let (bfc, content_sizes) = BlockFormattingContext::construct(
|
||||||
context,
|
context,
|
||||||
&style,
|
&style,
|
||||||
non_replaced,
|
non_replaced,
|
||||||
content_sizes,
|
content_sizes,
|
||||||
);
|
);
|
||||||
(Contents::Flow(bfc), box_content_sizes)
|
Self {
|
||||||
|
style,
|
||||||
|
content_sizes,
|
||||||
|
contents: IndependentFormattingContextContents::Flow(bfc),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Err(replaced) => {
|
Err(replaced) => {
|
||||||
// The `content_sizes` field is not used by layout code:
|
let content_sizes = content_sizes.compute(|| replaced.inline_content_sizes(&style));
|
||||||
(
|
|
||||||
Contents::Replaced(replaced),
|
|
||||||
BoxContentSizes::NoneWereRequested,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
};
|
|
||||||
Self {
|
Self {
|
||||||
style,
|
style,
|
||||||
contents,
|
|
||||||
content_sizes,
|
content_sizes,
|
||||||
|
contents: IndependentFormattingContextContents::Replaced(replaced),
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,13 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use crate::ContainingBlock;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::{Add, AddAssign, Sub};
|
use std::ops::{Add, AddAssign, Sub};
|
||||||
use style::logical_geometry::{BlockFlowDirection, InlineBaseDirection};
|
use style::logical_geometry::{BlockFlowDirection, InlineBaseDirection};
|
||||||
use style::logical_geometry::{PhysicalCorner, WritingMode};
|
use style::logical_geometry::{PhysicalCorner, WritingMode};
|
||||||
use style::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
|
use style::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
|
||||||
|
use style::values::generics::length::MaxSize;
|
||||||
use style::Zero;
|
use style::Zero;
|
||||||
use style_traits::CSSPixel;
|
use style_traits::CSSPixel;
|
||||||
|
|
||||||
|
@ -151,6 +153,39 @@ impl flow_relative::Vec2<LengthOrAuto> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl flow_relative::Vec2<LengthPercentageOrAuto> {
|
||||||
|
pub fn percentages_relative_to(
|
||||||
|
&self,
|
||||||
|
containing_block: &ContainingBlock,
|
||||||
|
) -> flow_relative::Vec2<LengthOrAuto> {
|
||||||
|
flow_relative::Vec2 {
|
||||||
|
inline: self
|
||||||
|
.inline
|
||||||
|
.percentage_relative_to(containing_block.inline_size),
|
||||||
|
block: self
|
||||||
|
.block
|
||||||
|
.maybe_percentage_relative_to(containing_block.block_size.non_auto()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl flow_relative::Vec2<MaxSize<LengthPercentage>> {
|
||||||
|
pub fn percentages_relative_to(
|
||||||
|
&self,
|
||||||
|
containing_block: &ContainingBlock,
|
||||||
|
) -> flow_relative::Vec2<Option<Length>> {
|
||||||
|
flow_relative::Vec2 {
|
||||||
|
inline: self
|
||||||
|
.inline
|
||||||
|
.to_option()
|
||||||
|
.map(|lp| lp.percentage_relative_to(containing_block.inline_size)),
|
||||||
|
block: self.block.to_option().and_then(|olp| {
|
||||||
|
olp.maybe_percentage_relative_to(containing_block.block_size.non_auto())
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl flow_relative::Rect<Length> {
|
impl flow_relative::Rect<Length> {
|
||||||
pub fn zero() -> Self {
|
pub fn zero() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
|
@ -44,6 +44,16 @@ struct DefiniteContainingBlock<'a> {
|
||||||
style: &'a ComputedValues,
|
style: &'a ComputedValues,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'_ DefiniteContainingBlock<'a>> for ContainingBlock<'a> {
|
||||||
|
fn from(definite: &DefiniteContainingBlock<'a>) -> Self {
|
||||||
|
ContainingBlock {
|
||||||
|
inline_size: definite.size.inline,
|
||||||
|
block_size: LengthOrAuto::LengthPercentage(definite.size.block),
|
||||||
|
style: definite.style,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css2/visuren.html#relative-positioning
|
/// https://drafts.csswg.org/css2/visuren.html#relative-positioning
|
||||||
fn relative_adjustement(
|
fn relative_adjustement(
|
||||||
style: &ComputedValues,
|
style: &ComputedValues,
|
||||||
|
|
|
@ -30,19 +30,24 @@ pub(crate) struct AbsolutelyPositionedFragment<'box_> {
|
||||||
/// static positions when going up the tree.
|
/// static positions when going up the tree.
|
||||||
pub(crate) tree_rank: usize,
|
pub(crate) tree_rank: usize,
|
||||||
|
|
||||||
pub(crate) inline_start: AbsoluteBoxOffsets<LengthPercentage>,
|
box_offsets: Vec2<AbsoluteBoxOffsets>,
|
||||||
inline_size: LengthPercentageOrAuto,
|
|
||||||
|
|
||||||
pub(crate) block_start: AbsoluteBoxOffsets<LengthPercentage>,
|
|
||||||
block_size: LengthPercentageOrAuto,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub(crate) enum AbsoluteBoxOffsets<NonStatic> {
|
pub(crate) enum AbsoluteBoxOffsets {
|
||||||
StaticStart { start: Length },
|
StaticStart {
|
||||||
Start { start: NonStatic },
|
start: Length,
|
||||||
End { end: NonStatic },
|
},
|
||||||
Both { start: NonStatic, end: NonStatic },
|
Start {
|
||||||
|
start: LengthPercentage,
|
||||||
|
},
|
||||||
|
End {
|
||||||
|
end: LengthPercentage,
|
||||||
|
},
|
||||||
|
Both {
|
||||||
|
start: LengthPercentage,
|
||||||
|
end: LengthPercentage,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AbsolutelyPositionedBox {
|
impl AbsolutelyPositionedBox {
|
||||||
|
@ -55,7 +60,7 @@ impl AbsolutelyPositionedBox {
|
||||||
// "Shrink-to-fit" in https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width
|
// "Shrink-to-fit" in https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width
|
||||||
let content_sizes = ContentSizesRequest::inline_if(
|
let content_sizes = ContentSizesRequest::inline_if(
|
||||||
// If inline-size is non-auto, that value is used without shrink-to-fit
|
// If inline-size is non-auto, that value is used without shrink-to-fit
|
||||||
style.inline_size_is_auto() &&
|
!style.inline_size_is_length() &&
|
||||||
// If it is, then the only case where shrink-to-fit is *not* used is
|
// If it is, then the only case where shrink-to-fit is *not* used is
|
||||||
// if both offsets are non-auto, leaving inline-size as the only variable
|
// if both offsets are non-auto, leaving inline-size as the only variable
|
||||||
// in the constraint equation.
|
// in the constraint equation.
|
||||||
|
@ -77,18 +82,11 @@ impl AbsolutelyPositionedBox {
|
||||||
initial_start_corner: Vec2<Length>,
|
initial_start_corner: Vec2<Length>,
|
||||||
tree_rank: usize,
|
tree_rank: usize,
|
||||||
) -> AbsolutelyPositionedFragment {
|
) -> AbsolutelyPositionedFragment {
|
||||||
let style = &self.contents.style;
|
|
||||||
let box_offsets = style.box_offsets();
|
|
||||||
let box_size = style.box_size();
|
|
||||||
|
|
||||||
let inline_size = box_size.inline;
|
|
||||||
let block_size = box_size.block;
|
|
||||||
|
|
||||||
fn absolute_box_offsets(
|
fn absolute_box_offsets(
|
||||||
initial_static_start: Length,
|
initial_static_start: Length,
|
||||||
start: LengthPercentageOrAuto,
|
start: LengthPercentageOrAuto,
|
||||||
end: LengthPercentageOrAuto,
|
end: LengthPercentageOrAuto,
|
||||||
) -> AbsoluteBoxOffsets<LengthPercentage> {
|
) -> AbsoluteBoxOffsets {
|
||||||
match (start.non_auto(), end.non_auto()) {
|
match (start.non_auto(), end.non_auto()) {
|
||||||
(None, None) => AbsoluteBoxOffsets::StaticStart {
|
(None, None) => AbsoluteBoxOffsets::StaticStart {
|
||||||
start: initial_static_start,
|
start: initial_static_start,
|
||||||
|
@ -99,24 +97,22 @@ impl AbsolutelyPositionedBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let inline_start = absolute_box_offsets(
|
let box_offsets = self.contents.style.box_offsets();
|
||||||
initial_start_corner.inline,
|
|
||||||
box_offsets.inline_start,
|
|
||||||
box_offsets.inline_end,
|
|
||||||
);
|
|
||||||
let block_start = absolute_box_offsets(
|
|
||||||
initial_start_corner.block,
|
|
||||||
box_offsets.block_start,
|
|
||||||
box_offsets.block_end,
|
|
||||||
);
|
|
||||||
|
|
||||||
AbsolutelyPositionedFragment {
|
AbsolutelyPositionedFragment {
|
||||||
absolutely_positioned_box: self,
|
absolutely_positioned_box: self,
|
||||||
tree_rank,
|
tree_rank,
|
||||||
inline_start,
|
box_offsets: Vec2 {
|
||||||
inline_size,
|
inline: absolute_box_offsets(
|
||||||
block_start,
|
initial_start_corner.inline,
|
||||||
block_size,
|
box_offsets.inline_start,
|
||||||
|
box_offsets.inline_end,
|
||||||
|
),
|
||||||
|
block: absolute_box_offsets(
|
||||||
|
initial_start_corner.block,
|
||||||
|
box_offsets.block_start,
|
||||||
|
box_offsets.block_end,
|
||||||
|
),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,168 +158,90 @@ impl<'a> AbsolutelyPositionedFragment<'a> {
|
||||||
let cbis = containing_block.size.inline;
|
let cbis = containing_block.size.inline;
|
||||||
let cbbs = containing_block.size.block;
|
let cbbs = containing_block.size.block;
|
||||||
|
|
||||||
|
let size;
|
||||||
|
let replaced_used_size;
|
||||||
|
match self.absolutely_positioned_box.contents.as_replaced() {
|
||||||
|
Ok(replaced) => {
|
||||||
|
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
|
||||||
|
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-height
|
||||||
|
let u = replaced.used_size_as_if_inline_element(&containing_block.into(), style);
|
||||||
|
size = Vec2 {
|
||||||
|
inline: LengthOrAuto::LengthPercentage(u.inline),
|
||||||
|
block: LengthOrAuto::LengthPercentage(u.block),
|
||||||
|
};
|
||||||
|
replaced_used_size = Some(u);
|
||||||
|
},
|
||||||
|
Err(_non_replaced) => {
|
||||||
|
let box_size = style.box_size();
|
||||||
|
size = Vec2 {
|
||||||
|
inline: box_size.inline.percentage_relative_to(cbis),
|
||||||
|
block: box_size.block.percentage_relative_to(cbbs),
|
||||||
|
};
|
||||||
|
replaced_used_size = None;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
let padding = style.padding().percentages_relative_to(cbis);
|
let padding = style.padding().percentages_relative_to(cbis);
|
||||||
let border = style.border_width();
|
let border = style.border_width();
|
||||||
let computed_margin = style.margin().percentages_relative_to(cbis);
|
let computed_margin = style.margin().percentages_relative_to(cbis);
|
||||||
let pb = &padding + &border;
|
let pb = &padding + &border;
|
||||||
|
|
||||||
enum Anchor {
|
let inline_axis = solve_axis(
|
||||||
Start(Length),
|
|
||||||
End(Length),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn solve_axis(
|
|
||||||
containing_size: Length,
|
|
||||||
padding_border_sum: Length,
|
|
||||||
computed_margin_start: LengthOrAuto,
|
|
||||||
computed_margin_end: LengthOrAuto,
|
|
||||||
solve_margins: impl FnOnce(Length) -> (Length, Length),
|
|
||||||
box_offsets: AbsoluteBoxOffsets<LengthPercentage>,
|
|
||||||
size: LengthPercentageOrAuto,
|
|
||||||
) -> (Anchor, LengthOrAuto, Length, Length) {
|
|
||||||
let size = size.percentage_relative_to(containing_size);
|
|
||||||
match box_offsets {
|
|
||||||
AbsoluteBoxOffsets::StaticStart { start } => (
|
|
||||||
Anchor::Start(start),
|
|
||||||
size,
|
|
||||||
computed_margin_start.auto_is(Length::zero),
|
|
||||||
computed_margin_end.auto_is(Length::zero),
|
|
||||||
),
|
|
||||||
AbsoluteBoxOffsets::Start { start } => (
|
|
||||||
Anchor::Start(start.percentage_relative_to(containing_size)),
|
|
||||||
size,
|
|
||||||
computed_margin_start.auto_is(Length::zero),
|
|
||||||
computed_margin_end.auto_is(Length::zero),
|
|
||||||
),
|
|
||||||
AbsoluteBoxOffsets::End { end } => (
|
|
||||||
Anchor::End(end.percentage_relative_to(containing_size)),
|
|
||||||
size,
|
|
||||||
computed_margin_start.auto_is(Length::zero),
|
|
||||||
computed_margin_end.auto_is(Length::zero),
|
|
||||||
),
|
|
||||||
AbsoluteBoxOffsets::Both { start, end } => {
|
|
||||||
let start = start.percentage_relative_to(containing_size);
|
|
||||||
let end = end.percentage_relative_to(containing_size);
|
|
||||||
|
|
||||||
let mut margin_start = computed_margin_start.auto_is(Length::zero);
|
|
||||||
let mut margin_end = computed_margin_end.auto_is(Length::zero);
|
|
||||||
|
|
||||||
let size = if let LengthOrAuto::LengthPercentage(size) = size {
|
|
||||||
let margins = containing_size - start - end - padding_border_sum - size;
|
|
||||||
match (computed_margin_start, computed_margin_end) {
|
|
||||||
(LengthOrAuto::Auto, LengthOrAuto::Auto) => {
|
|
||||||
let (s, e) = solve_margins(margins);
|
|
||||||
margin_start = s;
|
|
||||||
margin_end = e;
|
|
||||||
},
|
|
||||||
(LengthOrAuto::Auto, LengthOrAuto::LengthPercentage(end)) => {
|
|
||||||
margin_start = margins - end;
|
|
||||||
},
|
|
||||||
(LengthOrAuto::LengthPercentage(start), LengthOrAuto::Auto) => {
|
|
||||||
margin_end = margins - start;
|
|
||||||
},
|
|
||||||
(
|
|
||||||
LengthOrAuto::LengthPercentage(_),
|
|
||||||
LengthOrAuto::LengthPercentage(_),
|
|
||||||
) => {},
|
|
||||||
}
|
|
||||||
size
|
|
||||||
} else {
|
|
||||||
// FIXME(nox): What happens if that is negative?
|
|
||||||
containing_size -
|
|
||||||
start -
|
|
||||||
end -
|
|
||||||
padding_border_sum -
|
|
||||||
margin_start -
|
|
||||||
margin_end
|
|
||||||
};
|
|
||||||
(
|
|
||||||
Anchor::Start(start),
|
|
||||||
LengthOrAuto::LengthPercentage(size),
|
|
||||||
margin_start,
|
|
||||||
margin_end,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let (inline_anchor, inline_size, margin_inline_start, margin_inline_end) = solve_axis(
|
|
||||||
cbis,
|
cbis,
|
||||||
pb.inline_sum(),
|
pb.inline_sum(),
|
||||||
computed_margin.inline_start,
|
computed_margin.inline_start,
|
||||||
computed_margin.inline_end,
|
computed_margin.inline_end,
|
||||||
|margins| {
|
/* avoid_negative_margin_start */ true,
|
||||||
if margins.px() >= 0. {
|
self.box_offsets.inline,
|
||||||
(margins / 2., margins / 2.)
|
size.inline,
|
||||||
} else {
|
|
||||||
(Length::zero(), margins)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
self.inline_start,
|
|
||||||
self.inline_size,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let (block_anchor, block_size, margin_block_start, margin_block_end) = solve_axis(
|
let block_axis = solve_axis(
|
||||||
cbis,
|
cbis,
|
||||||
pb.block_sum(),
|
pb.block_sum(),
|
||||||
computed_margin.block_start,
|
computed_margin.block_start,
|
||||||
computed_margin.block_end,
|
computed_margin.block_end,
|
||||||
|margins| (margins / 2., margins / 2.),
|
/* avoid_negative_margin_start */ false,
|
||||||
self.block_start,
|
self.box_offsets.block,
|
||||||
self.block_size,
|
size.block,
|
||||||
);
|
);
|
||||||
|
|
||||||
let margin = Sides {
|
let margin = Sides {
|
||||||
inline_start: margin_inline_start,
|
inline_start: inline_axis.margin_start,
|
||||||
inline_end: margin_inline_end,
|
inline_end: inline_axis.margin_end,
|
||||||
block_start: margin_block_start,
|
block_start: block_axis.margin_start,
|
||||||
block_end: margin_block_end,
|
block_end: block_axis.margin_end,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inline_size = inline_size.auto_is(|| {
|
let mut absolutely_positioned_fragments = Vec::new();
|
||||||
let available_size = match inline_anchor {
|
let (size, mut fragments) = match self.absolutely_positioned_box.contents.as_replaced() {
|
||||||
Anchor::Start(start) => cbis - start - pb.inline_sum() - margin.inline_sum(),
|
Ok(replaced) => {
|
||||||
|
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
|
||||||
|
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-height
|
||||||
|
let style = &self.absolutely_positioned_box.contents.style;
|
||||||
|
let size = replaced_used_size.unwrap();
|
||||||
|
let fragments = replaced.make_fragments(style, size.clone());
|
||||||
|
(size, fragments)
|
||||||
|
},
|
||||||
|
Err(non_replaced) => {
|
||||||
|
// https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width
|
||||||
|
// https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height
|
||||||
|
let inline_size = inline_axis.size.auto_is(|| {
|
||||||
|
let available_size = match inline_axis.anchor {
|
||||||
|
Anchor::Start(start) => {
|
||||||
|
cbis - start - pb.inline_sum() - margin.inline_sum()
|
||||||
|
},
|
||||||
Anchor::End(end) => cbis - end - pb.inline_sum() - margin.inline_sum(),
|
Anchor::End(end) => cbis - end - pb.inline_sum() - margin.inline_sum(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if self
|
|
||||||
.absolutely_positioned_box
|
|
||||||
.contents
|
|
||||||
.as_replaced()
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
// FIXME: implement https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
|
|
||||||
available_size
|
|
||||||
} else {
|
|
||||||
self.absolutely_positioned_box
|
self.absolutely_positioned_box
|
||||||
.contents
|
.contents
|
||||||
.content_sizes
|
.content_sizes
|
||||||
.shrink_to_fit(available_size)
|
.shrink_to_fit(available_size)
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut absolutely_positioned_fragments = Vec::new();
|
|
||||||
let mut independent_layout = match self.absolutely_positioned_box.contents.as_replaced() {
|
|
||||||
Ok(replaced) => {
|
|
||||||
// FIXME: implement https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
|
|
||||||
// and https://drafts.csswg.org/css2/visudet.html#abs-replaced-height
|
|
||||||
let block_size = block_size.auto_is(Length::zero);
|
|
||||||
let fragments = replaced.make_fragments(
|
|
||||||
&self.absolutely_positioned_box.contents.style,
|
|
||||||
Vec2 {
|
|
||||||
inline: inline_size,
|
|
||||||
block: block_size,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
crate::formatting_contexts::IndependentLayout {
|
|
||||||
fragments,
|
|
||||||
content_block_size: block_size,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(non_replaced) => {
|
|
||||||
let containing_block_for_children = ContainingBlock {
|
let containing_block_for_children = ContainingBlock {
|
||||||
inline_size,
|
inline_size,
|
||||||
block_size,
|
block_size: block_axis.size,
|
||||||
style,
|
style,
|
||||||
};
|
};
|
||||||
// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
|
// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
|
||||||
|
@ -333,24 +251,30 @@ impl<'a> AbsolutelyPositionedFragment<'a> {
|
||||||
"Mixed writing modes are not supported yet"
|
"Mixed writing modes are not supported yet"
|
||||||
);
|
);
|
||||||
let dummy_tree_rank = 0;
|
let dummy_tree_rank = 0;
|
||||||
non_replaced.layout(
|
let independent_layout = non_replaced.layout(
|
||||||
layout_context,
|
layout_context,
|
||||||
&containing_block_for_children,
|
&containing_block_for_children,
|
||||||
dummy_tree_rank,
|
dummy_tree_rank,
|
||||||
&mut absolutely_positioned_fragments,
|
&mut absolutely_positioned_fragments,
|
||||||
)
|
);
|
||||||
|
|
||||||
|
let size = Vec2 {
|
||||||
|
inline: inline_size,
|
||||||
|
block: block_axis
|
||||||
|
.size
|
||||||
|
.auto_is(|| independent_layout.content_block_size),
|
||||||
|
};
|
||||||
|
(size, independent_layout.fragments)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let inline_start = match inline_anchor {
|
let inline_start = match inline_axis.anchor {
|
||||||
Anchor::Start(start) => start + pb.inline_start + margin.inline_start,
|
Anchor::Start(start) => start + pb.inline_start + margin.inline_start,
|
||||||
Anchor::End(end) => cbbs - end - pb.inline_end - margin.inline_end - inline_size,
|
Anchor::End(end) => cbbs - end - pb.inline_end - margin.inline_end - size.inline,
|
||||||
};
|
};
|
||||||
|
let block_start = match block_axis.anchor {
|
||||||
let block_size = block_size.auto_is(|| independent_layout.content_block_size);
|
|
||||||
let block_start = match block_anchor {
|
|
||||||
Anchor::Start(start) => start + pb.block_start + margin.block_start,
|
Anchor::Start(start) => start + pb.block_start + margin.block_start,
|
||||||
Anchor::End(end) => cbbs - end - pb.block_end - margin.block_end - block_size,
|
Anchor::End(end) => cbbs - end - pb.block_end - margin.block_end - size.block,
|
||||||
};
|
};
|
||||||
|
|
||||||
let content_rect = Rect {
|
let content_rect = Rect {
|
||||||
|
@ -358,16 +282,13 @@ impl<'a> AbsolutelyPositionedFragment<'a> {
|
||||||
inline: inline_start,
|
inline: inline_start,
|
||||||
block: block_start,
|
block: block_start,
|
||||||
},
|
},
|
||||||
size: Vec2 {
|
size,
|
||||||
inline: inline_size,
|
|
||||||
block: block_size,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
AbsolutelyPositionedFragment::in_positioned_containing_block(
|
AbsolutelyPositionedFragment::in_positioned_containing_block(
|
||||||
layout_context,
|
layout_context,
|
||||||
&absolutely_positioned_fragments,
|
&absolutely_positioned_fragments,
|
||||||
&mut independent_layout.fragments,
|
&mut fragments,
|
||||||
&content_rect.size,
|
&content_rect.size,
|
||||||
&padding,
|
&padding,
|
||||||
style,
|
style,
|
||||||
|
@ -375,7 +296,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> {
|
||||||
|
|
||||||
Fragment::Box(BoxFragment {
|
Fragment::Box(BoxFragment {
|
||||||
style: style.clone(),
|
style: style.clone(),
|
||||||
children: independent_layout.fragments,
|
children: fragments,
|
||||||
content_rect,
|
content_rect,
|
||||||
padding,
|
padding,
|
||||||
border,
|
border,
|
||||||
|
@ -385,6 +306,110 @@ impl<'a> AbsolutelyPositionedFragment<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Anchor {
|
||||||
|
Start(Length),
|
||||||
|
End(Length),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AxisResult {
|
||||||
|
anchor: Anchor,
|
||||||
|
size: LengthOrAuto,
|
||||||
|
margin_start: Length,
|
||||||
|
margin_end: Length,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This unifies some of the parts in common in:
|
||||||
|
///
|
||||||
|
/// * https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width
|
||||||
|
/// * https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height
|
||||||
|
///
|
||||||
|
/// … and:
|
||||||
|
///
|
||||||
|
/// * https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
|
||||||
|
/// * https://drafts.csswg.org/css2/visudet.html#abs-replaced-height
|
||||||
|
///
|
||||||
|
/// In the replaced case, `size` is never `Auto`.
|
||||||
|
fn solve_axis(
|
||||||
|
containing_size: Length,
|
||||||
|
padding_border_sum: Length,
|
||||||
|
computed_margin_start: LengthOrAuto,
|
||||||
|
computed_margin_end: LengthOrAuto,
|
||||||
|
avoid_negative_margin_start: bool,
|
||||||
|
box_offsets: AbsoluteBoxOffsets,
|
||||||
|
size: LengthOrAuto,
|
||||||
|
) -> AxisResult {
|
||||||
|
match box_offsets {
|
||||||
|
AbsoluteBoxOffsets::StaticStart { start } => AxisResult {
|
||||||
|
anchor: Anchor::Start(start),
|
||||||
|
size,
|
||||||
|
margin_start: computed_margin_start.auto_is(Length::zero),
|
||||||
|
margin_end: computed_margin_end.auto_is(Length::zero),
|
||||||
|
},
|
||||||
|
AbsoluteBoxOffsets::Start { start } => AxisResult {
|
||||||
|
anchor: Anchor::Start(start.percentage_relative_to(containing_size)),
|
||||||
|
size,
|
||||||
|
margin_start: computed_margin_start.auto_is(Length::zero),
|
||||||
|
margin_end: computed_margin_end.auto_is(Length::zero),
|
||||||
|
},
|
||||||
|
AbsoluteBoxOffsets::End { end } => AxisResult {
|
||||||
|
anchor: Anchor::End(end.percentage_relative_to(containing_size)),
|
||||||
|
size,
|
||||||
|
margin_start: computed_margin_start.auto_is(Length::zero),
|
||||||
|
margin_end: computed_margin_end.auto_is(Length::zero),
|
||||||
|
},
|
||||||
|
AbsoluteBoxOffsets::Both { start, end } => {
|
||||||
|
let start = start.percentage_relative_to(containing_size);
|
||||||
|
let end = end.percentage_relative_to(containing_size);
|
||||||
|
|
||||||
|
let margin_start;
|
||||||
|
let margin_end;
|
||||||
|
let used_size;
|
||||||
|
if let LengthOrAuto::LengthPercentage(s) = size {
|
||||||
|
used_size = s;
|
||||||
|
let margins = containing_size - start - end - padding_border_sum - s;
|
||||||
|
match (computed_margin_start, computed_margin_end) {
|
||||||
|
(LengthOrAuto::Auto, LengthOrAuto::Auto) => {
|
||||||
|
if avoid_negative_margin_start && margins < Length::zero() {
|
||||||
|
margin_start = Length::zero();
|
||||||
|
margin_end = margins;
|
||||||
|
} else {
|
||||||
|
margin_start = margins / 2.;
|
||||||
|
margin_end = margins / 2.;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(LengthOrAuto::Auto, LengthOrAuto::LengthPercentage(end)) => {
|
||||||
|
margin_start = margins - end;
|
||||||
|
margin_end = end;
|
||||||
|
},
|
||||||
|
(LengthOrAuto::LengthPercentage(start), LengthOrAuto::Auto) => {
|
||||||
|
margin_start = start;
|
||||||
|
margin_end = margins - start;
|
||||||
|
},
|
||||||
|
(
|
||||||
|
LengthOrAuto::LengthPercentage(start),
|
||||||
|
LengthOrAuto::LengthPercentage(end),
|
||||||
|
) => {
|
||||||
|
margin_start = start;
|
||||||
|
margin_end = end;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
margin_start = computed_margin_start.auto_is(Length::zero);
|
||||||
|
margin_end = computed_margin_end.auto_is(Length::zero);
|
||||||
|
// FIXME(nox): What happens if that is negative?
|
||||||
|
used_size =
|
||||||
|
containing_size - start - end - padding_border_sum - margin_start - margin_end
|
||||||
|
};
|
||||||
|
AxisResult {
|
||||||
|
anchor: Anchor::Start(start),
|
||||||
|
size: LengthOrAuto::LengthPercentage(used_size),
|
||||||
|
margin_start,
|
||||||
|
margin_end,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn adjust_static_positions(
|
pub(crate) fn adjust_static_positions(
|
||||||
absolutely_positioned_fragments: &mut [AbsolutelyPositionedFragment],
|
absolutely_positioned_fragments: &mut [AbsolutelyPositionedFragment],
|
||||||
child_fragments: &mut [Fragment],
|
child_fragments: &mut [Fragment],
|
||||||
|
@ -399,11 +424,11 @@ pub(crate) fn adjust_static_positions(
|
||||||
|
|
||||||
abspos_fragment.tree_rank = tree_rank_in_parent;
|
abspos_fragment.tree_rank = tree_rank_in_parent;
|
||||||
|
|
||||||
if let AbsoluteBoxOffsets::StaticStart { start } = &mut abspos_fragment.inline_start {
|
if let AbsoluteBoxOffsets::StaticStart { start } = &mut abspos_fragment.box_offsets.inline {
|
||||||
*start += child_fragment_rect.start_corner.inline;
|
*start += child_fragment_rect.start_corner.inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let AbsoluteBoxOffsets::StaticStart { start } = &mut abspos_fragment.block_start {
|
if let AbsoluteBoxOffsets::StaticStart { start } = &mut abspos_fragment.box_offsets.block {
|
||||||
*start += child_fragment_rect.start_corner.block;
|
*start += child_fragment_rect.start_corner.block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,17 +4,37 @@
|
||||||
|
|
||||||
use crate::dom_traversal::NodeExt;
|
use crate::dom_traversal::NodeExt;
|
||||||
use crate::fragments::{Fragment, ImageFragment};
|
use crate::fragments::{Fragment, ImageFragment};
|
||||||
use crate::geom::{flow_relative, physical};
|
use crate::geom::flow_relative::{Rect, Vec2};
|
||||||
|
use crate::geom::physical;
|
||||||
|
use crate::sizing::ContentSizes;
|
||||||
|
use crate::style_ext::ComputedValuesExt;
|
||||||
|
use crate::ContainingBlock;
|
||||||
use net_traits::image::base::Image;
|
use net_traits::image::base::Image;
|
||||||
use servo_arc::Arc as ServoArc;
|
use servo_arc::Arc as ServoArc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use style::properties::ComputedValues;
|
use style::properties::ComputedValues;
|
||||||
use style::values::computed::Length;
|
use style::values::computed::{Length, LengthOrAuto};
|
||||||
|
use style::values::CSSFloat;
|
||||||
|
use style::Zero;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct ReplacedContent {
|
pub(crate) struct ReplacedContent {
|
||||||
pub kind: ReplacedContentKind,
|
pub kind: ReplacedContentKind,
|
||||||
pub intrinsic_size: physical::Vec2<Length>,
|
|
||||||
|
/// * Raster images always have an instrinsic width and height, with 1 image pixel = 1px.
|
||||||
|
/// The intrinsic ratio should be based on dividing those.
|
||||||
|
/// See https://github.com/w3c/csswg-drafts/issues/4572 for the case where either is zero.
|
||||||
|
/// PNG specifically disallows this but I (SimonSapin) am not sure about other formats.
|
||||||
|
///
|
||||||
|
/// * Form controls have both intrinsic width and height **but no intrinsic ratio**.
|
||||||
|
/// See https://github.com/w3c/csswg-drafts/issues/1044 and
|
||||||
|
/// https://drafts.csswg.org/css-images/#intrinsic-dimensions “In general, […]”
|
||||||
|
///
|
||||||
|
/// * For SVG, see https://svgwg.org/svg2-draft/coords.html#SizingSVGInCSS
|
||||||
|
/// and again https://github.com/w3c/csswg-drafts/issues/4572.
|
||||||
|
intrinsic_width: Option<Length>,
|
||||||
|
intrinsic_height: Option<Length>,
|
||||||
|
intrinsic_ratio: Option<CSSFloat>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -24,19 +44,65 @@ pub(crate) enum ReplacedContentKind {
|
||||||
|
|
||||||
impl ReplacedContent {
|
impl ReplacedContent {
|
||||||
pub fn for_element<'dom>(element: impl NodeExt<'dom>) -> Option<Self> {
|
pub fn for_element<'dom>(element: impl NodeExt<'dom>) -> Option<Self> {
|
||||||
if let Some((image, intrinsic_size)) = element.as_image() {
|
if let Some((image, intrinsic_size_in_dots)) = element.as_image() {
|
||||||
|
// FIXME: should 'image-resolution' (when implemented) be used *instead* of
|
||||||
|
// `script::dom::htmlimageelement::ImageRequest::current_pixel_density`?
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-images-4/#the-image-resolution
|
||||||
|
let dppx = 1.0;
|
||||||
|
|
||||||
|
let width = (intrinsic_size_in_dots.x as CSSFloat) / dppx;
|
||||||
|
let height = (intrinsic_size_in_dots.y as CSSFloat) / dppx;
|
||||||
return Some(Self {
|
return Some(Self {
|
||||||
kind: ReplacedContentKind::Image(image),
|
kind: ReplacedContentKind::Image(image),
|
||||||
intrinsic_size,
|
intrinsic_width: Some(Length::new(width)),
|
||||||
|
intrinsic_height: Some(Length::new(height)),
|
||||||
|
// FIXME https://github.com/w3c/csswg-drafts/issues/4572
|
||||||
|
intrinsic_ratio: Some(width / height),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn flow_relative_intrinsic_size(&self, style: &ComputedValues) -> Vec2<Option<Length>> {
|
||||||
|
let intrinsic_size = physical::Vec2 {
|
||||||
|
x: self.intrinsic_width,
|
||||||
|
y: self.intrinsic_height,
|
||||||
|
};
|
||||||
|
intrinsic_size.size_to_flow_relative(style.writing_mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inline_size_over_block_size_intrinsic_ratio(
|
||||||
|
&self,
|
||||||
|
style: &ComputedValues,
|
||||||
|
) -> Option<CSSFloat> {
|
||||||
|
self.intrinsic_ratio.map(|width_over_height| {
|
||||||
|
if style.writing_mode.is_vertical() {
|
||||||
|
1. / width_over_height
|
||||||
|
} else {
|
||||||
|
width_over_height
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inline_content_sizes(&self, style: &ComputedValues) -> ContentSizes {
|
||||||
|
// FIXME: min/max-content of replaced elements is not defined in
|
||||||
|
// https://dbaron.org/css/intrinsic/
|
||||||
|
// This seems sensible?
|
||||||
|
let inline = self
|
||||||
|
.flow_relative_intrinsic_size(style)
|
||||||
|
.inline
|
||||||
|
.unwrap_or(Length::zero());
|
||||||
|
ContentSizes {
|
||||||
|
min_content: inline,
|
||||||
|
max_content: inline,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn make_fragments<'a>(
|
pub fn make_fragments<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
style: &ServoArc<ComputedValues>,
|
style: &ServoArc<ComputedValues>,
|
||||||
size: flow_relative::Vec2<Length>,
|
size: Vec2<Length>,
|
||||||
) -> Vec<Fragment> {
|
) -> Vec<Fragment> {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
ReplacedContentKind::Image(image) => image
|
ReplacedContentKind::Image(image) => image
|
||||||
|
@ -45,8 +111,8 @@ impl ReplacedContent {
|
||||||
.map(|image_key| {
|
.map(|image_key| {
|
||||||
Fragment::Image(ImageFragment {
|
Fragment::Image(ImageFragment {
|
||||||
style: style.clone(),
|
style: style.clone(),
|
||||||
rect: flow_relative::Rect {
|
rect: Rect {
|
||||||
start_corner: flow_relative::Vec2::zero(),
|
start_corner: Vec2::zero(),
|
||||||
size,
|
size,
|
||||||
},
|
},
|
||||||
image_key,
|
image_key,
|
||||||
|
@ -56,4 +122,214 @@ impl ReplacedContent {
|
||||||
.collect(),
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css2/visudet.html#inline-replaced-width
|
||||||
|
/// https://drafts.csswg.org/css2/visudet.html#inline-replaced-height
|
||||||
|
///
|
||||||
|
/// Also used in other cases, for example
|
||||||
|
/// https://drafts.csswg.org/css2/visudet.html#block-replaced-width
|
||||||
|
pub fn used_size_as_if_inline_element(
|
||||||
|
&self,
|
||||||
|
containing_block: &ContainingBlock,
|
||||||
|
style: &ComputedValues,
|
||||||
|
) -> Vec2<Length> {
|
||||||
|
let mode = style.writing_mode;
|
||||||
|
let intrinsic_size = self.flow_relative_intrinsic_size(style);
|
||||||
|
let intrinsic_ratio = self.inline_size_over_block_size_intrinsic_ratio(style);
|
||||||
|
|
||||||
|
let box_size = style.box_size().percentages_relative_to(containing_block);
|
||||||
|
let min_box_size = style
|
||||||
|
.min_box_size()
|
||||||
|
.percentages_relative_to(containing_block)
|
||||||
|
.auto_is(Length::zero);
|
||||||
|
let max_box_size = style
|
||||||
|
.max_box_size()
|
||||||
|
.percentages_relative_to(containing_block);
|
||||||
|
|
||||||
|
let default_object_size = || {
|
||||||
|
// FIXME:
|
||||||
|
// “If 300px is too wide to fit the device, UAs should use the width of
|
||||||
|
// the largest rectangle that has a 2:1 ratio and fits the device instead.”
|
||||||
|
// “height of the largest rectangle that has a 2:1 ratio, has a height not greater
|
||||||
|
// than 150px, and has a width not greater than the device width.”
|
||||||
|
physical::Vec2 {
|
||||||
|
x: Length::new(300.),
|
||||||
|
y: Length::new(150.),
|
||||||
|
}
|
||||||
|
.size_to_flow_relative(mode)
|
||||||
|
};
|
||||||
|
let clamp = |inline_size: Length, block_size: Length| Vec2 {
|
||||||
|
inline: inline_size.clamp_between_extremums(min_box_size.inline, max_box_size.inline),
|
||||||
|
block: block_size.clamp_between_extremums(min_box_size.block, max_box_size.block),
|
||||||
|
};
|
||||||
|
// https://drafts.csswg.org/css2/visudet.html#min-max-widths
|
||||||
|
// https://drafts.csswg.org/css2/visudet.html#min-max-heights
|
||||||
|
match (box_size.inline, box_size.block) {
|
||||||
|
(LengthOrAuto::LengthPercentage(inline), LengthOrAuto::LengthPercentage(block)) => {
|
||||||
|
clamp(inline, block)
|
||||||
|
},
|
||||||
|
(LengthOrAuto::LengthPercentage(inline), LengthOrAuto::Auto) => {
|
||||||
|
let block = if let Some(i_over_b) = intrinsic_ratio {
|
||||||
|
inline / i_over_b
|
||||||
|
} else if let Some(block) = intrinsic_size.block {
|
||||||
|
block
|
||||||
|
} else {
|
||||||
|
default_object_size().block
|
||||||
|
};
|
||||||
|
clamp(inline, block)
|
||||||
|
},
|
||||||
|
(LengthOrAuto::Auto, LengthOrAuto::LengthPercentage(block)) => {
|
||||||
|
let inline = if let Some(i_over_b) = intrinsic_ratio {
|
||||||
|
block * i_over_b
|
||||||
|
} else if let Some(inline) = intrinsic_size.inline {
|
||||||
|
inline
|
||||||
|
} else {
|
||||||
|
default_object_size().inline
|
||||||
|
};
|
||||||
|
clamp(inline, block)
|
||||||
|
},
|
||||||
|
(LengthOrAuto::Auto, LengthOrAuto::Auto) => {
|
||||||
|
let inline_size =
|
||||||
|
match (intrinsic_size.inline, intrinsic_size.block, intrinsic_ratio) {
|
||||||
|
(Some(inline), _, _) => inline,
|
||||||
|
(None, Some(block), Some(i_over_b)) => {
|
||||||
|
// “used height” in CSS 2 is always gonna be the intrinsic one,
|
||||||
|
// since it is available.
|
||||||
|
block * i_over_b
|
||||||
|
},
|
||||||
|
// FIXME
|
||||||
|
//
|
||||||
|
// “If 'height' and 'width' both have computed values of 'auto'
|
||||||
|
// and the element has an intrinsic ratio but no intrinsic height or width,
|
||||||
|
// […]”
|
||||||
|
//
|
||||||
|
// In this `match` expression this would be an additional arm here:
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// (Vec2 { inline: None, block: None }, Some(_)) => {…}
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// “[…] then the used value of 'width' is undefined in CSS 2.
|
||||||
|
// However, it is suggested that, if the containing block's width
|
||||||
|
// does not itself depend on the replaced element's width,
|
||||||
|
// then the used value of 'width' is calculated from the constraint
|
||||||
|
// equation used for block-level, non-replaced elements in normal flow.”
|
||||||
|
_ => default_object_size().inline,
|
||||||
|
};
|
||||||
|
let block_size = if let Some(block) = intrinsic_size.block {
|
||||||
|
block
|
||||||
|
} else if let Some(i_over_b) = intrinsic_ratio {
|
||||||
|
// “used width” in CSS 2 is what we just computed above
|
||||||
|
inline_size / i_over_b
|
||||||
|
} else {
|
||||||
|
default_object_size().block
|
||||||
|
};
|
||||||
|
|
||||||
|
let i_over_b = if let Some(i_over_b) = intrinsic_ratio {
|
||||||
|
i_over_b
|
||||||
|
} else {
|
||||||
|
return clamp(inline_size, block_size);
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css2/visudet.html#min-max-widths
|
||||||
|
// “However, for replaced elements with an intrinsic ratio and both
|
||||||
|
// 'width' and 'height' specified as 'auto', the algorithm is as follows”
|
||||||
|
enum Violation {
|
||||||
|
None,
|
||||||
|
Below(Length),
|
||||||
|
Above(Length),
|
||||||
|
}
|
||||||
|
let violation = |size, min_size, mut max_size: Option<Length>| {
|
||||||
|
if let Some(max) = max_size.as_mut() {
|
||||||
|
max.max_assign(min_size);
|
||||||
|
}
|
||||||
|
if size < min_size {
|
||||||
|
return Violation::Below(min_size);
|
||||||
|
}
|
||||||
|
match max_size {
|
||||||
|
Some(max_size) if size > max_size => Violation::Above(max_size),
|
||||||
|
_ => Violation::None,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match (
|
||||||
|
violation(inline_size, min_box_size.inline, max_box_size.inline),
|
||||||
|
violation(block_size, min_box_size.block, max_box_size.block),
|
||||||
|
) {
|
||||||
|
// Row 1.
|
||||||
|
(Violation::None, Violation::None) => Vec2 {
|
||||||
|
inline: inline_size,
|
||||||
|
block: block_size,
|
||||||
|
},
|
||||||
|
// Row 2.
|
||||||
|
(Violation::Above(max_inline_size), Violation::None) => Vec2 {
|
||||||
|
inline: max_inline_size,
|
||||||
|
block: (max_inline_size / i_over_b).max(min_box_size.block),
|
||||||
|
},
|
||||||
|
// Row 3.
|
||||||
|
(Violation::Below(min_inline_size), Violation::None) => Vec2 {
|
||||||
|
inline: min_inline_size,
|
||||||
|
block: (min_inline_size / i_over_b).clamp_below_max(max_box_size.block),
|
||||||
|
},
|
||||||
|
// Row 4.
|
||||||
|
(Violation::None, Violation::Above(max_block_size)) => Vec2 {
|
||||||
|
inline: (max_block_size * i_over_b).max(min_box_size.inline),
|
||||||
|
block: max_block_size,
|
||||||
|
},
|
||||||
|
// Row 5.
|
||||||
|
(Violation::None, Violation::Below(min_block_size)) => Vec2 {
|
||||||
|
inline: (min_block_size * i_over_b).clamp_below_max(max_box_size.inline),
|
||||||
|
block: min_block_size,
|
||||||
|
},
|
||||||
|
// Rows 6-7.
|
||||||
|
(Violation::Above(max_inline_size), Violation::Above(max_block_size)) => {
|
||||||
|
if max_inline_size.px() / inline_size.px() <=
|
||||||
|
max_block_size.px() / block_size.px()
|
||||||
|
{
|
||||||
|
// Row 6.
|
||||||
|
Vec2 {
|
||||||
|
inline: max_inline_size,
|
||||||
|
block: (max_inline_size / i_over_b).max(min_box_size.block),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Row 7.
|
||||||
|
Vec2 {
|
||||||
|
inline: (max_block_size * i_over_b).max(min_box_size.inline),
|
||||||
|
block: max_block_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Rows 8-9.
|
||||||
|
(Violation::Below(min_inline_size), Violation::Below(min_block_size)) => {
|
||||||
|
if min_inline_size.px() / inline_size.px() <=
|
||||||
|
min_block_size.px() / block_size.px()
|
||||||
|
{
|
||||||
|
// Row 8.
|
||||||
|
Vec2 {
|
||||||
|
inline: (min_block_size * i_over_b)
|
||||||
|
.clamp_below_max(max_box_size.inline),
|
||||||
|
block: min_block_size,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Row 9.
|
||||||
|
Vec2 {
|
||||||
|
inline: min_inline_size,
|
||||||
|
block: (min_inline_size / i_over_b)
|
||||||
|
.clamp_below_max(max_box_size.block),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Row 10.
|
||||||
|
(Violation::Below(min_inline_size), Violation::Above(max_block_size)) => Vec2 {
|
||||||
|
inline: min_inline_size,
|
||||||
|
block: max_block_size,
|
||||||
|
},
|
||||||
|
// Row 11.
|
||||||
|
(Violation::Above(max_inline_size), Violation::Below(min_block_size)) => Vec2 {
|
||||||
|
inline: max_inline_size,
|
||||||
|
block: min_block_size,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,17 +107,37 @@ impl BoxContentSizes {
|
||||||
&self,
|
&self,
|
||||||
style: &ComputedValues,
|
style: &ComputedValues,
|
||||||
) -> (ContentSizes, Percentage) {
|
) -> (ContentSizes, Percentage) {
|
||||||
// FIXME: account for 'min-width', 'max-width', 'box-sizing'
|
// FIXME: account for 'box-sizing'
|
||||||
|
|
||||||
let inline_size = style.box_size().inline;
|
let inline_size = style.box_size().inline;
|
||||||
|
let min_inline_size = style
|
||||||
|
.min_box_size()
|
||||||
|
.inline
|
||||||
|
.percentage_relative_to(Length::zero())
|
||||||
|
.auto_is(Length::zero);
|
||||||
|
let max_inline_size = style
|
||||||
|
.max_box_size()
|
||||||
|
.inline
|
||||||
|
.to_option()
|
||||||
|
.and_then(|lp| lp.as_length());
|
||||||
|
let clamp = |l: Length| l.clamp_between_extremums(min_inline_size, max_inline_size);
|
||||||
|
|
||||||
// Percentages for 'width' are treated as 'auto'
|
// Percentages for 'width' are treated as 'auto'
|
||||||
let inline_size = inline_size.map(|lp| lp.as_length());
|
let inline_size = inline_size.map(|lp| lp.as_length());
|
||||||
// The (inner) min/max-content are only used for 'auto'
|
// The (inner) min/max-content are only used for 'auto'
|
||||||
let mut outer = match inline_size.non_auto().flatten() {
|
let mut outer = match inline_size.non_auto().flatten() {
|
||||||
None => self.expect_inline().clone(),
|
None => {
|
||||||
Some(length) => ContentSizes {
|
let inner = self.expect_inline().clone();
|
||||||
|
ContentSizes {
|
||||||
|
min_content: clamp(inner.min_content),
|
||||||
|
max_content: clamp(inner.max_content),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(length) => {
|
||||||
|
let length = clamp(length);
|
||||||
|
ContentSizes {
|
||||||
min_content: length,
|
min_content: length,
|
||||||
max_content: length,
|
max_content: length,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ pub(crate) enum DisplayInside {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait ComputedValuesExt {
|
pub(crate) trait ComputedValuesExt {
|
||||||
fn inline_size_is_auto(&self) -> bool;
|
fn inline_size_is_length(&self) -> bool;
|
||||||
fn inline_box_offsets_are_both_non_auto(&self) -> bool;
|
fn inline_box_offsets_are_both_non_auto(&self) -> bool;
|
||||||
fn box_offsets(&self) -> flow_relative::Sides<LengthPercentageOrAuto>;
|
fn box_offsets(&self) -> flow_relative::Sides<LengthPercentageOrAuto>;
|
||||||
fn box_size(&self) -> flow_relative::Vec2<LengthPercentageOrAuto>;
|
fn box_size(&self) -> flow_relative::Vec2<LengthPercentageOrAuto>;
|
||||||
|
@ -52,14 +52,14 @@ pub(crate) trait ComputedValuesExt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ComputedValuesExt for ComputedValues {
|
impl ComputedValuesExt for ComputedValues {
|
||||||
fn inline_size_is_auto(&self) -> bool {
|
fn inline_size_is_length(&self) -> bool {
|
||||||
let position = self.get_position();
|
let position = self.get_position();
|
||||||
let size = if self.writing_mode.is_horizontal() {
|
let size = if self.writing_mode.is_horizontal() {
|
||||||
position.width
|
position.width
|
||||||
} else {
|
} else {
|
||||||
position.height
|
position.height
|
||||||
};
|
};
|
||||||
size == Size::Auto
|
matches!(size, Size::LengthPercentage(lp) if lp.0.as_length().is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inline_box_offsets_are_both_non_auto(&self) -> bool {
|
fn inline_box_offsets_are_both_non_auto(&self) -> bool {
|
||||||
|
|
|
@ -657,14 +657,14 @@ impl CSSPixelLength {
|
||||||
|
|
||||||
/// Return the containing pixel value.
|
/// Return the containing pixel value.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn px(&self) -> CSSFloat {
|
pub fn px(self) -> CSSFloat {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the length with app_unit i32 type.
|
/// Return the length with app_unit i32 type.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_i32_au(&self) -> i32 {
|
pub fn to_i32_au(self) -> i32 {
|
||||||
Au::from(*self).0
|
Au::from(self).0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the absolute value of this length.
|
/// Return the absolute value of this length.
|
||||||
|
@ -692,9 +692,29 @@ impl CSSPixelLength {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets `self` to the maximum between `self` and `other`.
|
/// Sets `self` to the maximum between `self` and `other`.
|
||||||
|
#[inline]
|
||||||
pub fn max_assign(&mut self, other: Self) {
|
pub fn max_assign(&mut self, other: Self) {
|
||||||
*self = self.max(other);
|
*self = self.max(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clamp the value to a lower bound and an optional upper bound.
|
||||||
|
///
|
||||||
|
/// Can be used for example with `min-width` and `max-width`.
|
||||||
|
#[inline]
|
||||||
|
pub fn clamp_between_extremums(self, min_size: Self, max_size: Option<Self>) -> Self {
|
||||||
|
self.clamp_below_max(max_size).max(min_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clamp the value to an optional upper bound.
|
||||||
|
///
|
||||||
|
/// Can be used for example with `max-width`.
|
||||||
|
#[inline]
|
||||||
|
pub fn clamp_below_max(self, max_size: Option<Self>) -> Self {
|
||||||
|
match max_size {
|
||||||
|
None => self,
|
||||||
|
Some(max_size) => self.min(max_size),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Zero for CSSPixelLength {
|
impl Zero for CSSPixelLength {
|
||||||
|
|
|
@ -207,6 +207,15 @@ impl<LengthPercentage> MaxSize<LengthPercentage> {
|
||||||
pub fn none() -> Self {
|
pub fn none() -> Self {
|
||||||
MaxSize::None
|
MaxSize::None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert
|
||||||
|
#[cfg(not(feature = "gecko"))]
|
||||||
|
pub fn to_option(self) -> Option<LengthPercentage> {
|
||||||
|
match self {
|
||||||
|
Self::LengthPercentage(lp) => Some(lp),
|
||||||
|
Self::None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A generic `<length>` | `<number>` value for the `-moz-tab-size` property.
|
/// A generic `<length>` | `<number>` value for the `-moz-tab-size` property.
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
[containing-block-008.xht]
|
||||||
|
expected: FAIL
|
|
@ -0,0 +1,2 @@
|
||||||
|
[containing-block-009.xht]
|
||||||
|
expected: FAIL
|
|
@ -0,0 +1,2 @@
|
||||||
|
[containing-block-010.xht]
|
||||||
|
expected: FAIL
|
|
@ -0,0 +1,2 @@
|
||||||
|
[containing-block-027.xht]
|
||||||
|
expected: FAIL
|
|
@ -0,0 +1,2 @@
|
||||||
|
[containing-block-028.xht]
|
||||||
|
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
||||||
[display-change-001.xht]
|
|
||||||
expected: FAIL
|
|
Loading…
Add table
Add a link
Reference in a new issue