mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
Auto merge of #25033 - servo:intrinsic, r=nox
Add support for inline-block and for computing min/max-content
This commit is contained in:
commit
e70397d90a
19 changed files with 823 additions and 272 deletions
|
@ -2,6 +2,7 @@
|
||||||
* 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::context::LayoutContext;
|
||||||
use crate::element_data::{LayoutBox, LayoutDataForElement};
|
use crate::element_data::{LayoutBox, LayoutDataForElement};
|
||||||
use crate::geom::physical::Vec2;
|
use crate::geom::physical::Vec2;
|
||||||
use crate::replaced::ReplacedContent;
|
use crate::replaced::ReplacedContent;
|
||||||
|
@ -13,7 +14,6 @@ use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode};
|
||||||
use servo_arc::Arc as ServoArc;
|
use servo_arc::Arc as ServoArc;
|
||||||
use std::marker::PhantomData as marker;
|
use std::marker::PhantomData as marker;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use style::context::SharedStyleContext;
|
|
||||||
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;
|
||||||
|
@ -66,7 +66,7 @@ where
|
||||||
|
|
||||||
fn traverse_children_of<'dom, Node>(
|
fn traverse_children_of<'dom, Node>(
|
||||||
parent_element: Node,
|
parent_element: Node,
|
||||||
context: &SharedStyleContext,
|
context: &LayoutContext,
|
||||||
handler: &mut impl TraversalHandler<'dom, Node>,
|
handler: &mut impl TraversalHandler<'dom, Node>,
|
||||||
) where
|
) where
|
||||||
Node: NodeExt<'dom>,
|
Node: NodeExt<'dom>,
|
||||||
|
@ -88,7 +88,7 @@ fn traverse_children_of<'dom, Node>(
|
||||||
|
|
||||||
fn traverse_element<'dom, Node>(
|
fn traverse_element<'dom, Node>(
|
||||||
element: Node,
|
element: Node,
|
||||||
context: &SharedStyleContext,
|
context: &LayoutContext,
|
||||||
handler: &mut impl TraversalHandler<'dom, Node>,
|
handler: &mut impl TraversalHandler<'dom, Node>,
|
||||||
) where
|
) where
|
||||||
Node: NodeExt<'dom>,
|
Node: NodeExt<'dom>,
|
||||||
|
@ -121,7 +121,7 @@ fn traverse_element<'dom, Node>(
|
||||||
fn traverse_pseudo_element<'dom, Node>(
|
fn traverse_pseudo_element<'dom, Node>(
|
||||||
which: WhichPseudoElement,
|
which: WhichPseudoElement,
|
||||||
element: Node,
|
element: Node,
|
||||||
context: &SharedStyleContext,
|
context: &LayoutContext,
|
||||||
handler: &mut impl TraversalHandler<'dom, Node>,
|
handler: &mut impl TraversalHandler<'dom, Node>,
|
||||||
) where
|
) where
|
||||||
Node: NodeExt<'dom>,
|
Node: NodeExt<'dom>,
|
||||||
|
@ -146,7 +146,7 @@ fn traverse_pseudo_element<'dom, Node>(
|
||||||
|
|
||||||
fn traverse_pseudo_element_contents<'dom, Node>(
|
fn traverse_pseudo_element_contents<'dom, Node>(
|
||||||
pseudo_element_style: &ServoArc<ComputedValues>,
|
pseudo_element_style: &ServoArc<ComputedValues>,
|
||||||
context: &SharedStyleContext,
|
context: &LayoutContext,
|
||||||
handler: &mut impl TraversalHandler<'dom, Node>,
|
handler: &mut impl TraversalHandler<'dom, Node>,
|
||||||
items: Vec<PseudoElementContentItem>,
|
items: Vec<PseudoElementContentItem>,
|
||||||
) where
|
) where
|
||||||
|
@ -159,9 +159,10 @@ fn traverse_pseudo_element_contents<'dom, Node>(
|
||||||
PseudoElementContentItem::Replaced(contents) => {
|
PseudoElementContentItem::Replaced(contents) => {
|
||||||
let item_style = anonymous_style.get_or_insert_with(|| {
|
let item_style = anonymous_style.get_or_insert_with(|| {
|
||||||
context
|
context
|
||||||
|
.shared_context()
|
||||||
.stylist
|
.stylist
|
||||||
.style_for_anonymous::<Node::ConcreteElement>(
|
.style_for_anonymous::<Node::ConcreteElement>(
|
||||||
&context.guards,
|
&context.shared_context().guards,
|
||||||
&PseudoElement::ServoText,
|
&PseudoElement::ServoText,
|
||||||
&pseudo_element_style,
|
&pseudo_element_style,
|
||||||
)
|
)
|
||||||
|
@ -187,6 +188,16 @@ fn traverse_pseudo_element_contents<'dom, Node>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<Node> Contents<Node> {
|
||||||
|
/// Returns true iff the `try_from` impl below would return `Err(_)`
|
||||||
|
pub fn is_replaced(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Contents::OfElement(_) | Contents::OfPseudoElement(_) => false,
|
||||||
|
Contents::Replaced(_) => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<Node> std::convert::TryFrom<Contents<Node>> for NonReplacedContents<Node> {
|
impl<Node> std::convert::TryFrom<Contents<Node>> for NonReplacedContents<Node> {
|
||||||
type Error = ReplacedContent;
|
type Error = ReplacedContent;
|
||||||
|
|
||||||
|
@ -215,7 +226,7 @@ where
|
||||||
pub(crate) fn traverse(
|
pub(crate) fn traverse(
|
||||||
self,
|
self,
|
||||||
inherited_style: &ServoArc<ComputedValues>,
|
inherited_style: &ServoArc<ComputedValues>,
|
||||||
context: &SharedStyleContext,
|
context: &LayoutContext,
|
||||||
handler: &mut impl TraversalHandler<'dom, Node>,
|
handler: &mut impl TraversalHandler<'dom, Node>,
|
||||||
) {
|
) {
|
||||||
match self {
|
match self {
|
||||||
|
@ -230,7 +241,7 @@ where
|
||||||
fn pseudo_element_style<'dom, Node>(
|
fn pseudo_element_style<'dom, Node>(
|
||||||
_which: WhichPseudoElement,
|
_which: WhichPseudoElement,
|
||||||
_element: Node,
|
_element: Node,
|
||||||
_context: &SharedStyleContext,
|
_context: &LayoutContext,
|
||||||
) -> Option<ServoArc<ComputedValues>>
|
) -> Option<ServoArc<ComputedValues>>
|
||||||
where
|
where
|
||||||
Node: NodeExt<'dom>,
|
Node: NodeExt<'dom>,
|
||||||
|
@ -243,7 +254,7 @@ where
|
||||||
fn generate_pseudo_element_content<'dom, Node>(
|
fn generate_pseudo_element_content<'dom, Node>(
|
||||||
_pseudo_element_style: &ComputedValues,
|
_pseudo_element_style: &ComputedValues,
|
||||||
_element: Node,
|
_element: Node,
|
||||||
_context: &SharedStyleContext,
|
_context: &LayoutContext,
|
||||||
) -> Vec<PseudoElementContentItem>
|
) -> Vec<PseudoElementContentItem>
|
||||||
where
|
where
|
||||||
Node: NodeExt<'dom>,
|
Node: NodeExt<'dom>,
|
||||||
|
@ -292,7 +303,7 @@ pub(crate) trait NodeExt<'dom>: 'dom + Copy + LayoutNode + Send + Sync {
|
||||||
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>;
|
||||||
fn style(self, context: &SharedStyleContext) -> ServoArc<ComputedValues>;
|
fn style(self, context: &LayoutContext) -> ServoArc<ComputedValues>;
|
||||||
|
|
||||||
fn layout_data_mut(&self) -> AtomicRefMut<LayoutDataForElement>;
|
fn layout_data_mut(&self) -> AtomicRefMut<LayoutDataForElement>;
|
||||||
fn element_box_slot(&self) -> BoxSlot<'dom>;
|
fn element_box_slot(&self) -> BoxSlot<'dom>;
|
||||||
|
@ -349,8 +360,8 @@ where
|
||||||
TNode::parent_node(&self)
|
TNode::parent_node(&self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn style(self, context: &SharedStyleContext) -> ServoArc<ComputedValues> {
|
fn style(self, context: &LayoutContext) -> ServoArc<ComputedValues> {
|
||||||
self.to_threadsafe().style(context)
|
self.to_threadsafe().style(context.shared_context())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_data_mut(&self) -> AtomicRefMut<LayoutDataForElement> {
|
fn layout_data_mut(&self) -> AtomicRefMut<LayoutDataForElement> {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* 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::context::LayoutContext;
|
||||||
use crate::dom_traversal::{BoxSlot, Contents, NodeExt, NonReplacedContents, TraversalHandler};
|
use crate::dom_traversal::{BoxSlot, Contents, NodeExt, NonReplacedContents, TraversalHandler};
|
||||||
use crate::element_data::LayoutBox;
|
use crate::element_data::LayoutBox;
|
||||||
use crate::flow::float::FloatBox;
|
use crate::flow::float::FloatBox;
|
||||||
|
@ -9,26 +10,31 @@ use crate::flow::inline::{InlineBox, InlineFormattingContext, InlineLevelBox, Te
|
||||||
use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox};
|
use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox};
|
||||||
use crate::formatting_contexts::IndependentFormattingContext;
|
use crate::formatting_contexts::IndependentFormattingContext;
|
||||||
use crate::positioned::AbsolutelyPositionedBox;
|
use crate::positioned::AbsolutelyPositionedBox;
|
||||||
use crate::style_ext::{DisplayGeneratingBox, DisplayInside, DisplayOutside};
|
use crate::sizing::{BoxContentSizes, ContentSizes, ContentSizesRequest};
|
||||||
|
use crate::style_ext::{ComputedValuesExt, DisplayGeneratingBox, DisplayInside, DisplayOutside};
|
||||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||||
use rayon_croissant::ParallelIteratorExt;
|
use rayon_croissant::ParallelIteratorExt;
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use std::convert::TryInto;
|
use std::convert::{TryFrom, TryInto};
|
||||||
use style::context::SharedStyleContext;
|
|
||||||
use style::properties::ComputedValues;
|
use style::properties::ComputedValues;
|
||||||
use style::selector_parser::PseudoElement;
|
use style::selector_parser::PseudoElement;
|
||||||
|
|
||||||
impl BlockFormattingContext {
|
impl BlockFormattingContext {
|
||||||
pub fn construct<'dom>(
|
pub fn construct<'dom>(
|
||||||
context: &SharedStyleContext<'_>,
|
context: &LayoutContext,
|
||||||
style: &Arc<ComputedValues>,
|
style: &Arc<ComputedValues>,
|
||||||
contents: NonReplacedContents<impl NodeExt<'dom>>,
|
contents: NonReplacedContents<impl NodeExt<'dom>>,
|
||||||
) -> Self {
|
content_sizes: ContentSizesRequest,
|
||||||
let (contents, contains_floats) = BlockContainer::construct(context, style, contents);
|
) -> (Self, BoxContentSizes) {
|
||||||
Self {
|
let (contents, contains_floats, inline_content_sizes) =
|
||||||
|
BlockContainer::construct(context, style, contents, content_sizes);
|
||||||
|
// FIXME: add contribution to `inline_content_sizes` of floats in this formatting context
|
||||||
|
// https://dbaron.org/css/intrinsic/#intrinsic
|
||||||
|
let bfc = Self {
|
||||||
contents,
|
contents,
|
||||||
contains_floats: contains_floats == ContainsFloats::Yes,
|
contains_floats: contains_floats == ContainsFloats::Yes,
|
||||||
}
|
};
|
||||||
|
(bfc, inline_content_sizes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +77,7 @@ enum IntermediateBlockContainer<Node> {
|
||||||
/// This builder starts from the first child of a given DOM node
|
/// This builder starts from the first child of a given DOM node
|
||||||
/// and does a preorder traversal of all of its inclusive siblings.
|
/// and does a preorder traversal of all of its inclusive siblings.
|
||||||
struct BlockContainerBuilder<'dom, 'style, Node> {
|
struct BlockContainerBuilder<'dom, 'style, Node> {
|
||||||
context: &'style SharedStyleContext<'style>,
|
context: &'style LayoutContext<'style>,
|
||||||
|
|
||||||
block_container_style: &'style Arc<ComputedValues>,
|
block_container_style: &'style Arc<ComputedValues>,
|
||||||
|
|
||||||
|
@ -123,19 +129,20 @@ struct BlockContainerBuilder<'dom, 'style, Node> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlockContainer {
|
impl BlockContainer {
|
||||||
pub fn construct<'dom, 'style>(
|
pub fn construct<'dom>(
|
||||||
context: &SharedStyleContext<'style>,
|
context: &LayoutContext,
|
||||||
block_container_style: &Arc<ComputedValues>,
|
block_container_style: &Arc<ComputedValues>,
|
||||||
contents: NonReplacedContents<impl NodeExt<'dom>>,
|
contents: NonReplacedContents<impl NodeExt<'dom>>,
|
||||||
) -> (BlockContainer, ContainsFloats) {
|
content_sizes: ContentSizesRequest,
|
||||||
|
) -> (BlockContainer, ContainsFloats, BoxContentSizes) {
|
||||||
let mut builder = BlockContainerBuilder {
|
let mut builder = BlockContainerBuilder {
|
||||||
context,
|
context,
|
||||||
block_container_style,
|
block_container_style,
|
||||||
block_level_boxes: Default::default(),
|
block_level_boxes: Vec::new(),
|
||||||
ongoing_inline_formatting_context: Default::default(),
|
ongoing_inline_formatting_context: InlineFormattingContext::default(),
|
||||||
ongoing_inline_boxes_stack: Default::default(),
|
ongoing_inline_boxes_stack: Vec::new(),
|
||||||
anonymous_style: Default::default(),
|
anonymous_style: None,
|
||||||
contains_floats: Default::default(),
|
contains_floats: ContainsFloats::No,
|
||||||
};
|
};
|
||||||
|
|
||||||
contents.traverse(block_container_style, context, &mut builder);
|
contents.traverse(block_container_style, context, &mut builder);
|
||||||
|
@ -148,32 +155,65 @@ impl BlockContainer {
|
||||||
.is_empty()
|
.is_empty()
|
||||||
{
|
{
|
||||||
if builder.block_level_boxes.is_empty() {
|
if builder.block_level_boxes.is_empty() {
|
||||||
|
let content_sizes = content_sizes.compute(|| {
|
||||||
|
builder
|
||||||
|
.ongoing_inline_formatting_context
|
||||||
|
.inline_content_sizes(context)
|
||||||
|
});
|
||||||
let container = BlockContainer::InlineFormattingContext(
|
let container = BlockContainer::InlineFormattingContext(
|
||||||
builder.ongoing_inline_formatting_context,
|
builder.ongoing_inline_formatting_context,
|
||||||
);
|
);
|
||||||
return (container, builder.contains_floats);
|
return (container, builder.contains_floats, content_sizes);
|
||||||
}
|
}
|
||||||
builder.end_ongoing_inline_formatting_context();
|
builder.end_ongoing_inline_formatting_context();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut contains_floats = builder.contains_floats;
|
type Intermediate<Node> = IntermediateBlockLevelBox<Node>;
|
||||||
let container = BlockContainer::BlockLevelBoxes(
|
struct Target {
|
||||||
builder
|
contains_floats: ContainsFloats,
|
||||||
.block_level_boxes
|
outer_content_sizes_of_children: ContentSizes,
|
||||||
.into_par_iter()
|
}
|
||||||
.mapfold_reduce_into(
|
impl Default for Target {
|
||||||
&mut contains_floats,
|
fn default() -> Self {
|
||||||
|contains_floats, (intermediate, box_slot): (IntermediateBlockLevelBox<_>, BoxSlot<'_>)| {
|
Self {
|
||||||
let (block_level_box, box_contains_floats) = intermediate.finish(context);
|
contains_floats: ContainsFloats::No,
|
||||||
*contains_floats |= box_contains_floats;
|
outer_content_sizes_of_children: ContentSizes::zero(),
|
||||||
box_slot.set(LayoutBox::BlockLevel(block_level_box.clone()));
|
}
|
||||||
block_level_box
|
}
|
||||||
},
|
}
|
||||||
|left, right| *left |= right,
|
let mut target = Target {
|
||||||
)
|
contains_floats: builder.contains_floats,
|
||||||
.collect(),
|
outer_content_sizes_of_children: ContentSizes::zero(),
|
||||||
|
};
|
||||||
|
let iter = builder.block_level_boxes.into_par_iter();
|
||||||
|
let iter = iter.mapfold_reduce_into(
|
||||||
|
&mut target,
|
||||||
|
|target, (intermediate, box_slot): (Intermediate<_>, BoxSlot<'_>)| {
|
||||||
|
let (block_level_box, box_contains_floats) = intermediate.finish(
|
||||||
|
context,
|
||||||
|
content_sizes
|
||||||
|
.if_requests_inline(|| &mut target.outer_content_sizes_of_children),
|
||||||
|
);
|
||||||
|
target.contains_floats |= box_contains_floats;
|
||||||
|
box_slot.set(LayoutBox::BlockLevel(block_level_box.clone()));
|
||||||
|
block_level_box
|
||||||
|
},
|
||||||
|
|left, right| {
|
||||||
|
left.contains_floats |= right.contains_floats;
|
||||||
|
if content_sizes.requests_inline() {
|
||||||
|
left.outer_content_sizes_of_children
|
||||||
|
.max_assign(&right.outer_content_sizes_of_children)
|
||||||
|
}
|
||||||
|
},
|
||||||
);
|
);
|
||||||
(container, contains_floats)
|
let container = BlockContainer::BlockLevelBoxes(iter.collect());
|
||||||
|
|
||||||
|
let Target {
|
||||||
|
contains_floats,
|
||||||
|
outer_content_sizes_of_children,
|
||||||
|
} = target;
|
||||||
|
let content_sizes = content_sizes.compute(|| outer_content_sizes_of_children);
|
||||||
|
(container, contains_floats, content_sizes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,38 +364,38 @@ where
|
||||||
display_inside: DisplayInside,
|
display_inside: DisplayInside,
|
||||||
contents: Contents<Node>,
|
contents: Contents<Node>,
|
||||||
) -> Arc<InlineLevelBox> {
|
) -> Arc<InlineLevelBox> {
|
||||||
let box_ = match contents.try_into() {
|
let box_ = if display_inside == DisplayInside::Flow && !contents.is_replaced() {
|
||||||
Err(replaced) => Arc::new(InlineLevelBox::Atomic(
|
// We found un inline box.
|
||||||
|
// Whatever happened before, all we need to do before recurring
|
||||||
|
// is to remember this ongoing inline level box.
|
||||||
|
self.ongoing_inline_boxes_stack.push(InlineBox {
|
||||||
|
style: style.clone(),
|
||||||
|
first_fragment: true,
|
||||||
|
last_fragment: false,
|
||||||
|
children: vec![],
|
||||||
|
});
|
||||||
|
|
||||||
|
// `unwrap` doesn’t panic here because `is_replaced` returned `false`.
|
||||||
|
NonReplacedContents::try_from(contents)
|
||||||
|
.unwrap()
|
||||||
|
.traverse(&style, self.context, self);
|
||||||
|
|
||||||
|
let mut inline_box = self
|
||||||
|
.ongoing_inline_boxes_stack
|
||||||
|
.pop()
|
||||||
|
.expect("no ongoing inline level box found");
|
||||||
|
inline_box.last_fragment = true;
|
||||||
|
Arc::new(InlineLevelBox::InlineBox(inline_box))
|
||||||
|
} else {
|
||||||
|
Arc::new(InlineLevelBox::Atomic(
|
||||||
IndependentFormattingContext::construct(
|
IndependentFormattingContext::construct(
|
||||||
self.context,
|
self.context,
|
||||||
style.clone(),
|
style.clone(),
|
||||||
display_inside,
|
display_inside,
|
||||||
<Contents<Node>>::Replaced(replaced),
|
contents,
|
||||||
|
ContentSizesRequest::inline_if(style.inline_size_is_auto()),
|
||||||
),
|
),
|
||||||
)),
|
))
|
||||||
Ok(non_replaced) => match display_inside {
|
|
||||||
DisplayInside::Flow |
|
|
||||||
// TODO: Properly implement display: inline-block.
|
|
||||||
DisplayInside::FlowRoot => {
|
|
||||||
// Whatever happened before, we just found an inline level element, so
|
|
||||||
// all we need to do is to remember this ongoing inline level box.
|
|
||||||
self.ongoing_inline_boxes_stack.push(InlineBox {
|
|
||||||
style: style.clone(),
|
|
||||||
first_fragment: true,
|
|
||||||
last_fragment: false,
|
|
||||||
children: vec![],
|
|
||||||
});
|
|
||||||
|
|
||||||
NonReplacedContents::traverse(non_replaced, &style, self.context, self);
|
|
||||||
|
|
||||||
let mut inline_box = self
|
|
||||||
.ongoing_inline_boxes_stack
|
|
||||||
.pop()
|
|
||||||
.expect("no ongoing inline level box found");
|
|
||||||
inline_box.last_fragment = true;
|
|
||||||
Arc::new(InlineLevelBox::InlineBox(inline_box))
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
self.current_inline_level_boxes().push(box_.clone());
|
self.current_inline_level_boxes().push(box_.clone());
|
||||||
box_
|
box_
|
||||||
|
@ -451,14 +491,7 @@ where
|
||||||
self.block_level_boxes.push((box_, box_slot));
|
self.block_level_boxes.push((box_, box_slot));
|
||||||
} else {
|
} else {
|
||||||
let box_ = Arc::new(InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(
|
let box_ = Arc::new(InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(
|
||||||
AbsolutelyPositionedBox {
|
AbsolutelyPositionedBox::construct(self.context, style, display_inside, contents),
|
||||||
contents: IndependentFormattingContext::construct(
|
|
||||||
self.context,
|
|
||||||
style,
|
|
||||||
display_inside,
|
|
||||||
contents,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
));
|
));
|
||||||
self.current_inline_level_boxes().push(box_.clone());
|
self.current_inline_level_boxes().push(box_.clone());
|
||||||
box_slot.set(LayoutBox::InlineLevel(box_))
|
box_slot.set(LayoutBox::InlineLevel(box_))
|
||||||
|
@ -482,14 +515,12 @@ where
|
||||||
};
|
};
|
||||||
self.block_level_boxes.push((box_, box_slot));
|
self.block_level_boxes.push((box_, box_slot));
|
||||||
} else {
|
} else {
|
||||||
let box_ = Arc::new(InlineLevelBox::OutOfFlowFloatBox(FloatBox {
|
let box_ = Arc::new(InlineLevelBox::OutOfFlowFloatBox(FloatBox::construct(
|
||||||
contents: IndependentFormattingContext::construct(
|
self.context,
|
||||||
self.context,
|
style,
|
||||||
style,
|
display_inside,
|
||||||
display_inside,
|
contents,
|
||||||
contents,
|
)));
|
||||||
),
|
|
||||||
}));
|
|
||||||
self.current_inline_level_boxes().push(box_.clone());
|
self.current_inline_level_boxes().push(box_.clone());
|
||||||
box_slot.set(LayoutBox::InlineLevel(box_))
|
box_slot.set(LayoutBox::InlineLevel(box_))
|
||||||
}
|
}
|
||||||
|
@ -509,9 +540,10 @@ where
|
||||||
let block_container_style = self.block_container_style;
|
let block_container_style = self.block_container_style;
|
||||||
let anonymous_style = self.anonymous_style.get_or_insert_with(|| {
|
let anonymous_style = self.anonymous_style.get_or_insert_with(|| {
|
||||||
context
|
context
|
||||||
|
.shared_context()
|
||||||
.stylist
|
.stylist
|
||||||
.style_for_anonymous::<Node::ConcreteElement>(
|
.style_for_anonymous::<Node::ConcreteElement>(
|
||||||
&context.guards,
|
&context.shared_context().guards,
|
||||||
&PseudoElement::ServoText,
|
&PseudoElement::ServoText,
|
||||||
&block_container_style,
|
&block_container_style,
|
||||||
)
|
)
|
||||||
|
@ -546,13 +578,24 @@ impl<'dom, Node> IntermediateBlockLevelBox<Node>
|
||||||
where
|
where
|
||||||
Node: NodeExt<'dom>,
|
Node: NodeExt<'dom>,
|
||||||
{
|
{
|
||||||
fn finish<'style>(
|
fn finish(
|
||||||
self,
|
self,
|
||||||
context: &SharedStyleContext<'style>,
|
context: &LayoutContext,
|
||||||
|
max_assign_in_flow_outer_content_sizes_to: Option<&mut ContentSizes>,
|
||||||
) -> (Arc<BlockLevelBox>, ContainsFloats) {
|
) -> (Arc<BlockLevelBox>, ContainsFloats) {
|
||||||
match self {
|
match self {
|
||||||
IntermediateBlockLevelBox::SameFormattingContextBlock { style, contents } => {
|
IntermediateBlockLevelBox::SameFormattingContextBlock { style, contents } => {
|
||||||
let (contents, contains_floats) = contents.finish(context, &style);
|
let (contents, contains_floats, box_content_sizes) = contents.finish(
|
||||||
|
context,
|
||||||
|
&style,
|
||||||
|
ContentSizesRequest::inline_if(
|
||||||
|
max_assign_in_flow_outer_content_sizes_to.is_some() &&
|
||||||
|
style.inline_size_is_auto(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if let Some(to) = max_assign_in_flow_outer_content_sizes_to {
|
||||||
|
to.max_assign(&box_content_sizes.outer_inline(&style))
|
||||||
|
}
|
||||||
let block_level_box =
|
let block_level_box =
|
||||||
Arc::new(BlockLevelBox::SameFormattingContextBlock { contents, style });
|
Arc::new(BlockLevelBox::SameFormattingContextBlock { contents, style });
|
||||||
(block_level_box, contains_floats)
|
(block_level_box, contains_floats)
|
||||||
|
@ -562,12 +605,20 @@ where
|
||||||
display_inside,
|
display_inside,
|
||||||
contents,
|
contents,
|
||||||
} => {
|
} => {
|
||||||
|
let content_sizes = ContentSizesRequest::inline_if(
|
||||||
|
max_assign_in_flow_outer_content_sizes_to.is_some() &&
|
||||||
|
style.inline_size_is_auto(),
|
||||||
|
);
|
||||||
let contents = IndependentFormattingContext::construct(
|
let contents = IndependentFormattingContext::construct(
|
||||||
context,
|
context,
|
||||||
style,
|
style,
|
||||||
display_inside,
|
display_inside,
|
||||||
contents,
|
contents,
|
||||||
|
content_sizes,
|
||||||
);
|
);
|
||||||
|
if let Some(to) = max_assign_in_flow_outer_content_sizes_to {
|
||||||
|
to.max_assign(&contents.content_sizes.outer_inline(&contents.style))
|
||||||
|
}
|
||||||
(
|
(
|
||||||
Arc::new(BlockLevelBox::Independent(contents)),
|
Arc::new(BlockLevelBox::Independent(contents)),
|
||||||
ContainsFloats::No,
|
ContainsFloats::No,
|
||||||
|
@ -579,14 +630,7 @@ where
|
||||||
contents,
|
contents,
|
||||||
} => {
|
} => {
|
||||||
let block_level_box = Arc::new(BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(
|
let block_level_box = Arc::new(BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(
|
||||||
AbsolutelyPositionedBox {
|
AbsolutelyPositionedBox::construct(context, style, display_inside, contents),
|
||||||
contents: IndependentFormattingContext::construct(
|
|
||||||
context,
|
|
||||||
style,
|
|
||||||
display_inside,
|
|
||||||
contents,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
));
|
));
|
||||||
(block_level_box, ContainsFloats::No)
|
(block_level_box, ContainsFloats::No)
|
||||||
},
|
},
|
||||||
|
@ -595,14 +639,9 @@ where
|
||||||
display_inside,
|
display_inside,
|
||||||
contents,
|
contents,
|
||||||
} => {
|
} => {
|
||||||
let contents = IndependentFormattingContext::construct(
|
let block_level_box = Arc::new(BlockLevelBox::OutOfFlowFloatBox(
|
||||||
context,
|
FloatBox::construct(context, style, display_inside, contents),
|
||||||
style,
|
));
|
||||||
display_inside,
|
|
||||||
contents,
|
|
||||||
);
|
|
||||||
let block_level_box =
|
|
||||||
Arc::new(BlockLevelBox::OutOfFlowFloatBox(FloatBox { contents }));
|
|
||||||
(block_level_box, ContainsFloats::Yes)
|
(block_level_box, ContainsFloats::Yes)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -613,22 +652,25 @@ impl<'dom, Node> IntermediateBlockContainer<Node>
|
||||||
where
|
where
|
||||||
Node: NodeExt<'dom>,
|
Node: NodeExt<'dom>,
|
||||||
{
|
{
|
||||||
fn finish<'style>(
|
fn finish(
|
||||||
self,
|
self,
|
||||||
context: &SharedStyleContext<'style>,
|
context: &LayoutContext,
|
||||||
style: &Arc<ComputedValues>,
|
style: &Arc<ComputedValues>,
|
||||||
) -> (BlockContainer, ContainsFloats) {
|
content_sizes: ContentSizesRequest,
|
||||||
|
) -> (BlockContainer, ContainsFloats, BoxContentSizes) {
|
||||||
match self {
|
match self {
|
||||||
IntermediateBlockContainer::Deferred { contents } => {
|
IntermediateBlockContainer::Deferred { contents } => {
|
||||||
BlockContainer::construct(context, style, contents)
|
BlockContainer::construct(context, style, contents, content_sizes)
|
||||||
},
|
},
|
||||||
IntermediateBlockContainer::InlineFormattingContext(ifc) => {
|
IntermediateBlockContainer::InlineFormattingContext(ifc) => {
|
||||||
|
let content_sizes = content_sizes.compute(|| ifc.inline_content_sizes(context));
|
||||||
// If that inline formatting context contained any float, those
|
// If that inline formatting context contained any float, those
|
||||||
// were already taken into account during the first phase of
|
// were already taken into account during the first phase of
|
||||||
// box construction.
|
// box construction.
|
||||||
(
|
(
|
||||||
BlockContainer::InlineFormattingContext(ifc),
|
BlockContainer::InlineFormattingContext(ifc),
|
||||||
ContainsFloats::No,
|
ContainsFloats::No,
|
||||||
|
content_sizes,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -648,9 +690,3 @@ impl std::ops::BitOrAssign for ContainsFloats {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ContainsFloats {
|
|
||||||
fn default() -> Self {
|
|
||||||
ContainsFloats::No
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,7 +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::context::LayoutContext;
|
||||||
|
use crate::dom_traversal::{Contents, NodeExt};
|
||||||
use crate::formatting_contexts::IndependentFormattingContext;
|
use crate::formatting_contexts::IndependentFormattingContext;
|
||||||
|
use crate::sizing::ContentSizesRequest;
|
||||||
|
use crate::style_ext::{ComputedValuesExt, DisplayInside};
|
||||||
|
use servo_arc::Arc;
|
||||||
|
use style::properties::ComputedValues;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct FloatBox {
|
pub(crate) struct FloatBox {
|
||||||
|
@ -19,3 +25,23 @@ impl FloatContext {
|
||||||
FloatContext {}
|
FloatContext {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FloatBox {
|
||||||
|
pub fn construct<'dom>(
|
||||||
|
context: &LayoutContext,
|
||||||
|
style: Arc<ComputedValues>,
|
||||||
|
display_inside: DisplayInside,
|
||||||
|
contents: Contents<impl NodeExt<'dom>>,
|
||||||
|
) -> Self {
|
||||||
|
let content_sizes = ContentSizesRequest::inline_if(style.inline_size_is_auto());
|
||||||
|
Self {
|
||||||
|
contents: IndependentFormattingContext::construct(
|
||||||
|
context,
|
||||||
|
style,
|
||||||
|
display_inside,
|
||||||
|
contents,
|
||||||
|
content_sizes,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -10,12 +10,16 @@ use crate::fragments::CollapsedBlockMargins;
|
||||||
use crate::fragments::{AnonymousFragment, BoxFragment, Fragment, TextFragment};
|
use crate::fragments::{AnonymousFragment, BoxFragment, Fragment, TextFragment};
|
||||||
use crate::geom::flow_relative::{Rect, Sides, Vec2};
|
use crate::geom::flow_relative::{Rect, Sides, Vec2};
|
||||||
use crate::positioned::{AbsolutelyPositionedBox, AbsolutelyPositionedFragment};
|
use crate::positioned::{AbsolutelyPositionedBox, AbsolutelyPositionedFragment};
|
||||||
|
use crate::sizing::ContentSizes;
|
||||||
use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayOutside};
|
use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayOutside};
|
||||||
use crate::{relative_adjustement, ContainingBlock};
|
use crate::{relative_adjustement, ContainingBlock};
|
||||||
|
use app_units::Au;
|
||||||
|
use gfx::text::text_run::GlyphRun;
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use style::properties::ComputedValues;
|
use style::properties::ComputedValues;
|
||||||
use style::values::computed::Length;
|
use style::values::computed::{Length, LengthPercentage, Percentage};
|
||||||
use style::Zero;
|
use style::Zero;
|
||||||
|
use webrender_api::FontInstanceKey;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub(crate) struct InlineFormattingContext {
|
pub(crate) struct InlineFormattingContext {
|
||||||
|
@ -63,8 +67,9 @@ struct PartialInlineBoxFragment<'box_tree> {
|
||||||
parent_nesting_level: InlineNestingLevelState<'box_tree>,
|
parent_nesting_level: InlineNestingLevelState<'box_tree>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InlineFormattingContextState<'box_tree, 'cb> {
|
struct InlineFormattingContextState<'box_tree, 'a> {
|
||||||
containing_block: &'cb ContainingBlock,
|
absolutely_positioned_fragments: &'a mut Vec<AbsolutelyPositionedFragment<'box_tree>>,
|
||||||
|
containing_block: &'a ContainingBlock,
|
||||||
line_boxes: LinesBoxes,
|
line_boxes: LinesBoxes,
|
||||||
inline_position: Length,
|
inline_position: Length,
|
||||||
partial_inline_boxes_stack: Vec<PartialInlineBoxFragment<'box_tree>>,
|
partial_inline_boxes_stack: Vec<PartialInlineBoxFragment<'box_tree>>,
|
||||||
|
@ -77,6 +82,114 @@ struct LinesBoxes {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InlineFormattingContext {
|
impl InlineFormattingContext {
|
||||||
|
// This works on an already-constructed `InlineFormattingContext`,
|
||||||
|
// Which would have to change if/when
|
||||||
|
// `BlockContainer::construct` parallelize their construction.
|
||||||
|
pub(super) fn inline_content_sizes(&self, layout_context: &LayoutContext) -> ContentSizes {
|
||||||
|
struct Computation {
|
||||||
|
paragraph: ContentSizes,
|
||||||
|
current_line: ContentSizes,
|
||||||
|
current_line_percentages: Percentage,
|
||||||
|
}
|
||||||
|
impl Computation {
|
||||||
|
fn traverse(
|
||||||
|
&mut self,
|
||||||
|
layout_context: &LayoutContext,
|
||||||
|
inline_level_boxes: &[Arc<InlineLevelBox>],
|
||||||
|
) {
|
||||||
|
for inline_level_box in inline_level_boxes {
|
||||||
|
match &**inline_level_box {
|
||||||
|
InlineLevelBox::InlineBox(inline_box) => {
|
||||||
|
let padding = inline_box.style.padding();
|
||||||
|
let border = inline_box.style.border_width();
|
||||||
|
let margin = inline_box.style.margin();
|
||||||
|
macro_rules! add {
|
||||||
|
($condition: ident, $side: ident) => {
|
||||||
|
if inline_box.$condition {
|
||||||
|
self.add_lengthpercentage(padding.$side);
|
||||||
|
self.add_length(border.$side);
|
||||||
|
if let Some(lp) = margin.$side.non_auto() {
|
||||||
|
self.add_lengthpercentage(lp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
add!(first_fragment, inline_start);
|
||||||
|
self.traverse(layout_context, &inline_box.children);
|
||||||
|
add!(last_fragment, inline_end);
|
||||||
|
},
|
||||||
|
InlineLevelBox::TextRun(text_run) => {
|
||||||
|
let BreakAndShapeResult {
|
||||||
|
runs,
|
||||||
|
break_at_start,
|
||||||
|
..
|
||||||
|
} = text_run.break_and_shape(layout_context);
|
||||||
|
if break_at_start {
|
||||||
|
self.line_break_opportunity()
|
||||||
|
}
|
||||||
|
for run in &runs {
|
||||||
|
let advance = Length::from(run.glyph_store.total_advance());
|
||||||
|
if run.glyph_store.is_whitespace() {
|
||||||
|
self.line_break_opportunity()
|
||||||
|
} else {
|
||||||
|
self.current_line.min_content += advance
|
||||||
|
}
|
||||||
|
self.current_line.max_content += advance
|
||||||
|
}
|
||||||
|
},
|
||||||
|
InlineLevelBox::Atomic(atomic) => {
|
||||||
|
let (outer, pc) = atomic
|
||||||
|
.content_sizes
|
||||||
|
.outer_inline_and_percentages(&atomic.style);
|
||||||
|
self.current_line.min_content += outer.min_content;
|
||||||
|
self.current_line.max_content += outer.max_content;
|
||||||
|
self.current_line_percentages += pc;
|
||||||
|
},
|
||||||
|
InlineLevelBox::OutOfFlowFloatBox(_) |
|
||||||
|
InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_lengthpercentage(&mut self, lp: LengthPercentage) {
|
||||||
|
self.add_length(lp.length_component());
|
||||||
|
self.current_line_percentages += lp.percentage_component();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_length(&mut self, l: Length) {
|
||||||
|
self.current_line.min_content += l;
|
||||||
|
self.current_line.max_content += l;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn line_break_opportunity(&mut self) {
|
||||||
|
self.paragraph
|
||||||
|
.min_content
|
||||||
|
.max_assign(take(&mut self.current_line.min_content));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn forced_line_break(&mut self) {
|
||||||
|
self.line_break_opportunity();
|
||||||
|
self.current_line
|
||||||
|
.adjust_for_pbm_percentages(take(&mut self.current_line_percentages));
|
||||||
|
self.paragraph
|
||||||
|
.max_content
|
||||||
|
.max_assign(take(&mut self.current_line.max_content));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn take<T: Zero>(x: &mut T) -> T {
|
||||||
|
std::mem::replace(x, T::zero())
|
||||||
|
}
|
||||||
|
let mut computation = Computation {
|
||||||
|
paragraph: ContentSizes::zero(),
|
||||||
|
current_line: ContentSizes::zero(),
|
||||||
|
current_line_percentages: Percentage::zero(),
|
||||||
|
};
|
||||||
|
computation.traverse(layout_context, &self.inline_level_boxes);
|
||||||
|
computation.forced_line_break();
|
||||||
|
computation.paragraph
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn layout<'a>(
|
pub(super) fn layout<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
layout_context: &LayoutContext,
|
layout_context: &LayoutContext,
|
||||||
|
@ -85,6 +198,7 @@ impl InlineFormattingContext {
|
||||||
absolutely_positioned_fragments: &mut Vec<AbsolutelyPositionedFragment<'a>>,
|
absolutely_positioned_fragments: &mut Vec<AbsolutelyPositionedFragment<'a>>,
|
||||||
) -> FlowLayout {
|
) -> FlowLayout {
|
||||||
let mut ifc = InlineFormattingContextState {
|
let mut ifc = InlineFormattingContextState {
|
||||||
|
absolutely_positioned_fragments,
|
||||||
containing_block,
|
containing_block,
|
||||||
partial_inline_boxes_stack: Vec::new(),
|
partial_inline_boxes_stack: Vec::new(),
|
||||||
line_boxes: LinesBoxes {
|
line_boxes: LinesBoxes {
|
||||||
|
@ -107,10 +221,7 @@ impl InlineFormattingContext {
|
||||||
ifc.partial_inline_boxes_stack.push(partial)
|
ifc.partial_inline_boxes_stack.push(partial)
|
||||||
},
|
},
|
||||||
InlineLevelBox::TextRun(run) => run.layout(layout_context, &mut ifc),
|
InlineLevelBox::TextRun(run) => run.layout(layout_context, &mut ifc),
|
||||||
InlineLevelBox::Atomic(_independent) => {
|
InlineLevelBox::Atomic(a) => layout_atomic(layout_context, &mut ifc, a),
|
||||||
// TODO
|
|
||||||
continue;
|
|
||||||
},
|
|
||||||
InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => {
|
InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => {
|
||||||
let initial_start_corner =
|
let initial_start_corner =
|
||||||
match Display::from(box_.contents.style.get_box().original_display) {
|
match Display::from(box_.contents.style.get_box().original_display) {
|
||||||
|
@ -131,12 +242,11 @@ impl InlineFormattingContext {
|
||||||
panic!("display:none does not generate an abspos box")
|
panic!("display:none does not generate an abspos box")
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
absolutely_positioned_fragments
|
ifc.absolutely_positioned_fragments
|
||||||
.push(box_.layout(initial_start_corner, tree_rank));
|
.push(box_.layout(initial_start_corner, tree_rank));
|
||||||
},
|
},
|
||||||
InlineLevelBox::OutOfFlowFloatBox(_box_) => {
|
InlineLevelBox::OutOfFlowFloatBox(_box_) => {
|
||||||
// TODO
|
// TODO
|
||||||
continue;
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
|
@ -282,12 +392,114 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn layout_atomic<'box_tree>(
|
||||||
|
layout_context: &LayoutContext,
|
||||||
|
ifc: &mut InlineFormattingContextState<'box_tree, '_>,
|
||||||
|
atomic: &'box_tree IndependentFormattingContext,
|
||||||
|
) {
|
||||||
|
let cbis = ifc.containing_block.inline_size;
|
||||||
|
let padding = atomic.style.padding().percentages_relative_to(cbis);
|
||||||
|
let border = atomic.style.border_width();
|
||||||
|
let margin = atomic
|
||||||
|
.style
|
||||||
|
.margin()
|
||||||
|
.percentages_relative_to(cbis)
|
||||||
|
.auto_is(Length::zero);
|
||||||
|
let pbm = &(&padding + &border) + &margin;
|
||||||
|
ifc.inline_position += pbm.inline_start;
|
||||||
|
let mut start_corner = Vec2 {
|
||||||
|
block: pbm.block_start,
|
||||||
|
inline: ifc.inline_position - ifc.current_nesting_level.inline_start,
|
||||||
|
};
|
||||||
|
start_corner += &relative_adjustement(
|
||||||
|
&atomic.style,
|
||||||
|
ifc.containing_block.inline_size,
|
||||||
|
ifc.containing_block.block_size,
|
||||||
|
);
|
||||||
|
|
||||||
|
let fragment = match atomic.as_replaced() {
|
||||||
|
Ok(replaced) => {
|
||||||
|
// FIXME: implement https://drafts.csswg.org/css2/visudet.html#inline-replaced-width
|
||||||
|
// 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 content_rect = Rect { start_corner, size };
|
||||||
|
BoxFragment {
|
||||||
|
style: atomic.style.clone(),
|
||||||
|
children: fragments,
|
||||||
|
content_rect,
|
||||||
|
padding,
|
||||||
|
border,
|
||||||
|
margin,
|
||||||
|
block_margins_collapsed_with_children: CollapsedBlockMargins::zero(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(non_replaced) => {
|
||||||
|
let box_size = atomic.style.box_size();
|
||||||
|
let inline_size = box_size.inline.percentage_relative_to(cbis).auto_is(|| {
|
||||||
|
let available_size = cbis - pbm.inline_sum();
|
||||||
|
atomic.content_sizes.shrink_to_fit(available_size)
|
||||||
|
});
|
||||||
|
let block_size = box_size
|
||||||
|
.block
|
||||||
|
.maybe_percentage_relative_to(ifc.containing_block.block_size.non_auto());
|
||||||
|
let containing_block_for_children = ContainingBlock {
|
||||||
|
inline_size,
|
||||||
|
block_size,
|
||||||
|
mode: atomic.style.writing_mode(),
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
ifc.containing_block.mode, containing_block_for_children.mode,
|
||||||
|
"Mixed writing modes are not supported yet"
|
||||||
|
);
|
||||||
|
// FIXME is this correct?
|
||||||
|
let dummy_tree_rank = 0;
|
||||||
|
// FIXME: Do we need to call `adjust_static_positions` somewhere near here?
|
||||||
|
let independent_layout = non_replaced.layout(
|
||||||
|
layout_context,
|
||||||
|
&containing_block_for_children,
|
||||||
|
dummy_tree_rank,
|
||||||
|
ifc.absolutely_positioned_fragments,
|
||||||
|
);
|
||||||
|
let block_size = block_size.auto_is(|| independent_layout.content_block_size);
|
||||||
|
let content_rect = Rect {
|
||||||
|
start_corner,
|
||||||
|
size: Vec2 {
|
||||||
|
block: block_size,
|
||||||
|
inline: inline_size,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
BoxFragment {
|
||||||
|
style: atomic.style.clone(),
|
||||||
|
children: independent_layout.fragments,
|
||||||
|
content_rect,
|
||||||
|
padding,
|
||||||
|
border,
|
||||||
|
margin,
|
||||||
|
block_margins_collapsed_with_children: CollapsedBlockMargins::zero(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
ifc.inline_position += pbm.inline_end;
|
||||||
|
ifc.current_nesting_level
|
||||||
|
.fragments_so_far
|
||||||
|
.push(Fragment::Box(fragment));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BreakAndShapeResult {
|
||||||
|
font_ascent: Au,
|
||||||
|
font_line_gap: Au,
|
||||||
|
font_key: FontInstanceKey,
|
||||||
|
runs: Vec<GlyphRun>,
|
||||||
|
break_at_start: bool,
|
||||||
|
}
|
||||||
|
|
||||||
impl TextRun {
|
impl TextRun {
|
||||||
fn layout(&self, layout_context: &LayoutContext, ifc: &mut InlineFormattingContextState) {
|
fn break_and_shape(&self, layout_context: &LayoutContext) -> BreakAndShapeResult {
|
||||||
use gfx::font::ShapingFlags;
|
use gfx::font::ShapingFlags;
|
||||||
use style::computed_values::text_rendering::T as TextRendering;
|
use style::computed_values::text_rendering::T as TextRendering;
|
||||||
use style::computed_values::word_break::T as WordBreak;
|
use style::computed_values::word_break::T as WordBreak;
|
||||||
use style::values::generics::text::LineHeight;
|
|
||||||
|
|
||||||
let font_style = self.parent_style.clone_font();
|
let font_style = self.parent_style.clone_font();
|
||||||
let inherited_text_style = self.parent_style.get_inherited_text();
|
let inherited_text_style = self.parent_style.get_inherited_text();
|
||||||
|
@ -316,30 +528,41 @@ impl TextRun {
|
||||||
flags,
|
flags,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (font_ascent, font_line_gap, font_key, runs) =
|
crate::context::with_thread_local_font_context(layout_context, |font_context| {
|
||||||
crate::context::with_thread_local_font_context(layout_context, |font_context| {
|
let font_group = font_context.font_group(font_style);
|
||||||
let font_group = font_context.font_group(font_style);
|
let font = font_group
|
||||||
let font = font_group
|
.borrow_mut()
|
||||||
.borrow_mut()
|
.first(font_context)
|
||||||
.first(font_context)
|
.expect("could not find font");
|
||||||
.expect("could not find font");
|
let mut font = font.borrow_mut();
|
||||||
let mut font = font.borrow_mut();
|
|
||||||
|
|
||||||
let (runs, _break_at_start) = gfx::text::text_run::TextRun::break_and_shape(
|
let (runs, break_at_start) = gfx::text::text_run::TextRun::break_and_shape(
|
||||||
&mut font,
|
&mut font,
|
||||||
&self.text,
|
&self.text,
|
||||||
&shaping_options,
|
&shaping_options,
|
||||||
&mut None,
|
&mut None,
|
||||||
);
|
);
|
||||||
|
|
||||||
(
|
BreakAndShapeResult {
|
||||||
font.metrics.ascent,
|
font_ascent: font.metrics.ascent,
|
||||||
font.metrics.line_gap,
|
font_line_gap: font.metrics.line_gap,
|
||||||
font.font_key,
|
font_key: font.font_key,
|
||||||
runs,
|
runs,
|
||||||
)
|
break_at_start,
|
||||||
});
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout(&self, layout_context: &LayoutContext, ifc: &mut InlineFormattingContextState) {
|
||||||
|
use style::values::generics::text::LineHeight;
|
||||||
|
|
||||||
|
let BreakAndShapeResult {
|
||||||
|
font_ascent,
|
||||||
|
font_line_gap,
|
||||||
|
font_key,
|
||||||
|
runs,
|
||||||
|
break_at_start: _,
|
||||||
|
} = self.break_and_shape(layout_context);
|
||||||
let font_size = self.parent_style.get_font().font_size.size.0;
|
let font_size = self.parent_style.get_font().font_size.size.0;
|
||||||
let mut runs = runs.iter();
|
let mut runs = runs.iter();
|
||||||
loop {
|
loop {
|
||||||
|
|
|
@ -656,17 +656,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 containing_block_for_children = ContainingBlock {
|
let size = Vec2 {
|
||||||
inline_size,
|
block: block_size,
|
||||||
block_size: LengthOrAuto::LengthPercentage(block_size),
|
inline: inline_size,
|
||||||
mode,
|
|
||||||
};
|
};
|
||||||
// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
|
let fragments = replaced.make_fragments(style, size.clone());
|
||||||
assert_eq!(
|
|
||||||
containing_block.mode, containing_block_for_children.mode,
|
|
||||||
"Mixed writing modes are not supported yet"
|
|
||||||
);
|
|
||||||
let independent_layout = replaced.layout(style, &containing_block_for_children);
|
|
||||||
let relative_adjustement = relative_adjustement(
|
let relative_adjustement = relative_adjustement(
|
||||||
style,
|
style,
|
||||||
inline_size,
|
inline_size,
|
||||||
|
@ -677,14 +671,11 @@ fn layout_in_flow_replaced_block_level<'a>(
|
||||||
block: pb.block_start + relative_adjustement.block,
|
block: pb.block_start + relative_adjustement.block,
|
||||||
inline: pb.inline_start + relative_adjustement.inline + margin.inline_start,
|
inline: pb.inline_start + relative_adjustement.inline + margin.inline_start,
|
||||||
},
|
},
|
||||||
size: Vec2 {
|
size,
|
||||||
block: block_size,
|
|
||||||
inline: inline_size,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
BoxFragment {
|
BoxFragment {
|
||||||
style: style.clone(),
|
style: style.clone(),
|
||||||
children: independent_layout.fragments,
|
children: fragments,
|
||||||
content_rect,
|
content_rect,
|
||||||
padding,
|
padding,
|
||||||
border,
|
border,
|
||||||
|
|
|
@ -14,12 +14,12 @@ use crate::geom;
|
||||||
use crate::geom::flow_relative::Vec2;
|
use crate::geom::flow_relative::Vec2;
|
||||||
use crate::positioned::AbsolutelyPositionedBox;
|
use crate::positioned::AbsolutelyPositionedBox;
|
||||||
use crate::replaced::ReplacedContent;
|
use crate::replaced::ReplacedContent;
|
||||||
|
use crate::sizing::ContentSizesRequest;
|
||||||
use crate::style_ext::{Direction, Display, DisplayGeneratingBox, DisplayInside, WritingMode};
|
use crate::style_ext::{Direction, Display, DisplayGeneratingBox, DisplayInside, WritingMode};
|
||||||
use crate::{ContainingBlock, DefiniteContainingBlock};
|
use crate::{ContainingBlock, 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::context::SharedStyleContext;
|
|
||||||
use style::values::computed::{Length, LengthOrAuto};
|
use style::values::computed::{Length, LengthOrAuto};
|
||||||
use style::Zero;
|
use style::Zero;
|
||||||
use style_traits::CSSPixel;
|
use style_traits::CSSPixel;
|
||||||
|
@ -28,7 +28,7 @@ pub struct BoxTreeRoot(BlockFormattingContext);
|
||||||
pub struct FragmentTreeRoot(Vec<Fragment>);
|
pub struct FragmentTreeRoot(Vec<Fragment>);
|
||||||
|
|
||||||
impl BoxTreeRoot {
|
impl BoxTreeRoot {
|
||||||
pub fn construct<'dom, Node>(context: &SharedStyleContext<'_>, root_element: Node) -> Self
|
pub fn construct<'dom, Node>(context: &LayoutContext, root_element: Node) -> Self
|
||||||
where
|
where
|
||||||
Node: 'dom + Copy + LayoutNode + Send + Sync,
|
Node: 'dom + Copy + LayoutNode + Send + Sync,
|
||||||
{
|
{
|
||||||
|
@ -41,7 +41,7 @@ impl BoxTreeRoot {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn construct_for_root_element<'dom>(
|
fn construct_for_root_element<'dom>(
|
||||||
context: &SharedStyleContext<'_>,
|
context: &LayoutContext,
|
||||||
root_element: impl NodeExt<'dom>,
|
root_element: impl NodeExt<'dom>,
|
||||||
) -> (ContainsFloats, Vec<Arc<BlockLevelBox>>) {
|
) -> (ContainsFloats, Vec<Arc<BlockLevelBox>>) {
|
||||||
let style = root_element.style(context);
|
let style = root_element.style(context);
|
||||||
|
@ -60,32 +60,33 @@ fn construct_for_root_element<'dom>(
|
||||||
Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { inside, .. }) => inside,
|
Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { inside, .. }) => inside,
|
||||||
};
|
};
|
||||||
|
|
||||||
let position = box_style.position;
|
let contents = replaced.map_or(Contents::OfElement(root_element), Contents::Replaced);
|
||||||
let float = box_style.float;
|
if box_style.position.is_absolutely_positioned() {
|
||||||
let contents = IndependentFormattingContext::construct(
|
|
||||||
context,
|
|
||||||
style,
|
|
||||||
display_inside,
|
|
||||||
replaced.map_or(Contents::OfElement(root_element), Contents::Replaced),
|
|
||||||
);
|
|
||||||
if position.is_absolutely_positioned() {
|
|
||||||
(
|
(
|
||||||
ContainsFloats::No,
|
ContainsFloats::No,
|
||||||
vec![Arc::new(BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(
|
vec![Arc::new(BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(
|
||||||
AbsolutelyPositionedBox { contents },
|
AbsolutelyPositionedBox::construct(context, style, display_inside, contents),
|
||||||
))],
|
))],
|
||||||
)
|
)
|
||||||
} else if float.is_floating() {
|
} else if box_style.float.is_floating() {
|
||||||
(
|
(
|
||||||
ContainsFloats::Yes,
|
ContainsFloats::Yes,
|
||||||
vec![Arc::new(BlockLevelBox::OutOfFlowFloatBox(FloatBox {
|
vec![Arc::new(BlockLevelBox::OutOfFlowFloatBox(
|
||||||
contents,
|
FloatBox::construct(context, style, display_inside, contents),
|
||||||
}))],
|
))],
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
ContainsFloats::No,
|
ContainsFloats::No,
|
||||||
vec![Arc::new(BlockLevelBox::Independent(contents))],
|
vec![Arc::new(BlockLevelBox::Independent(
|
||||||
|
IndependentFormattingContext::construct(
|
||||||
|
context,
|
||||||
|
style,
|
||||||
|
display_inside,
|
||||||
|
contents,
|
||||||
|
ContentSizesRequest::None,
|
||||||
|
),
|
||||||
|
))],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,11 @@ use crate::flow::BlockFormattingContext;
|
||||||
use crate::fragments::Fragment;
|
use crate::fragments::Fragment;
|
||||||
use crate::positioned::AbsolutelyPositionedFragment;
|
use crate::positioned::AbsolutelyPositionedFragment;
|
||||||
use crate::replaced::ReplacedContent;
|
use crate::replaced::ReplacedContent;
|
||||||
|
use crate::sizing::{BoxContentSizes, ContentSizesRequest};
|
||||||
use crate::style_ext::DisplayInside;
|
use crate::style_ext::DisplayInside;
|
||||||
use crate::ContainingBlock;
|
use crate::ContainingBlock;
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use style::context::SharedStyleContext;
|
|
||||||
use style::properties::ComputedValues;
|
use style::properties::ComputedValues;
|
||||||
use style::values::computed::Length;
|
use style::values::computed::Length;
|
||||||
|
|
||||||
|
@ -20,6 +20,10 @@ use style::values::computed::Length;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct IndependentFormattingContext {
|
pub(crate) struct IndependentFormattingContext {
|
||||||
pub style: Arc<ComputedValues>,
|
pub style: Arc<ComputedValues>,
|
||||||
|
|
||||||
|
/// If it was requested during construction
|
||||||
|
pub content_sizes: BoxContentSizes,
|
||||||
|
|
||||||
contents: IndependentFormattingContextContents,
|
contents: IndependentFormattingContextContents,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,22 +50,39 @@ enum NonReplacedIFCKind<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndependentFormattingContext {
|
impl IndependentFormattingContext {
|
||||||
pub fn construct<'dom, 'style>(
|
pub fn construct<'dom>(
|
||||||
context: &SharedStyleContext<'style>,
|
context: &LayoutContext,
|
||||||
style: Arc<ComputedValues>,
|
style: Arc<ComputedValues>,
|
||||||
display_inside: DisplayInside,
|
display_inside: DisplayInside,
|
||||||
contents: Contents<impl NodeExt<'dom>>,
|
contents: Contents<impl NodeExt<'dom>>,
|
||||||
|
content_sizes: ContentSizesRequest,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
use self::IndependentFormattingContextContents as Contents;
|
use self::IndependentFormattingContextContents as Contents;
|
||||||
let 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 => Contents::Flow(
|
DisplayInside::Flow | DisplayInside::FlowRoot => {
|
||||||
BlockFormattingContext::construct(context, &style, non_replaced),
|
let (bfc, box_content_sizes) = BlockFormattingContext::construct(
|
||||||
),
|
context,
|
||||||
|
&style,
|
||||||
|
non_replaced,
|
||||||
|
content_sizes,
|
||||||
|
);
|
||||||
|
(Contents::Flow(bfc), box_content_sizes)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Err(replaced) => {
|
||||||
|
// The `content_sizes` field is not used by layout code:
|
||||||
|
(
|
||||||
|
Contents::Replaced(replaced),
|
||||||
|
BoxContentSizes::NoneWereRequested,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
Err(replaced) => Contents::Replaced(replaced),
|
|
||||||
};
|
};
|
||||||
Self { style, contents }
|
Self {
|
||||||
|
style,
|
||||||
|
contents,
|
||||||
|
content_sizes,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_replaced(&self) -> Result<&ReplacedContent, NonReplacedIFC> {
|
pub fn as_replaced(&self) -> Result<&ReplacedContent, NonReplacedIFC> {
|
||||||
|
@ -73,24 +94,6 @@ impl IndependentFormattingContext {
|
||||||
Contents::Flow(f) => Err(NR(Kind::Flow(f))),
|
Contents::Flow(f) => Err(NR(Kind::Flow(f))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn layout<'a>(
|
|
||||||
&'a self,
|
|
||||||
layout_context: &LayoutContext,
|
|
||||||
containing_block: &ContainingBlock,
|
|
||||||
tree_rank: usize,
|
|
||||||
absolutely_positioned_fragments: &mut Vec<AbsolutelyPositionedFragment<'a>>,
|
|
||||||
) -> IndependentLayout {
|
|
||||||
match self.as_replaced() {
|
|
||||||
Ok(replaced) => replaced.layout(&self.style, containing_block),
|
|
||||||
Err(ifc) => ifc.layout(
|
|
||||||
layout_context,
|
|
||||||
containing_block,
|
|
||||||
tree_rank,
|
|
||||||
absolutely_positioned_fragments,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> NonReplacedIFC<'a> {
|
impl<'a> NonReplacedIFC<'a> {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#![deny(unsafe_code)]
|
#![deny(unsafe_code)]
|
||||||
#![feature(exact_size_is_empty)]
|
#![feature(exact_size_is_empty)]
|
||||||
|
#![feature(matches_macro)]
|
||||||
|
|
||||||
pub mod context;
|
pub mod context;
|
||||||
pub mod data;
|
pub mod data;
|
||||||
|
@ -18,6 +19,7 @@ mod opaque_node;
|
||||||
mod positioned;
|
mod positioned;
|
||||||
pub mod query;
|
pub mod query;
|
||||||
mod replaced;
|
mod replaced;
|
||||||
|
mod sizing;
|
||||||
mod style_ext;
|
mod style_ext;
|
||||||
pub mod traversal;
|
pub mod traversal;
|
||||||
pub mod wrapper;
|
pub mod wrapper;
|
||||||
|
|
|
@ -3,12 +3,16 @@
|
||||||
* 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::context::LayoutContext;
|
use crate::context::LayoutContext;
|
||||||
|
use crate::dom_traversal::{Contents, NodeExt};
|
||||||
use crate::formatting_contexts::IndependentFormattingContext;
|
use crate::formatting_contexts::IndependentFormattingContext;
|
||||||
use crate::fragments::{AnonymousFragment, BoxFragment, CollapsedBlockMargins, Fragment};
|
use crate::fragments::{AnonymousFragment, BoxFragment, CollapsedBlockMargins, Fragment};
|
||||||
use crate::geom::flow_relative::{Rect, Sides, Vec2};
|
use crate::geom::flow_relative::{Rect, Sides, Vec2};
|
||||||
use crate::style_ext::{ComputedValuesExt, Direction, WritingMode};
|
use crate::sizing::ContentSizesRequest;
|
||||||
|
use crate::style_ext::{ComputedValuesExt, Direction, DisplayInside, WritingMode};
|
||||||
use crate::{ContainingBlock, DefiniteContainingBlock};
|
use crate::{ContainingBlock, DefiniteContainingBlock};
|
||||||
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||||
|
use servo_arc::Arc;
|
||||||
|
use style::properties::ComputedValues;
|
||||||
use style::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
|
use style::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
|
||||||
use style::Zero;
|
use style::Zero;
|
||||||
|
|
||||||
|
@ -42,6 +46,32 @@ pub(crate) enum AbsoluteBoxOffsets<NonStatic> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AbsolutelyPositionedBox {
|
impl AbsolutelyPositionedBox {
|
||||||
|
pub fn construct<'dom>(
|
||||||
|
context: &LayoutContext,
|
||||||
|
style: Arc<ComputedValues>,
|
||||||
|
display_inside: DisplayInside,
|
||||||
|
contents: Contents<impl NodeExt<'dom>>,
|
||||||
|
) -> Self {
|
||||||
|
// "Shrink-to-fit" in https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width
|
||||||
|
let content_sizes = ContentSizesRequest::inline_if(
|
||||||
|
// If inline-size is non-auto, that value is used without shrink-to-fit
|
||||||
|
style.inline_size_is_auto() &&
|
||||||
|
// 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
|
||||||
|
// in the constraint equation.
|
||||||
|
!style.inline_box_offsets_are_both_non_auto(),
|
||||||
|
);
|
||||||
|
Self {
|
||||||
|
contents: IndependentFormattingContext::construct(
|
||||||
|
context,
|
||||||
|
style,
|
||||||
|
display_inside,
|
||||||
|
contents,
|
||||||
|
content_sizes,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn layout<'a>(
|
pub(crate) fn layout<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
initial_start_corner: Vec2<Length>,
|
initial_start_corner: Vec2<Length>,
|
||||||
|
@ -256,28 +286,60 @@ impl<'a> AbsolutelyPositionedFragment<'a> {
|
||||||
Anchor::End(end) => cbis - end - pb.inline_sum() - margin.inline_sum(),
|
Anchor::End(end) => cbis - end - pb.inline_sum() - margin.inline_sum(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME(nox): shrink-to-fit.
|
if self
|
||||||
available_size
|
.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
|
||||||
|
.contents
|
||||||
|
.content_sizes
|
||||||
|
.shrink_to_fit(available_size)
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let containing_block_for_children = ContainingBlock {
|
let mut absolutely_positioned_fragments = Vec::new();
|
||||||
inline_size,
|
let mut independent_layout = match self.absolutely_positioned_box.contents.as_replaced() {
|
||||||
block_size,
|
Ok(replaced) => {
|
||||||
mode: style.writing_mode(),
|
// 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 {
|
||||||
|
inline_size,
|
||||||
|
block_size,
|
||||||
|
mode: style.writing_mode(),
|
||||||
|
};
|
||||||
|
// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
|
||||||
|
assert_eq!(
|
||||||
|
containing_block.mode, containing_block_for_children.mode,
|
||||||
|
"Mixed writing modes are not supported yet"
|
||||||
|
);
|
||||||
|
let dummy_tree_rank = 0;
|
||||||
|
non_replaced.layout(
|
||||||
|
layout_context,
|
||||||
|
&containing_block_for_children,
|
||||||
|
dummy_tree_rank,
|
||||||
|
&mut absolutely_positioned_fragments,
|
||||||
|
)
|
||||||
|
},
|
||||||
};
|
};
|
||||||
// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
|
|
||||||
assert_eq!(
|
|
||||||
containing_block.mode, containing_block_for_children.mode,
|
|
||||||
"Mixed writing modes are not supported yet"
|
|
||||||
);
|
|
||||||
let dummy_tree_rank = 0;
|
|
||||||
let mut absolutely_positioned_fragments = vec![];
|
|
||||||
let mut independent_layout = self.absolutely_positioned_box.contents.layout(
|
|
||||||
layout_context,
|
|
||||||
&containing_block_for_children,
|
|
||||||
dummy_tree_rank,
|
|
||||||
&mut absolutely_positioned_fragments,
|
|
||||||
);
|
|
||||||
|
|
||||||
let inline_start = match inline_anchor {
|
let inline_start = match inline_anchor {
|
||||||
Anchor::Start(start) => start + pb.inline_start + margin.inline_start,
|
Anchor::Start(start) => start + pb.inline_start + margin.inline_start,
|
||||||
|
@ -307,7 +369,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> {
|
||||||
&mut independent_layout.fragments,
|
&mut independent_layout.fragments,
|
||||||
&content_rect.size,
|
&content_rect.size,
|
||||||
&padding,
|
&padding,
|
||||||
containing_block_for_children.mode,
|
style.writing_mode(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Fragment::Box(BoxFragment {
|
Fragment::Box(BoxFragment {
|
||||||
|
|
|
@ -3,10 +3,8 @@
|
||||||
* 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::dom_traversal::NodeExt;
|
use crate::dom_traversal::NodeExt;
|
||||||
use crate::formatting_contexts::IndependentLayout;
|
|
||||||
use crate::fragments::{Fragment, ImageFragment};
|
use crate::fragments::{Fragment, ImageFragment};
|
||||||
use crate::geom::{flow_relative, physical};
|
use crate::geom::{flow_relative, physical};
|
||||||
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;
|
||||||
|
@ -35,39 +33,27 @@ impl ReplacedContent {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn layout<'a>(
|
pub fn make_fragments<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
style: &ServoArc<ComputedValues>,
|
style: &ServoArc<ComputedValues>,
|
||||||
containing_block: &ContainingBlock,
|
size: flow_relative::Vec2<Length>,
|
||||||
) -> IndependentLayout {
|
) -> Vec<Fragment> {
|
||||||
let (fragments, content_block_size) = match self.kind {
|
match &self.kind {
|
||||||
ReplacedContentKind::Image(ref image) => {
|
ReplacedContentKind::Image(image) => image
|
||||||
// FIXME(nox): We should not assume block size is known.
|
.as_ref()
|
||||||
let block_size = containing_block.block_size.non_auto().unwrap();
|
.and_then(|image| image.id)
|
||||||
let fragments = image
|
.map(|image_key| {
|
||||||
.as_ref()
|
Fragment::Image(ImageFragment {
|
||||||
.and_then(|image| image.id)
|
style: style.clone(),
|
||||||
.map(|image_key| {
|
content_rect: flow_relative::Rect {
|
||||||
Fragment::Image(ImageFragment {
|
start_corner: flow_relative::Vec2::zero(),
|
||||||
style: style.clone(),
|
size,
|
||||||
content_rect: flow_relative::Rect {
|
},
|
||||||
start_corner: flow_relative::Vec2::zero(),
|
image_key,
|
||||||
size: flow_relative::Vec2 {
|
|
||||||
inline: containing_block.inline_size,
|
|
||||||
block: block_size,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
image_key,
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.into_iter()
|
})
|
||||||
.collect::<Vec<_>>();
|
.into_iter()
|
||||||
(fragments, block_size)
|
.collect(),
|
||||||
},
|
|
||||||
};
|
|
||||||
IndependentLayout {
|
|
||||||
fragments,
|
|
||||||
content_block_size,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
152
components/layout_2020/sizing.rs
Normal file
152
components/layout_2020/sizing.rs
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* 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/. */
|
||||||
|
|
||||||
|
//! https://drafts.csswg.org/css-sizing/
|
||||||
|
|
||||||
|
use crate::style_ext::ComputedValuesExt;
|
||||||
|
use style::properties::ComputedValues;
|
||||||
|
use style::values::computed::{Length, LengthPercentage, Percentage};
|
||||||
|
use style::Zero;
|
||||||
|
|
||||||
|
/// Which min/max-content values should be computed during box construction
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub(crate) enum ContentSizesRequest {
|
||||||
|
Inline,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContentSizesRequest {
|
||||||
|
pub fn inline_if(condition: bool) -> Self {
|
||||||
|
if condition {
|
||||||
|
Self::Inline
|
||||||
|
} else {
|
||||||
|
Self::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn requests_inline(self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Inline => true,
|
||||||
|
Self::None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn if_requests_inline<T>(self, f: impl FnOnce() -> T) -> Option<T> {
|
||||||
|
match self {
|
||||||
|
Self::Inline => Some(f()),
|
||||||
|
Self::None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compute(self, compute_inline: impl FnOnce() -> ContentSizes) -> BoxContentSizes {
|
||||||
|
match self {
|
||||||
|
Self::Inline => BoxContentSizes::Inline(compute_inline()),
|
||||||
|
Self::None => BoxContentSizes::NoneWereRequested,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(crate) struct ContentSizes {
|
||||||
|
pub min_content: Length,
|
||||||
|
pub max_content: Length,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-sizing/#intrinsic-sizes
|
||||||
|
impl ContentSizes {
|
||||||
|
pub fn zero() -> Self {
|
||||||
|
Self {
|
||||||
|
min_content: Length::zero(),
|
||||||
|
max_content: Length::zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn max_assign(&mut self, other: &Self) {
|
||||||
|
self.min_content.max_assign(other.min_content);
|
||||||
|
self.max_content.max_assign(other.max_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Relevant to outer intrinsic inline sizes, for percentages from padding and margin.
|
||||||
|
pub fn adjust_for_pbm_percentages(&mut self, percentages: Percentage) {
|
||||||
|
// " Note that this may yield an infinite result, but undefined results
|
||||||
|
// (zero divided by zero) must be treated as zero. "
|
||||||
|
if self.max_content.px() == 0. {
|
||||||
|
// Avoid a potential `NaN`.
|
||||||
|
// Zero is already the result we want regardless of `denominator`.
|
||||||
|
} else {
|
||||||
|
let denominator = (1. - percentages.0).max(0.);
|
||||||
|
self.max_content = Length::new(self.max_content.px() / denominator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Optional min/max-content for storage in the box tree
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum BoxContentSizes {
|
||||||
|
NoneWereRequested, // … during box construction
|
||||||
|
Inline(ContentSizes),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BoxContentSizes {
|
||||||
|
fn expect_inline(&self) -> &ContentSizes {
|
||||||
|
match self {
|
||||||
|
Self::NoneWereRequested => panic!("Accessing content size that was not requested"),
|
||||||
|
Self::Inline(s) => s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://dbaron.org/css/intrinsic/#outer-intrinsic
|
||||||
|
pub fn outer_inline(&self, style: &ComputedValues) -> ContentSizes {
|
||||||
|
let (mut outer, percentages) = self.outer_inline_and_percentages(style);
|
||||||
|
outer.adjust_for_pbm_percentages(percentages);
|
||||||
|
outer
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn outer_inline_and_percentages(
|
||||||
|
&self,
|
||||||
|
style: &ComputedValues,
|
||||||
|
) -> (ContentSizes, Percentage) {
|
||||||
|
// FIXME: account for 'min-width', 'max-width', 'box-sizing'
|
||||||
|
|
||||||
|
let inline_size = style.box_size().inline;
|
||||||
|
// Percentages for 'width' are treated as 'auto'
|
||||||
|
let inline_size = inline_size.map(|lp| lp.as_length());
|
||||||
|
// The (inner) min/max-content are only used for 'auto'
|
||||||
|
let mut outer = match inline_size.non_auto().flatten() {
|
||||||
|
None => self.expect_inline().clone(),
|
||||||
|
Some(length) => ContentSizes {
|
||||||
|
min_content: length,
|
||||||
|
max_content: length,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut pbm_lengths = Length::zero();
|
||||||
|
let mut pbm_percentages = Percentage::zero();
|
||||||
|
let padding = style.padding();
|
||||||
|
let border = style.border_width();
|
||||||
|
let margin = style.margin();
|
||||||
|
pbm_lengths += border.inline_sum();
|
||||||
|
let mut add = |x: LengthPercentage| {
|
||||||
|
pbm_lengths += x.length_component();
|
||||||
|
pbm_percentages += x.percentage_component();
|
||||||
|
};
|
||||||
|
add(padding.inline_start);
|
||||||
|
add(padding.inline_end);
|
||||||
|
margin.inline_start.non_auto().map(&mut add);
|
||||||
|
margin.inline_end.non_auto().map(&mut add);
|
||||||
|
|
||||||
|
outer.min_content += pbm_lengths;
|
||||||
|
outer.max_content += pbm_lengths;
|
||||||
|
|
||||||
|
(outer, pbm_percentages)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css2/visudet.html#shrink-to-fit-float
|
||||||
|
pub(crate) fn shrink_to_fit(&self, available_size: Length) -> Length {
|
||||||
|
let inline = self.expect_inline();
|
||||||
|
available_size
|
||||||
|
.max(inline.min_content)
|
||||||
|
.min(inline.max_content)
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,6 +45,9 @@ pub(crate) enum DisplayInside {
|
||||||
|
|
||||||
pub(crate) trait ComputedValuesExt {
|
pub(crate) trait ComputedValuesExt {
|
||||||
fn writing_mode(&self) -> (WritingMode, Direction);
|
fn writing_mode(&self) -> (WritingMode, Direction);
|
||||||
|
fn writing_mode_is_horizontal(&self) -> bool;
|
||||||
|
fn inline_size_is_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>;
|
||||||
fn min_box_size(&self) -> flow_relative::Vec2<LengthPercentageOrAuto>;
|
fn min_box_size(&self) -> flow_relative::Vec2<LengthPercentageOrAuto>;
|
||||||
|
@ -62,6 +65,39 @@ impl ComputedValuesExt for ComputedValues {
|
||||||
(writing_mode, direction)
|
(writing_mode, direction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn writing_mode_is_horizontal(&self) -> bool {
|
||||||
|
match self.get_inherited_box().writing_mode {
|
||||||
|
WritingMode::HorizontalTb => true,
|
||||||
|
WritingMode::VerticalLr | WritingMode::VerticalRl => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inline_size_is_auto(&self) -> bool {
|
||||||
|
let position = self.get_position();
|
||||||
|
let size = if self.writing_mode_is_horizontal() {
|
||||||
|
position.width
|
||||||
|
} else {
|
||||||
|
position.height
|
||||||
|
};
|
||||||
|
matches!(size, Size::Auto)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inline_box_offsets_are_both_non_auto(&self) -> bool {
|
||||||
|
let position = self.get_position();
|
||||||
|
let offsets = if self.writing_mode_is_horizontal() {
|
||||||
|
(position.left, position.right)
|
||||||
|
} else {
|
||||||
|
(position.top, position.bottom)
|
||||||
|
};
|
||||||
|
matches!(
|
||||||
|
offsets,
|
||||||
|
(
|
||||||
|
LengthPercentageOrAuto::LengthPercentage(_),
|
||||||
|
LengthPercentageOrAuto::LengthPercentage(_),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn box_offsets(&self) -> flow_relative::Sides<LengthPercentageOrAuto> {
|
fn box_offsets(&self) -> flow_relative::Sides<LengthPercentageOrAuto> {
|
||||||
let position = self.get_position();
|
let position = self.get_position();
|
||||||
|
|
|
@ -20,6 +20,10 @@ impl<'a> RecalcStyle<'a> {
|
||||||
RecalcStyle { context: context }
|
RecalcStyle { context: context }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn context(&self) -> &LayoutContext<'a> {
|
||||||
|
&self.context
|
||||||
|
}
|
||||||
|
|
||||||
pub fn destroy(self) -> LayoutContext<'a> {
|
pub fn destroy(self) -> LayoutContext<'a> {
|
||||||
self.context
|
self.context
|
||||||
}
|
}
|
||||||
|
|
|
@ -1081,9 +1081,9 @@ impl LayoutThread {
|
||||||
let box_tree = if token.should_traverse() {
|
let box_tree = if token.should_traverse() {
|
||||||
driver::traverse_dom(&traversal, token, Some(rayon_pool));
|
driver::traverse_dom(&traversal, token, Some(rayon_pool));
|
||||||
|
|
||||||
let shared = DomTraversal::<ServoLayoutElement>::shared_context(&traversal);
|
|
||||||
let root_node = document.root_element().unwrap().as_node();
|
let root_node = document.root_element().unwrap().as_node();
|
||||||
let box_tree = rayon_pool.install(|| BoxTreeRoot::construct(shared, root_node));
|
let box_tree =
|
||||||
|
rayon_pool.install(|| BoxTreeRoot::construct(traversal.context(), root_node));
|
||||||
Some(box_tree)
|
Some(box_tree)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
@ -170,6 +170,12 @@ impl LengthPercentage {
|
||||||
self.length
|
self.length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the percentage component of this `calc()`
|
||||||
|
#[inline]
|
||||||
|
pub fn percentage_component(&self) -> Percentage {
|
||||||
|
Percentage(self.clamping_mode.clamp(self.percentage.0))
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the percentage value as CSSFloat.
|
/// Return the percentage value as CSSFloat.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn percentage(&self) -> CSSFloat {
|
pub fn percentage(&self) -> CSSFloat {
|
||||||
|
@ -186,6 +192,16 @@ impl LengthPercentage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the length component if this could be represented as a
|
||||||
|
/// non-calc length.
|
||||||
|
pub fn as_length(&self) -> Option<Length> {
|
||||||
|
if !self.has_percentage {
|
||||||
|
Some(self.length_component())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the percentage component if this could be represented as a
|
/// Returns the percentage component if this could be represented as a
|
||||||
/// non-calc percentage.
|
/// non-calc percentage.
|
||||||
pub fn as_percentage(&self) -> Option<Percentage> {
|
pub fn as_percentage(&self) -> Option<Percentage> {
|
||||||
|
|
|
@ -64,6 +64,12 @@ impl Zero for Percentage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::ops::AddAssign for Percentage {
|
||||||
|
fn add_assign(&mut self, other: Self) {
|
||||||
|
self.0 += other.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ToCss for Percentage {
|
impl ToCss for Percentage {
|
||||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||||
where
|
where
|
||||||
|
|
|
@ -92,7 +92,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Maps the length of this value.
|
/// Maps the length of this value.
|
||||||
pub fn map(&self, f: impl FnOnce(LengthPercentage) -> LengthPercentage) -> Self {
|
pub fn map<T>(&self, f: impl FnOnce(LengthPercentage) -> T) -> LengthPercentageOrAuto<T> {
|
||||||
match self {
|
match self {
|
||||||
LengthPercentageOrAuto::LengthPercentage(l) => {
|
LengthPercentageOrAuto::LengthPercentage(l) => {
|
||||||
LengthPercentageOrAuto::LengthPercentage(f(l.clone()))
|
LengthPercentageOrAuto::LengthPercentage(f(l.clone()))
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[containing-block-008.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[containing-block-010.xht]
|
|
||||||
expected: FAIL
|
|
Loading…
Add table
Add a link
Reference in a new issue