Import victor's layout system 🍷

This commit is contained in:
Anthony Ramine 2019-09-09 17:21:26 +02:00
parent 86904757e6
commit 4444c5a2ad
25 changed files with 1270 additions and 712 deletions

View file

@ -6,6 +6,7 @@ Agoston Szepessy <agszepp@gmail.com>
Alexander Popiak <alex@zenguard.org> <alexander.popiak@gmail.com> Alexander Popiak <alex@zenguard.org> <alexander.popiak@gmail.com>
Alexandrov Sergey <splavgm@gmail.com> Alexandrov Sergey <splavgm@gmail.com>
Antonio de Luna <tonydelun@gmail.com> Antonio de Luna <tonydelun@gmail.com>
Anthony Ramine <nox@nox.paris> <n.oxyde@gmail.com>
Askeing Yen (fyen) <askeing@gmail.com> Askeing Yen (fyen) <askeing@gmail.com>
Attila Dusnoki <dati91@gmail.com> <dati91@users.noreply.github.com> Attila Dusnoki <dati91@gmail.com> <dati91@users.noreply.github.com>
bd339 <bd339@tuta.io> <bd339@localhost.localdomain> bd339 <bd339@tuta.io> <bd339@localhost.localdomain>

21
Cargo.lock generated
View file

@ -2321,6 +2321,8 @@ name = "layout_2020"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.25.8 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"gfx 0.0.1", "gfx 0.0.1",
@ -2328,12 +2330,15 @@ dependencies = [
"ipc-channel 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"malloc_size_of 0.0.1", "malloc_size_of 0.0.1",
"matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"msg 0.0.1", "msg 0.0.1",
"range 0.0.1", "range 0.0.1",
"rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon_croissant 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"script_layout_interface 0.0.1", "script_layout_interface 0.0.1",
"script_traits 0.0.1", "script_traits 0.0.1",
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
"servo_arc 0.1.1",
"servo_url 0.0.1", "servo_url 0.0.1",
"style 0.0.1", "style 0.0.1",
"style_traits 0.0.1", "style_traits 0.0.1",
@ -2901,6 +2906,11 @@ name = "mitochondria"
version = "1.1.2" version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "moite_moite"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "mozangle" name = "mozangle"
version = "0.2.5" version = "0.2.5"
@ -3692,6 +3702,15 @@ dependencies = [
"num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "rayon_croissant"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"moite_moite 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "rdrand" name = "rdrand"
version = "0.4.0" version = "0.4.0"
@ -6052,6 +6071,7 @@ dependencies = [
"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" "checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125"
"checksum miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226" "checksum miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226"
"checksum mitochondria 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9de3eca27871df31c33b807f834b94ef7d000956f57aa25c5aed9c5f0aae8f6f" "checksum mitochondria 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9de3eca27871df31c33b807f834b94ef7d000956f57aa25c5aed9c5f0aae8f6f"
"checksum moite_moite 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb5a94c61e12e2cfc16ee3e2b6eca8f126a43c888586626337544a7e824a1af"
"checksum mozangle 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0643469d6622a4ffae6a74f13e78a32bfa1406fe8a63010b3fbf2946797fdac7" "checksum mozangle 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0643469d6622a4ffae6a74f13e78a32bfa1406fe8a63010b3fbf2946797fdac7"
"checksum mozjs 0.12.0 (git+https://github.com/servo/rust-mozjs)" = "<none>" "checksum mozjs 0.12.0 (git+https://github.com/servo/rust-mozjs)" = "<none>"
"checksum mozjs_sys 0.67.0 (git+https://github.com/servo/mozjs?rev=2bf4912a6ff1f5ea333afb1cb53dfe9361157b14)" = "<none>" "checksum mozjs_sys 0.67.0 (git+https://github.com/servo/mozjs?rev=2bf4912a6ff1f5ea333afb1cb53dfe9361157b14)" = "<none>"
@ -6122,6 +6142,7 @@ dependencies = [
"checksum raqote 0.6.2-alpha.0 (git+https://github.com/jrmuizel/raqote)" = "<none>" "checksum raqote 0.6.2-alpha.0 (git+https://github.com/jrmuizel/raqote)" = "<none>"
"checksum rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83a27732a533a1be0a0035a111fe76db89ad312f6f0347004c220c57f209a123" "checksum rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83a27732a533a1be0a0035a111fe76db89ad312f6f0347004c220c57f209a123"
"checksum rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98dcf634205083b17d0861252431eb2acbfb698ab7478a2d20de07954f47ec7b" "checksum rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98dcf634205083b17d0861252431eb2acbfb698ab7478a2d20de07954f47ec7b"
"checksum rayon_croissant 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5b725e815f3aa08718063883a75003336889debafe2f8fa67fbe91563ddc4efa"
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
"checksum redox_syscall 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "29dbdfd4b9df8ab31dec47c6087b7b13cbf4a776f335e4de8efba8288dda075b" "checksum redox_syscall 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "29dbdfd4b9df8ab31dec47c6087b7b13cbf4a776f335e4de8efba8288dda075b"
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"

View file

@ -502,7 +502,7 @@ pub fn style_length(style_length: Size, container_size: Option<Au>) -> MaybeAuto
match style_length { match style_length {
Size::Auto => MaybeAuto::Auto, Size::Auto => MaybeAuto::Auto,
Size::LengthPercentage(ref lp) => { Size::LengthPercentage(ref lp) => {
MaybeAuto::from_option(lp.0.maybe_to_used_value(container_size)) MaybeAuto::from_option(lp.0.maybe_to_used_value(container_size.map(|l| l.into())))
}, },
} }
} }

View file

@ -14,6 +14,8 @@ doctest = false
[dependencies] [dependencies]
app_units = "0.7" app_units = "0.7"
atomic_refcell = "0.1"
cssparser = "0.25"
euclid = "0.20" euclid = "0.20"
fnv = "1.0" fnv = "1.0"
gfx = {path = "../gfx"} gfx = {path = "../gfx"}
@ -21,12 +23,15 @@ gfx_traits = {path = "../gfx_traits"}
ipc-channel = "0.12" ipc-channel = "0.12"
libc = "0.2" libc = "0.2"
malloc_size_of = { path = "../malloc_size_of" } malloc_size_of = { path = "../malloc_size_of" }
matches = "0.1"
msg = {path = "../msg"} msg = {path = "../msg"}
range = {path = "../range"} range = {path = "../range"}
rayon = "1" rayon = "1"
rayon_croissant = "0.1.1"
script_layout_interface = {path = "../script_layout_interface"} script_layout_interface = {path = "../script_layout_interface"}
script_traits = {path = "../script_traits"} script_traits = {path = "../script_traits"}
serde = "1.0" serde = "1.0"
servo_arc = { path = "../servo_arc" }
servo_url = {path = "../url"} servo_url = {path = "../url"}
style = {path = "../style", features = ["servo", "servo-layout-2020"]} style = {path = "../style", features = ["servo", "servo-layout-2020"]}
style_traits = {path = "../style_traits"} style_traits = {path = "../style_traits"}

View file

@ -2,12 +2,15 @@
* 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 gfx::font_cache_thread::FontCacheThread;
use msg::constellation_msg::PipelineId; use msg::constellation_msg::PipelineId;
use std::sync::Mutex;
use style::context::SharedStyleContext; use style::context::SharedStyleContext;
pub struct LayoutContext<'a> { pub struct LayoutContext<'a> {
pub id: PipelineId, pub id: PipelineId,
pub style_context: SharedStyleContext<'a>, pub style_context: SharedStyleContext<'a>,
pub font_cache_thread: Mutex<FontCacheThread>,
} }
impl<'a> LayoutContext<'a> { impl<'a> LayoutContext<'a> {

View file

@ -2,17 +2,21 @@
* 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::element_data::LayoutDataForElement;
use atomic_refcell::AtomicRefCell;
use script_layout_interface::StyleData; use script_layout_interface::StyleData;
#[repr(C)] #[repr(C)]
pub struct StyleAndLayoutData { pub struct StyleAndLayoutData {
pub style_data: StyleData, pub style_data: StyleData,
pub(super) layout_data: AtomicRefCell<LayoutDataForElement>,
} }
impl StyleAndLayoutData { impl StyleAndLayoutData {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
style_data: StyleData::new(), style_data: StyleData::new(),
layout_data: Default::default(),
} }
} }
} }

View file

@ -1,22 +1,28 @@
use super::*; /* This Source Code Form is subject to the terms of the Mozilla Public
use crate::dom::{Document, NodeData, NodeId}; * License, v. 2.0. If a copy of the MPL was not distributed with this
use crate::style::StyleSet; * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::element_data::{LayoutBox, LayoutDataForElement};
use crate::replaced::ReplacedContent;
use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside, DisplayOutside};
use crate::wrapper::GetRawData;
use atomic_refcell::AtomicRefMut; use atomic_refcell::AtomicRefMut;
use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode};
use servo_arc::Arc;
use std::convert::TryInto;
use style::context::SharedStyleContext;
use style::dom::TNode;
use style::properties::ComputedValues;
pub(super) struct Context<'a> { #[derive(Clone, Copy)]
pub document: &'a Document,
pub author_styles: &'a StyleSet,
}
#[derive(Copy, Clone)]
pub(super) enum WhichPseudoElement { pub(super) enum WhichPseudoElement {
Before, Before,
After, After,
} }
pub(super) enum Contents { pub(super) enum Contents<Node> {
/// Refers to a DOM subtree, plus `::before` and `::after` pseudo-elements. /// Refers to a DOM subtree, plus `::before` and `::after` pseudo-elements.
OfElement(NodeId), OfElement(Node),
/// Example: an `<img src=…>` element. /// Example: an `<img src=…>` element.
/// <https://drafts.csswg.org/css2/conform.html#replaced-element> /// <https://drafts.csswg.org/css2/conform.html#replaced-element>
@ -27,8 +33,8 @@ pub(super) enum Contents {
OfPseudoElement(Vec<PseudoElementContentItem>), OfPseudoElement(Vec<PseudoElementContentItem>),
} }
pub(super) enum NonReplacedContents { pub(super) enum NonReplacedContents<Node> {
OfElement(NodeId), OfElement(Node),
OfPseudoElement(Vec<PseudoElementContentItem>), OfPseudoElement(Vec<PseudoElementContentItem>),
} }
@ -37,206 +43,196 @@ pub(super) enum PseudoElementContentItem {
Replaced(ReplacedContent), Replaced(ReplacedContent),
} }
pub(super) trait TraversalHandler<'dom> { pub(super) trait TraversalHandler<Node> {
fn handle_text(&mut self, text: &str, parent_style: &Arc<ComputedValues>); fn handle_text(&mut self, text: String, parent_style: &Arc<ComputedValues>);
/// Or pseudo-element /// Or pseudo-element
fn handle_element( fn handle_element(
&mut self, &mut self,
style: &Arc<ComputedValues>, style: &Arc<ComputedValues>,
display: DisplayGeneratingBox, display: DisplayGeneratingBox,
contents: Contents, contents: Contents<Node>,
box_slot: BoxSlot<'dom>, box_slot: BoxSlot,
); );
} }
fn traverse_children_of<'dom>( fn traverse_children_of<'dom, Node>(
parent_element: NodeId, parent_element: Node,
parent_element_style: &Arc<ComputedValues>, context: &SharedStyleContext,
context: &'dom Context, handler: &mut impl TraversalHandler<Node>,
handler: &mut impl TraversalHandler<'dom>, ) where
) { Node: NodeExt<'dom>,
traverse_pseudo_element( {
WhichPseudoElement::Before, traverse_pseudo_element(WhichPseudoElement::Before, parent_element, context, handler);
parent_element,
parent_element_style,
context,
handler,
);
let mut next = context.document[parent_element].first_child; let mut next = parent_element.first_child();
while let Some(child) = next { while let Some(child) = next {
match &context.document[child].data { if let Some(contents) = child.as_text() {
NodeData::Document handler.handle_text(contents, &child.style(context));
| NodeData::Doctype { .. } } else if child.is_element() {
| NodeData::Comment { .. } traverse_element(child, context, handler);
| NodeData::ProcessingInstruction { .. } => {}
NodeData::Text { contents } => {
handler.handle_text(contents, parent_element_style);
}
NodeData::Element(_) => traverse_element(child, parent_element_style, context, handler),
} }
next = context.document[child].next_sibling next = child.next_sibling();
} }
traverse_pseudo_element( traverse_pseudo_element(WhichPseudoElement::After, parent_element, context, handler);
WhichPseudoElement::After,
parent_element,
&parent_element_style,
context,
handler,
);
} }
fn traverse_element<'dom>( fn traverse_element<'dom, Node>(
element_id: NodeId, element: Node,
parent_element_style: &ComputedValues, context: &SharedStyleContext,
context: &'dom Context, handler: &mut impl TraversalHandler<Node>,
handler: &mut impl TraversalHandler<'dom>, ) where
) { Node: NodeExt<'dom>,
let style = style_for_element( {
context.author_styles, let style = element.style(context);
context.document, match Display::from(style.get_box().display) {
element_id, Display::None => element.unset_boxes_in_subtree(),
Some(parent_element_style),
);
match style.box_.display {
Display::None => context.unset_boxes_in_subtree(element_id),
Display::Contents => { Display::Contents => {
if ReplacedContent::for_element(element_id, context).is_some() { if ReplacedContent::for_element(element, context).is_some() {
// `display: content` on a replaced element computes to `display: none` // `display: content` on a replaced element computes to `display: none`
// <https://drafts.csswg.org/css-display-3/#valdef-display-contents> // <https://drafts.csswg.org/css-display-3/#valdef-display-contents>
context.unset_boxes_in_subtree(element_id) element.unset_boxes_in_subtree()
} else { } else {
context.layout_data_mut(element_id).self_box = Some(LayoutBox::DisplayContents); element.layout_data_mut().self_box = Some(LayoutBox::DisplayContents);
traverse_children_of(element_id, &style, context, handler) traverse_children_of(element, context, handler)
} }
} },
Display::GeneratingBox(display) => handler.handle_element( Display::GeneratingBox(display) => {
&style, handler.handle_element(
display, &style,
match ReplacedContent::for_element(element_id, context) { display,
Some(replaced) => Contents::Replaced(replaced), match ReplacedContent::for_element(element, context) {
None => Contents::OfElement(element_id), Some(replaced) => Contents::Replaced(replaced),
}, None => Contents::OfElement(element),
context.element_box_slot(element_id), },
), element.element_box_slot(),
);
},
} }
} }
fn traverse_pseudo_element<'dom>( fn traverse_pseudo_element<'dom, Node>(
which: WhichPseudoElement, which: WhichPseudoElement,
element: NodeId, element: Node,
element_style: &ComputedValues, context: &SharedStyleContext,
context: &'dom Context, handler: &mut impl TraversalHandler<Node>,
handler: &mut impl TraversalHandler<'dom>, ) where
) { Node: NodeExt<'dom>,
if let Some(style) = pseudo_element_style(which, element, element_style, context) { {
match style.box_.display { if let Some(style) = pseudo_element_style(which, element, context) {
Display::None => context.unset_pseudo_element_box(element, which), match Display::from(style.get_box().display) {
Display::None => element.unset_pseudo_element_box(which),
Display::Contents => { Display::Contents => {
context.unset_pseudo_element_box(element, which); element.unset_pseudo_element_box(which);
let items = generate_pseudo_element_content(&style, element, context); let items = generate_pseudo_element_content(&style, element, context);
traverse_pseudo_element_contents(&style, items, handler); traverse_pseudo_element_contents(&style, items, handler);
} },
Display::GeneratingBox(display) => { Display::GeneratingBox(display) => {
let items = generate_pseudo_element_content(&style, element, context); let items = generate_pseudo_element_content(&style, element, context);
let contents = Contents::OfPseudoElement(items); let contents = Contents::OfPseudoElement(items);
let box_slot = context.pseudo_element_box_slot(element, which); let box_slot = element.pseudo_element_box_slot(which);
handler.handle_element(&style, display, contents, box_slot); handler.handle_element(&style, display, contents, box_slot);
} },
} }
} }
} }
fn traverse_pseudo_element_contents<'dom>( fn traverse_pseudo_element_contents<'dom, Node>(
pseudo_element_style: &Arc<ComputedValues>, pseudo_element_style: &Arc<ComputedValues>,
items: Vec<PseudoElementContentItem>, items: Vec<PseudoElementContentItem>,
handler: &mut impl TraversalHandler<'dom>, handler: &mut impl TraversalHandler<Node>,
) { ) where
let mut anonymous_style = None; Node: 'dom,
{
// let mut anonymous_style = None;
for item in items { for item in items {
match item { match item {
PseudoElementContentItem::Text(text) => { PseudoElementContentItem::Text(text) => handler.handle_text(text, pseudo_element_style),
handler.handle_text(&text, pseudo_element_style)
}
PseudoElementContentItem::Replaced(contents) => { PseudoElementContentItem::Replaced(contents) => {
let item_style = anonymous_style.get_or_insert_with(|| { // FIXME
ComputedValues::anonymous_inheriting_from(Some(pseudo_element_style)) // let item_style = anonymous_style.get_or_insert_with(|| {
}); // ComputedValues::anonymous_inheriting_from(Some(pseudo_element_style))
let display_inline = DisplayGeneratingBox::OutsideInside { // });
outside: DisplayOutside::Inline, // let display_inline = DisplayGeneratingBox::OutsideInside {
inside: DisplayInside::Flow, // outside: DisplayOutside::Inline,
}; // inside: DisplayInside::Flow,
// `display` is not inherited, so we get the initial value // };
debug_assert!(item_style.box_.display == Display::GeneratingBox(display_inline)); // // `display` is not inherited, so we get the initial value
handler.handle_element( // debug_assert!(item_style.box_.display == Display::GeneratingBox(display_inline));
item_style, // handler.handle_element(
display_inline, // item_style,
Contents::Replaced(contents), // display_inline,
// We dont keep pointers to boxes generated by contents of pseudo-elements // Contents::Replaced(contents),
BoxSlot::dummy(), // // We dont keep pointers to boxes generated by contents of pseudo-elements
) // BoxSlot::dummy(),
} // )
},
} }
} }
} }
impl std::convert::TryFrom<Contents> for NonReplacedContents { impl<Node> std::convert::TryFrom<Contents<Node>> for NonReplacedContents<Node> {
type Error = ReplacedContent; type Error = ReplacedContent;
fn try_from(contents: Contents) -> Result<Self, Self::Error> { fn try_from(contents: Contents<Node>) -> Result<Self, Self::Error> {
match contents { match contents {
Contents::OfElement(id) => Ok(NonReplacedContents::OfElement(id)), Contents::OfElement(node) => Ok(NonReplacedContents::OfElement(node)),
Contents::OfPseudoElement(items) => Ok(NonReplacedContents::OfPseudoElement(items)), Contents::OfPseudoElement(items) => Ok(NonReplacedContents::OfPseudoElement(items)),
Contents::Replaced(replaced) => Err(replaced), Contents::Replaced(replaced) => Err(replaced),
} }
} }
} }
impl std::convert::From<NonReplacedContents> for Contents { impl<Node> std::convert::From<NonReplacedContents<Node>> for Contents<Node> {
fn from(contents: NonReplacedContents) -> Self { fn from(contents: NonReplacedContents<Node>) -> Self {
match contents { match contents {
NonReplacedContents::OfElement(id) => Contents::OfElement(id), NonReplacedContents::OfElement(node) => Contents::OfElement(node),
NonReplacedContents::OfPseudoElement(items) => Contents::OfPseudoElement(items), NonReplacedContents::OfPseudoElement(items) => Contents::OfPseudoElement(items),
} }
} }
} }
impl NonReplacedContents { impl<'dom, Node> NonReplacedContents<Node>
pub fn traverse<'dom>( where
Node: NodeExt<'dom>,
{
pub(crate) fn traverse(
self, self,
inherited_style: &Arc<ComputedValues>, inherited_style: &Arc<ComputedValues>,
context: &'dom Context, context: &SharedStyleContext,
handler: &mut impl TraversalHandler<'dom>, handler: &mut impl TraversalHandler<Node>,
) { ) {
match self { match self {
NonReplacedContents::OfElement(id) => { NonReplacedContents::OfElement(node) => traverse_children_of(node, context, handler),
traverse_children_of(id, inherited_style, context, handler)
}
NonReplacedContents::OfPseudoElement(items) => { NonReplacedContents::OfPseudoElement(items) => {
traverse_pseudo_element_contents(inherited_style, items, handler) traverse_pseudo_element_contents(inherited_style, items, handler)
} },
} }
} }
} }
fn pseudo_element_style( fn pseudo_element_style<'dom, Node>(
_which: WhichPseudoElement, _which: WhichPseudoElement,
_element: NodeId, _element: Node,
_element_style: &ComputedValues, _context: &SharedStyleContext,
_context: &Context, ) -> Option<Arc<ComputedValues>>
) -> Option<Arc<ComputedValues>> { where
Node: NodeExt<'dom>,
{
// FIXME: run the cascade, then return None for `content: normal` or `content: none` // FIXME: run the cascade, then return None for `content: normal` or `content: none`
// https://drafts.csswg.org/css2/generate.html#content // https://drafts.csswg.org/css2/generate.html#content
None None
} }
fn generate_pseudo_element_content( fn generate_pseudo_element_content<'dom, Node>(
_pseudo_element_style: &ComputedValues, _pseudo_element_style: &ComputedValues,
_element: NodeId, _element: Node,
_context: &Context, _context: &SharedStyleContext,
) -> Vec<PseudoElementContentItem> { ) -> Vec<PseudoElementContentItem>
where
Node: NodeExt<'dom>,
{
let _ = PseudoElementContentItem::Text; let _ = PseudoElementContentItem::Text;
let _ = PseudoElementContentItem::Replaced; let _ = PseudoElementContentItem::Replaced;
unimplemented!() unimplemented!()
@ -271,37 +267,77 @@ impl Drop for BoxSlot<'_> {
} }
} }
impl Context<'_> { pub(crate) trait NodeExt<'dom>: 'dom + Copy + Send + Sync {
fn layout_data_mut(&self, element_id: NodeId) -> AtomicRefMut<LayoutDataForElement> { fn is_element(self) -> bool;
self.document[element_id] fn as_text(self) -> Option<String>;
.as_element() fn first_child(self) -> Option<Self>;
fn next_sibling(self) -> Option<Self>;
fn parent_node(self) -> Option<Self>;
fn style(self, context: &SharedStyleContext) -> Arc<ComputedValues>;
fn layout_data_mut(&self) -> AtomicRefMut<LayoutDataForElement>;
fn element_box_slot(&self) -> BoxSlot;
fn pseudo_element_box_slot(&self, which: WhichPseudoElement) -> BoxSlot;
fn unset_pseudo_element_box(self, which: WhichPseudoElement);
fn unset_boxes_in_subtree(self);
}
impl<'dom, T> NodeExt<'dom> for T
where
T: 'dom + LayoutNode + Send + Sync,
{
fn is_element(self) -> bool {
self.to_threadsafe().as_element().is_some()
}
fn as_text(self) -> Option<String> {
if self.is_text_node() {
Some(self.to_threadsafe().node_text_content())
} else {
None
}
}
fn first_child(self) -> Option<Self> {
TNode::first_child(&self)
}
fn next_sibling(self) -> Option<Self> {
TNode::next_sibling(&self)
}
fn parent_node(self) -> Option<Self> {
TNode::next_sibling(&self)
}
fn style(self, context: &SharedStyleContext) -> Arc<ComputedValues> {
self.to_threadsafe().style(context)
}
fn layout_data_mut(&self) -> AtomicRefMut<LayoutDataForElement> {
self.get_raw_data()
.map(|d| d.layout_data.borrow_mut())
.unwrap() .unwrap()
.layout_data
.borrow_mut()
} }
fn element_box_slot(&self, element_id: NodeId) -> BoxSlot { fn element_box_slot(&self) -> BoxSlot {
BoxSlot::new(AtomicRefMut::map( BoxSlot::new(AtomicRefMut::map(self.layout_data_mut(), |data| {
self.layout_data_mut(element_id), &mut data.self_box
|data| &mut data.self_box, }))
))
} }
fn pseudo_element_box_slot(&self, element_id: NodeId, which: WhichPseudoElement) -> BoxSlot { fn pseudo_element_box_slot(&self, which: WhichPseudoElement) -> BoxSlot {
BoxSlot::new(AtomicRefMut::map( BoxSlot::new(AtomicRefMut::map(self.layout_data_mut(), |data| {
self.layout_data_mut(element_id), let pseudos = data.pseudo_elements.get_or_insert_with(Default::default);
|data| { match which {
let pseudos = data.pseudo_elements.get_or_insert_with(Default::default); WhichPseudoElement::Before => &mut pseudos.before,
match which { WhichPseudoElement::After => &mut pseudos.after,
WhichPseudoElement::Before => &mut pseudos.before, }
WhichPseudoElement::After => &mut pseudos.after, }))
}
},
))
} }
fn unset_pseudo_element_box(&self, element_id: NodeId, which: WhichPseudoElement) { fn unset_pseudo_element_box(self, which: WhichPseudoElement) {
if let Some(pseudos) = &mut self.layout_data_mut(element_id).pseudo_elements { if let Some(pseudos) = &mut self.layout_data_mut().pseudo_elements {
match which { match which {
WhichPseudoElement::Before => pseudos.before = None, WhichPseudoElement::Before => pseudos.before = None,
WhichPseudoElement::After => pseudos.after = None, WhichPseudoElement::After => pseudos.after = None,
@ -309,34 +345,36 @@ impl Context<'_> {
} }
} }
fn unset_boxes_in_subtree(&self, base_element: NodeId) { fn unset_boxes_in_subtree(self) {
let mut node_id = base_element; let mut node = self;
loop { loop {
let node = &self.document[node_id]; if node.is_element() {
if let Some(element_data) = node.as_element() { let traverse_children = {
let mut layout_data = element_data.layout_data.borrow_mut(); let mut layout_data = node.layout_data_mut();
layout_data.pseudo_elements = None; layout_data.pseudo_elements = None;
if layout_data.self_box.take().is_some() { layout_data.self_box.take().is_some()
};
if traverse_children {
// Only descend into children if we removed a box. // Only descend into children if we removed a box.
// If there wasnt one, then descendants dont have boxes either. // If there wasnt one, then descendants dont have boxes either.
if let Some(child) = node.first_child { if let Some(child) = node.first_child() {
node_id = child; node = child;
continue; continue;
} }
} }
} }
let mut next_is_a_sibling_of = node_id; let mut next_is_a_sibling_of = node;
node_id = loop { node = loop {
if let Some(sibling) = self.document[next_is_a_sibling_of].next_sibling { if let Some(sibling) = next_is_a_sibling_of.next_sibling() {
break sibling; break sibling;
} else { } else {
next_is_a_sibling_of = node next_is_a_sibling_of = node
.parent .parent_node()
.expect("reached the root while traversing only a subtree"); .expect("reached the root while traversing only a subtree");
} }
}; };
if next_is_a_sibling_of == base_element { if next_is_a_sibling_of == self {
// Dont go outside the subtree // Dont go outside the subtree.
return; return;
} }
} }

View file

@ -1,4 +1,10 @@
use super::*; /* 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/. */
use crate::flow::inline::InlineLevelBox;
use crate::flow::BlockLevelBox;
use servo_arc::Arc;
#[derive(Default)] #[derive(Default)]
pub(crate) struct LayoutDataForElement { pub(crate) struct LayoutDataForElement {

View file

@ -1,10 +1,27 @@
use super::*; /* 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/. */
use crate::dom_traversal::{BoxSlot, Contents, NodeExt, NonReplacedContents, TraversalHandler};
use crate::element_data::LayoutBox;
use crate::flow::float::FloatBox;
use crate::flow::inline::{InlineBox, InlineFormattingContext, InlineLevelBox, TextRun};
use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox};
use crate::positioned::AbsolutelyPositionedBox;
use crate::style_ext::{DisplayGeneratingBox, DisplayInside, DisplayOutside};
use crate::IndependentFormattingContext;
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use rayon_croissant::ParallelIteratorExt;
use servo_arc::Arc;
use std::convert::TryInto;
use style::context::SharedStyleContext;
use style::properties::ComputedValues;
impl BlockFormattingContext { impl BlockFormattingContext {
pub fn construct<'a>( pub fn construct<'dom>(
context: &'a Context<'a>, context: &SharedStyleContext<'_>,
style: &'a Arc<ComputedValues>, style: &Arc<ComputedValues>,
contents: NonReplacedContents, contents: NonReplacedContents<impl NodeExt<'dom>>,
) -> Self { ) -> Self {
let (contents, contains_floats) = BlockContainer::construct(context, style, contents); let (contents, contains_floats) = BlockContainer::construct(context, style, contents);
Self { Self {
@ -14,25 +31,25 @@ impl BlockFormattingContext {
} }
} }
enum IntermediateBlockLevelBox { enum IntermediateBlockLevelBox<Node> {
SameFormattingContextBlock { SameFormattingContextBlock {
style: Arc<ComputedValues>, style: Arc<ComputedValues>,
contents: IntermediateBlockContainer, contents: IntermediateBlockContainer<Node>,
}, },
Independent { Independent {
style: Arc<ComputedValues>, style: Arc<ComputedValues>,
display_inside: DisplayInside, display_inside: DisplayInside,
contents: Contents, contents: Contents<Node>,
}, },
OutOfFlowAbsolutelyPositionedBox { OutOfFlowAbsolutelyPositionedBox {
style: Arc<ComputedValues>, style: Arc<ComputedValues>,
display_inside: DisplayInside, display_inside: DisplayInside,
contents: Contents, contents: Contents<Node>,
}, },
OutOfFlowFloatBox { OutOfFlowFloatBox {
style: Arc<ComputedValues>, style: Arc<ComputedValues>,
display_inside: DisplayInside, display_inside: DisplayInside,
contents: Contents, contents: Contents<Node>,
}, },
} }
@ -43,18 +60,19 @@ enum IntermediateBlockLevelBox {
/// of a given element. /// of a given element.
/// ///
/// Deferring allows using rayons `into_par_iter`. /// Deferring allows using rayons `into_par_iter`.
enum IntermediateBlockContainer { enum IntermediateBlockContainer<Node> {
InlineFormattingContext(InlineFormattingContext), InlineFormattingContext(InlineFormattingContext),
Deferred { contents: NonReplacedContents }, Deferred { contents: NonReplacedContents<Node> },
} }
/// A builder for a block container. /// A builder for a block container.
/// ///
/// 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<'a> { struct BlockContainerBuilder<'dom, 'style, Node> {
context: &'a Context<'a>, context: &'style SharedStyleContext<'style>,
block_container_style: &'a Arc<ComputedValues>,
block_container_style: &'style Arc<ComputedValues>,
/// The list of block-level boxes of the final block container. /// The list of block-level boxes of the final block container.
/// ///
@ -69,7 +87,7 @@ struct BlockContainerBuilder<'a> {
/// doesn't have a next sibling, we either reached the end of the container /// doesn't have a next sibling, we either reached the end of the container
/// root or there are ongoing inline-level boxes /// root or there are ongoing inline-level boxes
/// (see `handle_block_level_element`). /// (see `handle_block_level_element`).
block_level_boxes: Vec<(IntermediateBlockLevelBox, BoxSlot<'a>)>, block_level_boxes: Vec<(IntermediateBlockLevelBox<Node>, BoxSlot<'dom>)>,
/// The ongoing inline formatting context of the builder. /// The ongoing inline formatting context of the builder.
/// ///
@ -104,10 +122,10 @@ struct BlockContainerBuilder<'a> {
} }
impl BlockContainer { impl BlockContainer {
pub fn construct<'a>( pub fn construct<'dom, 'style>(
context: &'a Context<'a>, context: &SharedStyleContext<'style>,
block_container_style: &'a Arc<ComputedValues>, block_container_style: &Arc<ComputedValues>,
contents: NonReplacedContents, contents: NonReplacedContents<impl NodeExt<'dom>>,
) -> (BlockContainer, ContainsFloats) { ) -> (BlockContainer, ContainsFloats) {
let mut builder = BlockContainerBuilder { let mut builder = BlockContainerBuilder {
context, context,
@ -134,7 +152,8 @@ impl BlockContainer {
); );
return (container, builder.contains_floats); return (container, builder.contains_floats);
} }
builder.end_ongoing_inline_formatting_context(); // FIXME
// builder.end_ongoing_inline_formatting_context();
} }
let mut contains_floats = builder.contains_floats; let mut contains_floats = builder.contains_floats;
@ -144,10 +163,11 @@ impl BlockContainer {
.into_par_iter() .into_par_iter()
.mapfold_reduce_into( .mapfold_reduce_into(
&mut contains_floats, &mut contains_floats,
|contains_floats, (intermediate, box_slot)| { |contains_floats, (intermediate, box_slot): (IntermediateBlockLevelBox<_>, BoxSlot<'_>)| {
let (block_level_box, box_contains_floats) = intermediate.finish(context); let (block_level_box, box_contains_floats) = intermediate.finish(context);
*contains_floats |= box_contains_floats; *contains_floats |= box_contains_floats;
box_slot.set(LayoutBox::BlockLevel(block_level_box.clone())); // FIXME
// box_slot.set(LayoutBox::BlockLevel(block_level_box.clone()));
block_level_box block_level_box
}, },
|left, right| *left |= right, |left, right| *left |= right,
@ -158,13 +178,16 @@ impl BlockContainer {
} }
} }
impl<'a> TraversalHandler<'a> for BlockContainerBuilder<'a> { impl<'dom, Node> TraversalHandler<Node> for BlockContainerBuilder<'dom, '_, Node>
where
Node: NodeExt<'dom>,
{
fn handle_element( fn handle_element(
&mut self, &mut self,
style: &Arc<ComputedValues>, style: &Arc<ComputedValues>,
display: DisplayGeneratingBox, display: DisplayGeneratingBox,
contents: Contents, contents: Contents<Node>,
box_slot: BoxSlot<'a>, box_slot: BoxSlot,
) { ) {
match display { match display {
DisplayGeneratingBox::OutsideInside { outside, inside } => match outside { DisplayGeneratingBox::OutsideInside { outside, inside } => match outside {
@ -172,27 +195,29 @@ impl<'a> TraversalHandler<'a> for BlockContainerBuilder<'a> {
self.handle_inline_level_element(style, inside, contents), self.handle_inline_level_element(style, inside, contents),
)), )),
DisplayOutside::Block => { DisplayOutside::Block => {
// FIXME
// Floats and abspos cause blockification, so they only happen in this case. // Floats and abspos cause blockification, so they only happen in this case.
// https://drafts.csswg.org/css2/visuren.html#dis-pos-flo // https://drafts.csswg.org/css2/visuren.html#dis-pos-flo
if style.box_.position.is_absolutely_positioned() { // if style.box_.position.is_absolutely_positioned() {
self.handle_absolutely_positioned_element( // self.handle_absolutely_positioned_element(
style.clone(), // style.clone(),
inside, // inside,
contents, // contents,
box_slot, // box_slot,
) // )
} else if style.box_.float.is_floating() { // } else if style.box_.float.is_floating() {
self.handle_float_element(style.clone(), inside, contents, box_slot) // self.handle_float_element(style.clone(), inside, contents, box_slot)
} else { // } else {
self.handle_block_level_element(style.clone(), inside, contents, box_slot) // self.handle_block_level_element(style.clone(), inside, contents, box_slot)
} // }
} },
DisplayOutside::None => panic!(":("),
}, },
} }
} }
fn handle_text(&mut self, input: &str, parent_style: &Arc<ComputedValues>) { fn handle_text(&mut self, input: String, parent_style: &Arc<ComputedValues>) {
let (leading_whitespace, mut input) = self.handle_leading_whitespace(input); let (leading_whitespace, mut input) = self.handle_leading_whitespace(&input);
if leading_whitespace || !input.is_empty() { if leading_whitespace || !input.is_empty() {
// This text node should be pushed either to the next ongoing // This text node should be pushed either to the next ongoing
// inline level box with the parent style of that inline level box // inline level box with the parent style of that inline level box
@ -256,7 +281,10 @@ impl<'a> TraversalHandler<'a> for BlockContainerBuilder<'a> {
} }
} }
impl<'a> BlockContainerBuilder<'a> { impl<'dom, Node> BlockContainerBuilder<'dom, '_, Node>
where
Node: NodeExt<'dom>,
{
/// Returns: /// Returns:
/// ///
/// * Whether this text run has preserved (non-collapsible) leading whitespace /// * Whether this text run has preserved (non-collapsible) leading whitespace
@ -273,19 +301,19 @@ impl<'a> BlockContainerBuilder<'a> {
match inline_level_boxes.next().map(|b| &**b) { match inline_level_boxes.next().map(|b| &**b) {
Some(InlineLevelBox::TextRun(r)) => break !r.text.ends_with(' '), Some(InlineLevelBox::TextRun(r)) => break !r.text.ends_with(' '),
Some(InlineLevelBox::Atomic { .. }) => break false, Some(InlineLevelBox::Atomic { .. }) => break false,
Some(InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(_)) Some(InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(_)) |
| Some(InlineLevelBox::OutOfFlowFloatBox(_)) => {} Some(InlineLevelBox::OutOfFlowFloatBox(_)) => {},
Some(InlineLevelBox::InlineBox(b)) => { Some(InlineLevelBox::InlineBox(b)) => {
stack.push(inline_level_boxes); stack.push(inline_level_boxes);
inline_level_boxes = b.children.iter().rev() inline_level_boxes = b.children.iter().rev()
} },
None => { None => {
if let Some(iter) = stack.pop() { if let Some(iter) = stack.pop() {
inline_level_boxes = iter inline_level_boxes = iter
} else { } else {
break false; // Paragraph start break false; // Paragraph start
} }
} },
} }
}; };
let text = text.trim_start_matches(|c: char| c.is_ascii_whitespace()); let text = text.trim_start_matches(|c: char| c.is_ascii_whitespace());
@ -296,7 +324,7 @@ impl<'a> BlockContainerBuilder<'a> {
&mut self, &mut self,
style: &Arc<ComputedValues>, style: &Arc<ComputedValues>,
display_inside: DisplayInside, display_inside: DisplayInside,
contents: Contents, contents: Contents<Node>,
) -> Arc<InlineLevelBox> { ) -> Arc<InlineLevelBox> {
let box_ = match contents.try_into() { let box_ = match contents.try_into() {
Err(replaced) => Arc::new(InlineLevelBox::Atomic { Err(replaced) => Arc::new(InlineLevelBox::Atomic {
@ -322,97 +350,99 @@ impl<'a> BlockContainerBuilder<'a> {
.expect("no ongoing inline level box found"); .expect("no ongoing inline level box found");
inline_box.last_fragment = true; inline_box.last_fragment = true;
Arc::new(InlineLevelBox::InlineBox(inline_box)) Arc::new(InlineLevelBox::InlineBox(inline_box))
} },
DisplayInside::FlowRoot => { DisplayInside::FlowRoot => {
// a.k.a. `inline-block` // a.k.a. `inline-block`
unimplemented!() unimplemented!()
} },
DisplayInside::None | DisplayInside::Contents => panic!(":("),
}, },
}; };
self.current_inline_level_boxes().push(box_.clone()); self.current_inline_level_boxes().push(box_.clone());
box_ box_
} }
fn handle_block_level_element( // FIXME
&mut self, // fn handle_block_level_element(
style: Arc<ComputedValues>, // &mut self,
display_inside: DisplayInside, // style: Arc<ComputedValues>,
contents: Contents, // display_inside: DisplayInside,
box_slot: BoxSlot<'a>, // contents: Contents,
) { // box_slot: BoxSlot<'a>,
// We just found a block level element, all ongoing inline level boxes // ) {
// need to be split around it. We iterate on the fragmented inline // // We just found a block level element, all ongoing inline level boxes
// level box stack to take their contents and set their first_fragment // // need to be split around it. We iterate on the fragmented inline
// field to false, for the fragmented inline level boxes that will // // level box stack to take their contents and set their first_fragment
// come after the block level element. // // field to false, for the fragmented inline level boxes that will
let mut fragmented_inline_boxes = // // come after the block level element.
self.ongoing_inline_boxes_stack // let mut fragmented_inline_boxes =
.iter_mut() // self.ongoing_inline_boxes_stack
.rev() // .iter_mut()
.map(|ongoing| { // .rev()
let fragmented = InlineBox { // .map(|ongoing| {
style: ongoing.style.clone(), // let fragmented = InlineBox {
first_fragment: ongoing.first_fragment, // style: ongoing.style.clone(),
// The fragmented boxes before the block level element // first_fragment: ongoing.first_fragment,
// are obviously not the last fragment. // // The fragmented boxes before the block level element
last_fragment: false, // // are obviously not the last fragment.
children: take(&mut ongoing.children), // last_fragment: false,
}; // children: take(&mut ongoing.children),
ongoing.first_fragment = false; // };
fragmented // ongoing.first_fragment = false;
}); // fragmented
// });
if let Some(last) = fragmented_inline_boxes.next() { // if let Some(last) = fragmented_inline_boxes.next() {
// There were indeed some ongoing inline level boxes before // // There were indeed some ongoing inline level boxes before
// the block, we accumulate them as a single inline level box // // the block, we accumulate them as a single inline level box
// to be pushed to the ongoing inline formatting context. // // to be pushed to the ongoing inline formatting context.
let mut fragmented_inline = InlineLevelBox::InlineBox(last); // let mut fragmented_inline = InlineLevelBox::InlineBox(last);
for mut fragmented_parent_inline_box in fragmented_inline_boxes { // for mut fragmented_parent_inline_box in fragmented_inline_boxes {
fragmented_parent_inline_box // fragmented_parent_inline_box
.children // .children
.push(Arc::new(fragmented_inline)); // .push(Arc::new(fragmented_inline));
fragmented_inline = InlineLevelBox::InlineBox(fragmented_parent_inline_box); // fragmented_inline = InlineLevelBox::InlineBox(fragmented_parent_inline_box);
} // }
self.ongoing_inline_formatting_context // self.ongoing_inline_formatting_context
.inline_level_boxes // .inline_level_boxes
.push(Arc::new(fragmented_inline)); // .push(Arc::new(fragmented_inline));
} // }
// We found a block level element, so the ongoing inline formatting // // We found a block level element, so the ongoing inline formatting
// context needs to be ended. // // context needs to be ended.
self.end_ongoing_inline_formatting_context(); // self.end_ongoing_inline_formatting_context();
let intermediate_box = match contents.try_into() { // let intermediate_box = match contents.try_into() {
Ok(contents) => match display_inside { // Ok(contents) => match display_inside {
DisplayInside::Flow => IntermediateBlockLevelBox::SameFormattingContextBlock { // DisplayInside::Flow => IntermediateBlockLevelBox::SameFormattingContextBlock {
style, // style,
contents: IntermediateBlockContainer::Deferred { contents }, // contents: IntermediateBlockContainer::Deferred { contents },
}, // },
_ => IntermediateBlockLevelBox::Independent { // _ => IntermediateBlockLevelBox::Independent {
style, // style,
display_inside, // display_inside,
contents: contents.into(), // contents: contents.into(),
}, // },
}, // },
Err(contents) => { // Err(contents) => {
let contents = Contents::Replaced(contents); // let contents = Contents::Replaced(contents);
IntermediateBlockLevelBox::Independent { // IntermediateBlockLevelBox::Independent {
style, // style,
display_inside, // display_inside,
contents, // contents,
} // }
} // }
}; // };
self.block_level_boxes.push((intermediate_box, box_slot)) // self.block_level_boxes.push((intermediate_box, box_slot))
} // }
fn handle_absolutely_positioned_element( fn handle_absolutely_positioned_element(
&mut self, &mut self,
style: Arc<ComputedValues>, style: Arc<ComputedValues>,
display_inside: DisplayInside, display_inside: DisplayInside,
contents: Contents, contents: Contents<Node>,
box_slot: BoxSlot<'a>, box_slot: BoxSlot<'dom>,
) { ) {
if !self.has_ongoing_inline_formatting_context() { if !self.has_ongoing_inline_formatting_context() {
let box_ = IntermediateBlockLevelBox::OutOfFlowAbsolutelyPositionedBox { let box_ = IntermediateBlockLevelBox::OutOfFlowAbsolutelyPositionedBox {
@ -420,12 +450,12 @@ impl<'a> BlockContainerBuilder<'a> {
contents, contents,
display_inside, display_inside,
}; };
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 {
contents: IndependentFormattingContext::construct( contents: IndependentFormattingContext::construct(
self.context, unimplemented!(),
&style, &style,
display_inside, display_inside,
contents, contents,
@ -442,8 +472,8 @@ impl<'a> BlockContainerBuilder<'a> {
&mut self, &mut self,
style: Arc<ComputedValues>, style: Arc<ComputedValues>,
display_inside: DisplayInside, display_inside: DisplayInside,
contents: Contents, contents: Contents<Node>,
box_slot: BoxSlot<'a>, box_slot: BoxSlot<'dom>,
) { ) {
self.contains_floats = ContainsFloats::Yes; self.contains_floats = ContainsFloats::Yes;
@ -485,20 +515,21 @@ impl<'a> BlockContainerBuilder<'a> {
} }
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(|| { // FIXME
// If parent_style is None, the parent is the document node, // let anonymous_style = self.anonymous_style.get_or_insert_with(|| {
// in which case anonymous inline boxes should inherit their // // If parent_style is None, the parent is the document node,
// styles from initial values. // // in which case anonymous inline boxes should inherit their
ComputedValues::anonymous_inheriting_from(Some(block_container_style)) // // styles from initial values.
}); // ComputedValues::anonymous_inheriting_from(Some(block_container_style))
// });
let box_ = IntermediateBlockLevelBox::SameFormattingContextBlock { // let box_ = IntermediateBlockLevelBox::SameFormattingContextBlock {
style: anonymous_style.clone(), // style: anonymous_style.clone(),
contents: IntermediateBlockContainer::InlineFormattingContext(take( // contents: IntermediateBlockContainer::InlineFormattingContext(take(
&mut self.ongoing_inline_formatting_context, // &mut self.ongoing_inline_formatting_context,
)), // )),
}; // };
self.block_level_boxes.push((box_, BoxSlot::dummy())) // self.block_level_boxes.push((box_, BoxSlot::dummy()))
} }
fn current_inline_level_boxes(&mut self) -> &mut Vec<Arc<InlineLevelBox>> { fn current_inline_level_boxes(&mut self) -> &mut Vec<Arc<InlineLevelBox>> {
@ -512,20 +543,26 @@ impl<'a> BlockContainerBuilder<'a> {
!self !self
.ongoing_inline_formatting_context .ongoing_inline_formatting_context
.inline_level_boxes .inline_level_boxes
.is_empty() .is_empty() ||
|| !self.ongoing_inline_boxes_stack.is_empty() !self.ongoing_inline_boxes_stack.is_empty()
} }
} }
impl IntermediateBlockLevelBox { impl<'dom, Node> IntermediateBlockLevelBox<Node>
fn finish(self, context: &Context) -> (Arc<BlockLevelBox>, ContainsFloats) { where
Node: NodeExt<'dom>,
{
fn finish<'style>(
self,
context: &SharedStyleContext<'style>,
) -> (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) = contents.finish(context, &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)
} },
IntermediateBlockLevelBox::Independent { IntermediateBlockLevelBox::Independent {
style, style,
display_inside, display_inside,
@ -541,7 +578,7 @@ impl IntermediateBlockLevelBox {
Arc::new(BlockLevelBox::Independent { style, contents }), Arc::new(BlockLevelBox::Independent { style, contents }),
ContainsFloats::No, ContainsFloats::No,
) )
} },
IntermediateBlockLevelBox::OutOfFlowAbsolutelyPositionedBox { IntermediateBlockLevelBox::OutOfFlowAbsolutelyPositionedBox {
style, style,
display_inside, display_inside,
@ -559,7 +596,7 @@ impl IntermediateBlockLevelBox {
}, },
)); ));
(block_level_box, ContainsFloats::No) (block_level_box, ContainsFloats::No)
} },
IntermediateBlockLevelBox::OutOfFlowFloatBox { IntermediateBlockLevelBox::OutOfFlowFloatBox {
style, style,
display_inside, display_inside,
@ -576,21 +613,24 @@ impl IntermediateBlockLevelBox {
style, style,
})); }));
(block_level_box, ContainsFloats::Yes) (block_level_box, ContainsFloats::Yes)
} },
} }
} }
} }
impl IntermediateBlockContainer { impl<'dom, Node> IntermediateBlockContainer<Node>
fn finish( where
Node: NodeExt<'dom>,
{
fn finish<'style>(
self, self,
context: &Context, context: &SharedStyleContext<'style>,
style: &Arc<ComputedValues>, style: &Arc<ComputedValues>,
) -> (BlockContainer, ContainsFloats) { ) -> (BlockContainer, ContainsFloats) {
match self { match self {
IntermediateBlockContainer::Deferred { contents } => { IntermediateBlockContainer::Deferred { contents } => {
BlockContainer::construct(context, style, contents) BlockContainer::construct(context, style, contents)
} },
IntermediateBlockContainer::InlineFormattingContext(ifc) => { IntermediateBlockContainer::InlineFormattingContext(ifc) => {
// 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
@ -599,13 +639,13 @@ impl IntermediateBlockContainer {
BlockContainer::InlineFormattingContext(ifc), BlockContainer::InlineFormattingContext(ifc),
ContainsFloats::No, ContainsFloats::No,
) )
} },
} }
} }
} }
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::layout) enum ContainsFloats { pub(crate) enum ContainsFloats {
No, No,
Yes, Yes,
} }

View file

@ -1,13 +1,19 @@
use super::*; /* 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/. */
use crate::IndependentFormattingContext;
use servo_arc::Arc;
use style::properties::ComputedValues;
#[derive(Debug)] #[derive(Debug)]
pub(in crate::layout) struct FloatBox { pub(crate) struct FloatBox {
pub style: Arc<ComputedValues>, pub style: Arc<ComputedValues>,
pub contents: IndependentFormattingContext, pub contents: IndependentFormattingContext,
} }
/// Data kept during layout about the floats in a given block formatting context. /// Data kept during layout about the floats in a given block formatting context.
pub(in crate::layout) struct FloatContext { pub(crate) struct FloatContext {
// TODO // TODO
} }

View file

@ -1,14 +1,27 @@
use super::*; /* This Source Code Form is subject to the terms of the Mozilla Public
use crate::fonts::BITSTREAM_VERA_SANS; * License, v. 2.0. If a copy of the MPL was not distributed with this
use crate::text::ShapedSegment; * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::flow::float::FloatBox;
use crate::flow::FlowChildren;
use crate::fragments::{AnonymousFragment, BoxFragment, CollapsedBlockMargins, Fragment};
use crate::geom::flow_relative::{Rect, Sides, Vec2};
use crate::positioned::{AbsolutelyPositionedBox, AbsolutelyPositionedFragment};
use crate::replaced::ReplacedContent;
use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayOutside};
use crate::{relative_adjustement, take, ContainingBlock};
use servo_arc::Arc;
use style::properties::ComputedValues;
use style::values::computed::Length;
use style::Zero;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub(in crate::layout) struct InlineFormattingContext { pub(crate) struct InlineFormattingContext {
pub(super) inline_level_boxes: Vec<Arc<InlineLevelBox>>, pub(super) inline_level_boxes: Vec<Arc<InlineLevelBox>>,
} }
#[derive(Debug)] #[derive(Debug)]
pub(in crate::layout) enum InlineLevelBox { pub(crate) enum InlineLevelBox {
InlineBox(InlineBox), InlineBox(InlineBox),
TextRun(TextRun), TextRun(TextRun),
OutOfFlowAbsolutelyPositionedBox(AbsolutelyPositionedBox), OutOfFlowAbsolutelyPositionedBox(AbsolutelyPositionedBox),
@ -21,7 +34,7 @@ pub(in crate::layout) enum InlineLevelBox {
} }
#[derive(Debug)] #[derive(Debug)]
pub(in crate::layout) struct InlineBox { pub(crate) struct InlineBox {
pub style: Arc<ComputedValues>, pub style: Arc<ComputedValues>,
pub first_fragment: bool, pub first_fragment: bool,
pub last_fragment: bool, pub last_fragment: bool,
@ -30,7 +43,7 @@ pub(in crate::layout) struct InlineBox {
/// https://www.w3.org/TR/css-display-3/#css-text-run /// https://www.w3.org/TR/css-display-3/#css-text-run
#[derive(Debug)] #[derive(Debug)]
pub(in crate::layout) struct TextRun { pub(crate) struct TextRun {
pub parent_style: Arc<ComputedValues>, pub parent_style: Arc<ComputedValues>,
pub text: String, pub text: String,
} }
@ -93,36 +106,40 @@ impl InlineFormattingContext {
InlineLevelBox::InlineBox(inline) => { InlineLevelBox::InlineBox(inline) => {
let partial = inline.start_layout(&mut ifc); let partial = inline.start_layout(&mut ifc);
ifc.partial_inline_boxes_stack.push(partial) ifc.partial_inline_boxes_stack.push(partial)
} },
InlineLevelBox::TextRun(run) => run.layout(&mut ifc), InlineLevelBox::TextRun(run) => run.layout(&mut ifc),
InlineLevelBox::Atomic { style: _, contents } => { InlineLevelBox::Atomic { style: _, contents } => {
// FIXME // FIXME
match *contents {} match *contents {}
} },
InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => { InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => {
let initial_start_corner = match box_.style.specified_display { let initial_start_corner =
Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { match Display::from(box_.style.get_box().original_display) {
outside, Display::GeneratingBox(DisplayGeneratingBox::OutsideInside {
inside: _, outside,
}) => Vec2 { inside: _,
inline: match outside { }) => Vec2 {
DisplayOutside::Inline => ifc.inline_position, inline: match outside {
DisplayOutside::Block => Length::zero(), DisplayOutside::Inline => ifc.inline_position,
DisplayOutside::Block => Length::zero(),
DisplayOutside::None => unreachable!(":("),
},
block: ifc.line_boxes.next_line_block_position,
}, },
block: ifc.line_boxes.next_line_block_position, Display::Contents => {
}, panic!("display:contents does not generate an abspos box")
Display::Contents => { },
panic!("display:contents does not generate an abspos box") Display::None => {
} panic!("display:none does not generate an abspos box")
Display::None => panic!("display:none does not generate an abspos box"), },
}; };
absolutely_positioned_fragments 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; continue;
} },
} }
} else } else
// Reached the end of ifc.remaining_boxes // Reached the end of ifc.remaining_boxes
@ -180,7 +197,7 @@ impl InlineBox {
let style = self.style.clone(); let style = self.style.clone();
let cbis = ifc.containing_block.inline_size; let cbis = ifc.containing_block.inline_size;
let mut padding = style.padding().percentages_relative_to(cbis); let mut padding = style.padding().percentages_relative_to(cbis);
let mut border = style.border_width().percentages_relative_to(cbis); let mut border = style.border_width();
let mut margin = style let mut margin = style
.margin() .margin()
.percentages_relative_to(cbis) .percentages_relative_to(cbis)
@ -245,9 +262,9 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
}; };
let last_fragment = self.last_box_tree_fragment && !at_line_break; let last_fragment = self.last_box_tree_fragment && !at_line_break;
if last_fragment { if last_fragment {
*inline_position += fragment.padding.inline_end *inline_position += fragment.padding.inline_end +
+ fragment.border.inline_end fragment.border.inline_end +
+ fragment.margin.inline_end; fragment.margin.inline_end;
} else { } else {
fragment.padding.inline_end = Length::zero(); fragment.padding.inline_end = Length::zero();
fragment.border.inline_end = Length::zero(); fragment.border.inline_end = Length::zero();
@ -256,10 +273,10 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
self.parent_nesting_level self.parent_nesting_level
.max_block_size_of_fragments_so_far .max_block_size_of_fragments_so_far
.max_assign( .max_assign(
fragment.content_rect.size.block fragment.content_rect.size.block +
+ fragment.padding.block_sum() fragment.padding.block_sum() +
+ fragment.border.block_sum() fragment.border.block_sum() +
+ fragment.margin.block_sum(), fragment.margin.block_sum(),
); );
self.parent_nesting_level self.parent_nesting_level
.fragments_so_far .fragments_so_far
@ -268,78 +285,7 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
} }
impl TextRun { impl TextRun {
fn layout(&self, ifc: &mut InlineFormattingContextState) { fn layout(&self, _ifc: &mut InlineFormattingContextState) {
let available = ifc.containing_block.inline_size - ifc.inline_position; // TODO
let mut chars = self.text.chars();
loop {
let mut shaped = ShapedSegment::new_with_naive_shaping(BITSTREAM_VERA_SANS.clone());
let mut last_break_opportunity = None;
loop {
let next = chars.next();
if matches!(next, Some(' ') | None) {
let inline_size = self.parent_style.font.font_size * shaped.advance_width;
if inline_size > available {
if let Some((state, iter)) = last_break_opportunity.take() {
shaped.restore(&state);
chars = iter;
}
break;
}
}
if let Some(ch) = next {
if ch == ' ' {
last_break_opportunity = Some((shaped.save(), chars.clone()))
}
shaped.append_char(ch).unwrap()
} else {
break;
}
}
let inline_size = self.parent_style.font.font_size * shaped.advance_width;
// https://www.w3.org/TR/CSS2/visudet.html#propdef-line-height
// 'normal':
// “set the used value to a "reasonable" value based on the font of the element.”
let line_height = self.parent_style.font.font_size.0 * 1.2;
let content_rect = Rect {
start_corner: Vec2 {
block: Length::zero(),
inline: ifc.inline_position - ifc.current_nesting_level.inline_start,
},
size: Vec2 {
block: line_height,
inline: inline_size,
},
};
ifc.inline_position += inline_size;
ifc.current_nesting_level
.max_block_size_of_fragments_so_far
.max_assign(line_height);
ifc.current_nesting_level
.fragments_so_far
.push(Fragment::Text(TextFragment {
parent_style: self.parent_style.clone(),
content_rect,
text: shaped,
}));
if chars.as_str().is_empty() {
break;
} else {
// New line
ifc.current_nesting_level.inline_start = Length::zero();
let mut nesting_level = &mut ifc.current_nesting_level;
for partial in ifc.partial_inline_boxes_stack.iter_mut().rev() {
partial.finish_layout(nesting_level, &mut ifc.inline_position, true);
partial.start_corner.inline = Length::zero();
partial.padding.inline_start = Length::zero();
partial.border.inline_start = Length::zero();
partial.margin.inline_start = Length::zero();
partial.parent_nesting_level.inline_start = Length::zero();
nesting_level = &mut partial.parent_nesting_level;
}
ifc.line_boxes
.finish_line(nesting_level, ifc.containing_block);
ifc.inline_position = Length::zero();
}
}
} }
} }

View file

@ -1,32 +1,46 @@
/* 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/. */
//! Flow layout, also known as block-and-inline layout. //! Flow layout, also known as block-and-inline layout.
use super::*; use crate::flow::float::{FloatBox, FloatContext};
use rayon::prelude::*; use crate::flow::inline::InlineFormattingContext;
use crate::fragments::{
AnonymousFragment, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment,
};
use crate::geom::flow_relative::{Rect, Vec2};
use crate::positioned::{
adjust_static_positions, AbsolutelyPositionedBox, AbsolutelyPositionedFragment,
};
use crate::style_ext::{ComputedValuesExt, Position};
use crate::{relative_adjustement, ContainingBlock, IndependentFormattingContext};
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
use rayon_croissant::ParallelIteratorExt; use rayon_croissant::ParallelIteratorExt;
use servo_arc::Arc;
use style::properties::ComputedValues;
use style::values::computed::{Length, LengthOrAuto};
use style::Zero;
mod construct; mod construct;
mod float; mod float;
mod inline; pub mod inline;
mod root; mod root;
pub(super) use construct::*;
pub(super) use float::*;
pub(super) use inline::*;
#[derive(Debug)] #[derive(Debug)]
pub(super) struct BlockFormattingContext { pub(crate) struct BlockFormattingContext {
pub contents: BlockContainer, pub contents: BlockContainer,
pub contains_floats: bool, pub contains_floats: bool,
} }
#[derive(Debug)] #[derive(Debug)]
pub(super) enum BlockContainer { pub(crate) enum BlockContainer {
BlockLevelBoxes(Vec<Arc<BlockLevelBox>>), BlockLevelBoxes(Vec<Arc<BlockLevelBox>>),
InlineFormattingContext(InlineFormattingContext), InlineFormattingContext(InlineFormattingContext),
} }
#[derive(Debug)] #[derive(Debug)]
pub(super) enum BlockLevelBox { pub(crate) enum BlockLevelBox {
SameFormattingContextBlock { SameFormattingContextBlock {
style: Arc<ComputedValues>, style: Arc<ComputedValues>,
contents: BlockContainer, contents: BlockContainer,
@ -98,7 +112,7 @@ impl BlockContainer {
), ),
BlockContainer::InlineFormattingContext(ifc) => { BlockContainer::InlineFormattingContext(ifc) => {
ifc.layout(containing_block, tree_rank, absolutely_positioned_fragments) ifc.layout(containing_block, tree_rank, absolutely_positioned_fragments)
} },
} }
} }
} }
@ -115,9 +129,9 @@ fn layout_block_level_children<'a>(
match fragment { match fragment {
Fragment::Box(fragment) => { Fragment::Box(fragment) => {
let fragment_block_margins = &fragment.block_margins_collapsed_with_children; let fragment_block_margins = &fragment.block_margins_collapsed_with_children;
let fragment_block_size = fragment.padding.block_sum() let fragment_block_size = fragment.padding.block_sum() +
+ fragment.border.block_sum() fragment.border.block_sum() +
+ fragment.content_rect.size.block; fragment.content_rect.size.block;
if placement_state.next_in_flow_margin_collapses_with_parent_start_margin { if placement_state.next_in_flow_margin_collapses_with_parent_start_margin {
assert_eq!(placement_state.current_margin.solve(), Length::zero()); assert_eq!(placement_state.current_margin.solve(), Length::zero());
@ -136,8 +150,8 @@ fn layout_block_level_children<'a>(
.current_margin .current_margin
.adjoin_assign(&fragment_block_margins.start); .adjoin_assign(&fragment_block_margins.start);
} }
fragment.content_rect.start_corner.block += placement_state.current_margin.solve() fragment.content_rect.start_corner.block += placement_state.current_margin.solve() +
+ placement_state.current_block_direction_position; placement_state.current_block_direction_position;
if fragment_block_margins.collapsed_through { if fragment_block_margins.collapsed_through {
placement_state placement_state
.current_margin .current_margin
@ -147,7 +161,7 @@ fn layout_block_level_children<'a>(
placement_state.current_block_direction_position += placement_state.current_block_direction_position +=
placement_state.current_margin.solve() + fragment_block_size; placement_state.current_margin.solve() + fragment_block_size;
placement_state.current_margin = fragment_block_margins.end; placement_state.current_margin = fragment_block_margins.end;
} },
Fragment::Anonymous(fragment) => { Fragment::Anonymous(fragment) => {
// FIXME(nox): Margin collapsing for hypothetical boxes of // FIXME(nox): Margin collapsing for hypothetical boxes of
// abspos elements is probably wrong. // abspos elements is probably wrong.
@ -155,7 +169,7 @@ fn layout_block_level_children<'a>(
assert_eq!(fragment.rect.size.block, Length::zero()); assert_eq!(fragment.rect.size.block, Length::zero());
fragment.rect.start_corner.block += fragment.rect.start_corner.block +=
placement_state.current_block_direction_position; placement_state.current_block_direction_position;
} },
_ => unreachable!(), _ => unreachable!(),
} }
} }
@ -261,12 +275,12 @@ impl BlockLevelBox {
) )
}, },
)) ))
} },
BlockLevelBox::Independent { style, contents } => match contents.as_replaced() { BlockLevelBox::Independent { style, contents } => match contents.as_replaced() {
Ok(replaced) => { Ok(replaced) => {
// FIXME // FIXME
match *replaced {} match *replaced {}
} },
Err(contents) => Fragment::Box(layout_in_flow_non_replaced_block_level( Err(contents) => Fragment::Box(layout_in_flow_non_replaced_block_level(
containing_block, containing_block,
absolutely_positioned_fragments, absolutely_positioned_fragments,
@ -280,11 +294,11 @@ impl BlockLevelBox {
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => { BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => {
absolutely_positioned_fragments.push(box_.layout(Vec2::zero(), tree_rank)); absolutely_positioned_fragments.push(box_.layout(Vec2::zero(), tree_rank));
Fragment::Anonymous(AnonymousFragment::no_op(containing_block.mode)) Fragment::Anonymous(AnonymousFragment::no_op(containing_block.mode))
} },
BlockLevelBox::OutOfFlowFloatBox(_box_) => { BlockLevelBox::OutOfFlowFloatBox(_box_) => {
// TODO // TODO
Fragment::Anonymous(AnonymousFragment::no_op(containing_block.mode)) Fragment::Anonymous(AnonymousFragment::no_op(containing_block.mode))
} },
} }
} }
} }
@ -310,43 +324,40 @@ fn layout_in_flow_non_replaced_block_level<'a>(
) -> BoxFragment { ) -> BoxFragment {
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().percentages_relative_to(cbis); let border = style.border_width();
let mut computed_margin = style.margin().percentages_relative_to(cbis); let mut computed_margin = style.margin().percentages_relative_to(cbis);
let pb = &padding + &border; let pb = &padding + &border;
let box_size = style.box_size(); let box_size = style.box_size();
let inline_size = box_size.inline.percentage_relative_to(cbis); let inline_size = box_size.inline.percentage_relative_to(cbis);
if let LengthOrAuto::Length(is) = inline_size { if let LengthOrAuto::LengthPercentage(is) = inline_size {
let inline_margins = cbis - is - pb.inline_sum(); let inline_margins = cbis - is - pb.inline_sum();
use LengthOrAuto::*;
match ( match (
&mut computed_margin.inline_start, &mut computed_margin.inline_start,
&mut computed_margin.inline_end, &mut computed_margin.inline_end,
) { ) {
(s @ &mut Auto, e @ &mut Auto) => { (s @ &mut LengthOrAuto::Auto, e @ &mut LengthOrAuto::Auto) => {
*s = Length(inline_margins / 2.); *s = LengthOrAuto::LengthPercentage(inline_margins / 2.);
*e = Length(inline_margins / 2.); *e = LengthOrAuto::LengthPercentage(inline_margins / 2.);
} },
(s @ &mut Auto, _) => { (s @ &mut LengthOrAuto::Auto, _) => {
*s = Length(inline_margins); *s = LengthOrAuto::LengthPercentage(inline_margins);
} },
(_, e @ &mut Auto) => { (_, e @ &mut LengthOrAuto::Auto) => {
*e = Length(inline_margins); *e = LengthOrAuto::LengthPercentage(inline_margins);
} },
(_, e @ _) => { (_, e @ _) => {
// Either the inline-end margin is auto, // Either the inline-end margin is auto,
// or were over-constrained and we do as if it were. // or were over-constrained and we do as if it were.
*e = Length(inline_margins); *e = LengthOrAuto::LengthPercentage(inline_margins);
} },
} }
} }
let margin = computed_margin.auto_is(Length::zero); let margin = computed_margin.auto_is(Length::zero);
let mut block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin); let mut block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
let inline_size = inline_size.auto_is(|| cbis - pb.inline_sum() - margin.inline_sum()); let inline_size = inline_size.auto_is(|| cbis - pb.inline_sum() - margin.inline_sum());
let block_size = match box_size.block { let block_size = box_size
LengthOrPercentageOrAuto::Length(l) => LengthOrAuto::Length(l), .block
LengthOrPercentageOrAuto::Percentage(p) => containing_block.block_size.map(|cbbs| cbbs * p), .maybe_percentage_relative_to(containing_block.block_size.non_auto());
LengthOrPercentageOrAuto::Auto => LengthOrAuto::Auto,
};
let containing_block_for_children = ContainingBlock { let containing_block_for_children = ContainingBlock {
inline_size, inline_size,
block_size, block_size,
@ -358,11 +369,11 @@ fn layout_in_flow_non_replaced_block_level<'a>(
"Mixed writing modes are not supported yet" "Mixed writing modes are not supported yet"
); );
let this_start_margin_can_collapse_with_children = CollapsibleWithParentStartMargin( let this_start_margin_can_collapse_with_children = CollapsibleWithParentStartMargin(
block_level_kind == BlockLevelKind::SameFormattingContextBlock block_level_kind == BlockLevelKind::SameFormattingContextBlock &&
&& pb.block_start == Length::zero(), pb.block_start == Length::zero(),
); );
let this_end_margin_can_collapse_with_children = (block_level_kind, pb.block_end, block_size) let this_end_margin_can_collapse_with_children = (block_level_kind, pb.block_end, block_size) ==
== ( (
BlockLevelKind::SameFormattingContextBlock, BlockLevelKind::SameFormattingContextBlock,
Length::zero(), Length::zero(),
LengthOrAuto::Auto, LengthOrAuto::Auto,
@ -370,7 +381,7 @@ fn layout_in_flow_non_replaced_block_level<'a>(
let mut nested_abspos = vec![]; let mut nested_abspos = vec![];
let mut flow_children = layout_contents( let mut flow_children = layout_contents(
&containing_block_for_children, &containing_block_for_children,
if style.box_.position.is_relatively_positioned() { if style.get_box().position == Position::Relative {
&mut nested_abspos &mut nested_abspos
} else { } else {
absolutely_positioned_fragments absolutely_positioned_fragments
@ -401,9 +412,9 @@ fn layout_in_flow_non_replaced_block_level<'a>(
flow_children.block_size += flow_children.collapsible_margins_in_children.end.solve(); flow_children.block_size += flow_children.collapsible_margins_in_children.end.solve();
} }
block_margins_collapsed_with_children.collapsed_through = block_margins_collapsed_with_children.collapsed_through =
this_start_margin_can_collapse_with_children.0 this_start_margin_can_collapse_with_children.0 &&
&& this_end_margin_can_collapse_with_children this_end_margin_can_collapse_with_children &&
&& flow_children flow_children
.collapsible_margins_in_children .collapsible_margins_in_children
.collapsed_through; .collapsed_through;
let relative_adjustement = relative_adjustement(style, inline_size, block_size); let relative_adjustement = relative_adjustement(style, inline_size, block_size);
@ -418,7 +429,7 @@ fn layout_in_flow_non_replaced_block_level<'a>(
inline: inline_size, inline: inline_size,
}, },
}; };
if style.box_.position.is_relatively_positioned() { if style.get_box().position == Position::Relative {
AbsolutelyPositionedFragment::in_positioned_containing_block( AbsolutelyPositionedFragment::in_positioned_containing_block(
&nested_abspos, &nested_abspos,
&mut flow_children.fragments, &mut flow_children.fragments,

View file

@ -1,122 +1,129 @@
/* 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/. */
use super::*; use super::*;
impl crate::dom::Document { // FIXME
pub(crate) fn layout( // impl crate::dom::Document {
&self, // pub(crate) fn layout(
viewport: crate::primitives::Size<crate::primitives::CssPx>, // &self,
) -> Vec<Fragment> { // viewport: crate::geom::Size<crate::geom::CssPx>,
BoxTreeRoot::construct(self).layout(viewport) // ) -> Vec<Fragment> {
} // BoxTreeRoot::construct(self).layout(viewport)
} // }
// }
struct BoxTreeRoot(BlockFormattingContext); struct BoxTreeRoot(BlockFormattingContext);
impl BoxTreeRoot { // FIXME
pub fn construct(document: &dom::Document) -> Self { // impl BoxTreeRoot {
let author_styles = &document.parse_stylesheets(); // pub fn construct(document: &dom::Document) -> Self {
let context = Context { // let author_styles = &document.parse_stylesheets();
document, // let context = Context {
author_styles, // document,
}; // author_styles,
let root_element = document.root_element(); // };
let style = style_for_element(context.author_styles, context.document, root_element, None); // let root_element = document.root_element();
let (contains_floats, boxes) = construct_for_root_element(&context, root_element, style); // let style = style_for_element(context.author_styles, context.document, root_element, None);
Self(BlockFormattingContext { // let (contains_floats, boxes) = construct_for_root_element(&context, root_element, style);
contains_floats: contains_floats == ContainsFloats::Yes, // Self(BlockFormattingContext {
contents: BlockContainer::BlockLevelBoxes(boxes), // contains_floats: contains_floats == ContainsFloats::Yes,
}) // contents: BlockContainer::BlockLevelBoxes(boxes),
} // })
} // }
// }
fn construct_for_root_element( // fn construct_for_root_element(
context: &Context, // context: &Context,
root_element: dom::NodeId, // root_element: dom::NodeId,
style: Arc<ComputedValues>, // style: Arc<ComputedValues>,
) -> (ContainsFloats, Vec<Arc<BlockLevelBox>>) { // ) -> (ContainsFloats, Vec<Arc<BlockLevelBox>>) {
let replaced = ReplacedContent::for_element(root_element, context); // let replaced = ReplacedContent::for_element(root_element, context);
let display_inside = match style.box_.display { // let display_inside = match style.box_.display {
Display::None => return (ContainsFloats::No, Vec::new()), // Display::None => return (ContainsFloats::No, Vec::new()),
Display::Contents if replaced.is_some() => { // Display::Contents if replaced.is_some() => {
// 'display: contents' computes to 'none' for replaced elements // // 'display: contents' computes to 'none' for replaced elements
return (ContainsFloats::No, Vec::new()); // return (ContainsFloats::No, Vec::new());
} // }
// https://drafts.csswg.org/css-display-3/#transformations // // https://drafts.csswg.org/css-display-3/#transformations
Display::Contents => DisplayInside::Flow, // Display::Contents => DisplayInside::Flow,
// The root element is blockified, ignore DisplayOutside // // The root element is blockified, ignore DisplayOutside
Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { inside, .. }) => inside, // Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { inside, .. }) => inside,
}; // };
if let Some(replaced) = replaced { // if let Some(replaced) = replaced {
let _box = match replaced {}; // let _box = match replaced {};
#[allow(unreachable_code)] // #[allow(unreachable_code)]
{ // {
return (ContainsFloats::No, vec![Arc::new(_box)]); // return (ContainsFloats::No, vec![Arc::new(_box)]);
} // }
} // }
let contents = IndependentFormattingContext::construct( // let contents = IndependentFormattingContext::construct(
context, // context,
&style, // &style,
display_inside, // display_inside,
Contents::OfElement(root_element), // Contents::OfElement(root_element),
); // );
if style.box_.position.is_absolutely_positioned() { // if style.box_.position.is_absolutely_positioned() {
( // (
ContainsFloats::No, // ContainsFloats::No,
vec![Arc::new(BlockLevelBox::OutOfFlowAbsolutelyPositionedBox( // vec![Arc::new(BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(
AbsolutelyPositionedBox { style, contents }, // AbsolutelyPositionedBox { style, contents },
))], // ))],
) // )
} else if style.box_.float.is_floating() { // } else if style.box_.float.is_floating() {
( // (
ContainsFloats::Yes, // ContainsFloats::Yes,
vec![Arc::new(BlockLevelBox::OutOfFlowFloatBox(FloatBox { // vec![Arc::new(BlockLevelBox::OutOfFlowFloatBox(FloatBox {
contents, // contents,
style, // style,
}))], // }))],
) // )
} else { // } else {
( // (
ContainsFloats::No, // ContainsFloats::No,
vec![Arc::new(BlockLevelBox::Independent { style, contents })], // vec![Arc::new(BlockLevelBox::Independent { style, contents })],
) // )
} // }
} // }
impl BoxTreeRoot { // impl BoxTreeRoot {
fn layout(&self, viewport: crate::primitives::Size<crate::primitives::CssPx>) -> Vec<Fragment> { // fn layout(&self, viewport: crate::geom::Size<crate::geom::CssPx>) -> Vec<Fragment> {
let initial_containing_block_size = Vec2 { // let initial_containing_block_size = Vec2 {
inline: Length { px: viewport.width }, // inline: Length { px: viewport.width },
block: Length { // block: Length {
px: viewport.height, // px: viewport.height,
}, // },
}; // };
let initial_containing_block = ContainingBlock { // let initial_containing_block = ContainingBlock {
inline_size: initial_containing_block_size.inline, // inline_size: initial_containing_block_size.inline,
block_size: LengthOrAuto::Length(initial_containing_block_size.block), // block_size: LengthOrAuto::Length(initial_containing_block_size.block),
// FIXME: use the documents mode: // // FIXME: use the documents mode:
// https://drafts.csswg.org/css-writing-modes/#principal-flow // // https://drafts.csswg.org/css-writing-modes/#principal-flow
mode: (WritingMode::HorizontalTb, Direction::Ltr), // mode: (WritingMode::HorizontalTb, Direction::Ltr),
}; // };
let dummy_tree_rank = 0; // let dummy_tree_rank = 0;
let mut absolutely_positioned_fragments = vec![]; // let mut absolutely_positioned_fragments = vec![];
let mut flow_children = self.0.layout( // let mut fragments = self.0.layout(
&initial_containing_block, // &initial_containing_block,
dummy_tree_rank, // &mut absolutely_positioned_fragments,
&mut absolutely_positioned_fragments, // dummy_tree_rank,
); // &mut PlacementState::root(),
// );
let initial_containing_block = DefiniteContainingBlock { // let initial_containing_block = DefiniteContainingBlock {
size: initial_containing_block_size, // size: initial_containing_block_size,
mode: initial_containing_block.mode, // mode: initial_containing_block.mode,
}; // };
flow_children.fragments.par_extend( // fragments.par_extend(
absolutely_positioned_fragments // absolutely_positioned_fragments
.par_iter() // .par_iter()
.map(|a| a.layout(&initial_containing_block)), // .map(|a| a.layout(&initial_containing_block)),
); // );
flow_children.fragments // fragments
} // }
} // }

View file

@ -1,5 +1,14 @@
use super::*; /* This Source Code Form is subject to the terms of the Mozilla Public
use crate::text::ShapedSegment; * 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/. */
use crate::geom::flow_relative::{Rect, Sides};
use crate::style_ext::{Direction, WritingMode};
// use crate::text::ShapedSegment;
use servo_arc::Arc;
use style::properties::ComputedValues;
use style::values::computed::Length;
use style::Zero;
pub(crate) enum Fragment { pub(crate) enum Fragment {
Box(BoxFragment), Box(BoxFragment),
@ -45,7 +54,7 @@ pub(crate) struct AnonymousFragment {
pub(crate) struct TextFragment { pub(crate) struct TextFragment {
pub parent_style: Arc<ComputedValues>, pub parent_style: Arc<ComputedValues>,
pub content_rect: Rect<Length>, pub content_rect: Rect<Length>,
pub text: ShapedSegment, // pub text: ShapedSegment,
} }
impl AnonymousFragment { impl AnonymousFragment {

View file

@ -1,20 +1,31 @@
pub(crate) use crate::style::values::Length; /* This Source Code Form is subject to the terms of the Mozilla Public
use crate::style::values::{LengthOrAuto, LengthOrPercentage, LengthOrPercentageOrAuto}; * 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/. */
use crate::style_ext::{Direction, WritingMode};
use std::ops::{Add, AddAssign, Sub};
use style::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
use style::Zero;
use style_traits::CSSPixel;
pub type Point<U> = euclid::Point2D<f32, U>;
pub type Size<U> = euclid::Size2D<f32, U>;
pub type Rect<U> = euclid::Rect<f32, U>;
pub(crate) mod physical { pub(crate) mod physical {
#[derive(Debug, Clone)] #[derive(Clone, Debug)]
pub(crate) struct Vec2<T> { pub(crate) struct Vec2<T> {
pub x: T, pub x: T,
pub y: T, pub y: T,
} }
#[derive(Debug, Clone)] #[derive(Clone, Debug)]
pub(crate) struct Rect<T> { pub(crate) struct Rect<T> {
pub top_left: Vec2<T>, pub top_left: Vec2<T>,
pub size: Vec2<T>, pub size: Vec2<T>,
} }
#[derive(Debug, Clone)] #[derive(Clone, Debug)]
pub(crate) struct Sides<T> { pub(crate) struct Sides<T> {
pub top: T, pub top: T,
pub left: T, pub left: T,
@ -24,19 +35,19 @@ pub(crate) mod physical {
} }
pub(crate) mod flow_relative { pub(crate) mod flow_relative {
#[derive(Debug, Clone)] #[derive(Clone, Debug)]
pub(crate) struct Vec2<T> { pub(crate) struct Vec2<T> {
pub inline: T, pub inline: T,
pub block: T, pub block: T,
} }
#[derive(Debug, Clone)] #[derive(Clone, Debug)]
pub(crate) struct Rect<T> { pub(crate) struct Rect<T> {
pub start_corner: Vec2<T>, pub start_corner: Vec2<T>,
pub size: Vec2<T>, pub size: Vec2<T>,
} }
#[derive(Debug, Clone)] #[derive(Clone, Debug)]
pub(crate) struct Sides<T> { pub(crate) struct Sides<T> {
pub inline_start: T, pub inline_start: T,
pub inline_end: T, pub inline_end: T,
@ -45,9 +56,6 @@ pub(crate) mod flow_relative {
} }
} }
use crate::style::values::{Direction, WritingMode};
use std::ops::{Add, AddAssign, Sub};
impl<T> Add<&'_ physical::Vec2<T>> for &'_ physical::Vec2<T> impl<T> Add<&'_ physical::Vec2<T>> for &'_ physical::Vec2<T>
where where
T: Add<Output = T> + Copy, T: Add<Output = T> + Copy,
@ -145,9 +153,9 @@ impl<T: Clone> flow_relative::Vec2<T> {
} }
} }
impl From<physical::Vec2<Length>> for crate::primitives::Point<crate::primitives::CssPx> { impl From<physical::Vec2<Length>> for Point<CSSPixel> {
fn from(v: physical::Vec2<Length>) -> Self { fn from(v: physical::Vec2<Length>) -> Self {
crate::primitives::Point::from_lengths(v.x.into(), v.y.into()) Point::from_lengths(v.x.into(), v.y.into())
} }
} }
@ -159,18 +167,14 @@ impl<T: Clone> physical::Sides<T> {
// https://drafts.csswg.org/css-writing-modes/#logical-to-physical // https://drafts.csswg.org/css-writing-modes/#logical-to-physical
let (bs, be) = match mode.0 { let (bs, be) = match mode.0 {
HorizontalTb => (&self.top, &self.bottom), HorizontalTb => (&self.top, &self.bottom),
VerticalRl | SidewaysRl => (&self.right, &self.left), VerticalRl => (&self.right, &self.left),
VerticalLr | SidewaysLr => (&self.left, &self.right), VerticalLr => (&self.left, &self.right),
}; };
let (is, ie) = match mode { let (is, ie) = match mode {
(HorizontalTb, Ltr) => (&self.left, &self.right), (HorizontalTb, Ltr) => (&self.left, &self.right),
(HorizontalTb, Rtl) => (&self.right, &self.left), (HorizontalTb, Rtl) => (&self.right, &self.left),
(VerticalRl, Ltr) | (SidewaysRl, Ltr) | (VerticalLr, Ltr) | (SidewaysLr, Rtl) => { (VerticalRl, Ltr) | (VerticalLr, Ltr) => (&self.top, &self.bottom),
(&self.top, &self.bottom) (VerticalRl, Rtl) | (VerticalLr, Rtl) => (&self.bottom, &self.top),
}
(VerticalRl, Rtl) | (SidewaysRl, Rtl) | (VerticalLr, Rtl) | (SidewaysLr, Ltr) => {
(&self.bottom, &self.top)
}
}; };
flow_relative::Sides { flow_relative::Sides {
inline_start: is.clone(), inline_start: is.clone(),
@ -229,13 +233,13 @@ impl<T> flow_relative::Sides<T> {
} }
} }
impl flow_relative::Sides<LengthOrPercentage> { impl flow_relative::Sides<LengthPercentage> {
pub fn percentages_relative_to(&self, basis: Length) -> flow_relative::Sides<Length> { pub fn percentages_relative_to(&self, basis: Length) -> flow_relative::Sides<Length> {
self.map(|s| s.percentage_relative_to(basis)) self.map(|s| s.percentage_relative_to(basis))
} }
} }
impl flow_relative::Sides<LengthOrPercentageOrAuto> { impl flow_relative::Sides<LengthPercentageOrAuto> {
pub fn percentages_relative_to(&self, basis: Length) -> flow_relative::Sides<LengthOrAuto> { pub fn percentages_relative_to(&self, basis: Length) -> flow_relative::Sides<LengthOrAuto> {
self.map(|s| s.percentage_relative_to(basis)) self.map(|s| s.percentage_relative_to(basis))
} }
@ -320,11 +324,11 @@ impl<T> physical::Rect<T> {
} }
} }
impl From<physical::Rect<Length>> for crate::primitives::Rect<crate::primitives::CssPx> { impl From<physical::Rect<Length>> for Rect<CSSPixel> {
fn from(r: physical::Rect<Length>) -> Self { fn from(r: physical::Rect<Length>) -> Self {
crate::primitives::Rect { Rect {
origin: crate::primitives::Point::new(r.top_left.x.px, r.top_left.y.px), origin: Point::new(r.top_left.x.px(), r.top_left.y.px()),
size: crate::primitives::Size::new(r.size.x.px, r.size.y.px), size: Size::new(r.size.x.px(), r.size.y.px()),
} }
} }
} }

View file

@ -2,11 +2,159 @@
* 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/. */
#![allow(dead_code)]
#![allow(unreachable_code)]
#![allow(unused_imports)]
#![allow(unused_variables)]
#![deny(unsafe_code)] #![deny(unsafe_code)]
#[macro_use]
extern crate serde;
use style::properties::ComputedValues;
use style::values::computed::{Length, LengthOrAuto};
use style::Zero;
pub mod context; pub mod context;
pub mod data; pub mod data;
pub mod dom_traversal;
pub mod element_data;
pub mod flow;
pub mod fragments;
pub mod geom;
pub mod opaque_node; pub mod opaque_node;
pub mod positioned;
pub mod query; pub mod query;
pub mod replaced;
pub mod style_ext;
pub mod traversal; pub mod traversal;
pub mod wrapper; pub mod wrapper;
use crate::dom_traversal::{Contents, NodeExt};
use crate::flow::{BlockFormattingContext, FlowChildren};
use crate::geom::flow_relative::Vec2;
use crate::positioned::AbsolutelyPositionedFragment;
use crate::replaced::ReplacedContent;
use crate::style_ext::{ComputedValuesExt, Direction, Position, WritingMode};
use servo_arc::Arc;
use std::convert::TryInto;
use style::context::SharedStyleContext;
use style::values::specified::box_::DisplayInside;
/// https://drafts.csswg.org/css-display/#independent-formatting-context
#[derive(Debug)]
enum IndependentFormattingContext {
Flow(BlockFormattingContext),
// Not called FC in specs, but behaves close enough
Replaced(ReplacedContent),
// Other layout modes go here
}
enum NonReplacedIFC<'a> {
Flow(&'a BlockFormattingContext),
}
impl IndependentFormattingContext {
fn construct<'dom, 'style>(
context: &SharedStyleContext<'style>,
style: &Arc<ComputedValues>,
display_inside: DisplayInside,
contents: Contents<impl NodeExt<'dom>>,
) -> Self {
match contents.try_into() {
Ok(non_replaced) => match display_inside {
DisplayInside::Flow | DisplayInside::FlowRoot => {
IndependentFormattingContext::Flow(BlockFormattingContext::construct(
context,
style,
non_replaced,
))
},
DisplayInside::None | DisplayInside::Contents => panic!(":("),
},
Err(replaced) => IndependentFormattingContext::Replaced(replaced),
}
}
fn as_replaced(&self) -> Result<&ReplacedContent, NonReplacedIFC> {
match self {
IndependentFormattingContext::Replaced(r) => Ok(r),
IndependentFormattingContext::Flow(f) => Err(NonReplacedIFC::Flow(f)),
}
}
fn layout<'a>(
&'a self,
containing_block: &ContainingBlock,
tree_rank: usize,
absolutely_positioned_fragments: &mut Vec<AbsolutelyPositionedFragment<'a>>,
) -> FlowChildren {
match self.as_replaced() {
Ok(replaced) => match *replaced {},
Err(ifc) => ifc.layout(containing_block, tree_rank, absolutely_positioned_fragments),
}
}
}
impl<'a> NonReplacedIFC<'a> {
fn layout(
&self,
containing_block: &ContainingBlock,
tree_rank: usize,
absolutely_positioned_fragments: &mut Vec<AbsolutelyPositionedFragment<'a>>,
) -> FlowChildren {
match self {
NonReplacedIFC::Flow(bfc) => {
bfc.layout(containing_block, tree_rank, absolutely_positioned_fragments)
},
}
}
}
struct ContainingBlock {
inline_size: Length,
block_size: LengthOrAuto,
mode: (WritingMode, Direction),
}
struct DefiniteContainingBlock {
size: Vec2<Length>,
mode: (WritingMode, Direction),
}
/// https://drafts.csswg.org/css2/visuren.html#relative-positioning
fn relative_adjustement(
style: &ComputedValues,
inline_size: Length,
block_size: LengthOrAuto,
) -> Vec2<Length> {
if style.get_box().position != Position::Relative {
return Vec2::zero();
}
fn adjust(start: LengthOrAuto, end: LengthOrAuto) -> Length {
match (start, end) {
(LengthOrAuto::Auto, LengthOrAuto::Auto) => Length::zero(),
(LengthOrAuto::Auto, LengthOrAuto::LengthPercentage(end)) => -end,
(LengthOrAuto::LengthPercentage(start), _) => start,
}
}
let block_size = block_size.auto_is(Length::zero);
let box_offsets = style.box_offsets().map_inline_and_block_axes(
|v| v.percentage_relative_to(inline_size),
|v| v.percentage_relative_to(block_size),
);
Vec2 {
inline: adjust(box_offsets.inline_start, box_offsets.inline_end),
block: adjust(box_offsets.block_start, box_offsets.block_end),
}
}
// FIXME: use std::mem::take when its stable.
// https://github.com/rust-lang/rust/issues/61129
fn take<T>(x: &mut T) -> T
where
T: Default,
{
std::mem::replace(x, Default::default())
}

View file

@ -1,30 +1,41 @@
use super::*; /* This Source Code Form is subject to the terms of the Mozilla Public
use rayon::prelude::*; * 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/. */
use crate::fragments::{AnonymousFragment, BoxFragment, CollapsedBlockMargins, Fragment};
use crate::geom::flow_relative::{Rect, Sides, Vec2};
use crate::style_ext::{ComputedValuesExt, Direction, WritingMode};
use crate::{ContainingBlock, DefiniteContainingBlock, IndependentFormattingContext};
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use servo_arc::Arc;
use style::properties::ComputedValues;
use style::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
use style::Zero;
#[derive(Debug)] #[derive(Debug)]
pub(super) struct AbsolutelyPositionedBox { pub(crate) struct AbsolutelyPositionedBox {
pub style: Arc<ComputedValues>, pub style: Arc<ComputedValues>,
pub contents: IndependentFormattingContext, pub contents: IndependentFormattingContext,
} }
#[derive(Debug)] #[derive(Debug)]
pub(super) struct AbsolutelyPositionedFragment<'box_> { pub(crate) struct AbsolutelyPositionedFragment<'box_> {
absolutely_positioned_box: &'box_ AbsolutelyPositionedBox, absolutely_positioned_box: &'box_ AbsolutelyPositionedBox,
/// The rank of the child from which this absolutely positioned fragment /// The rank of the child from which this absolutely positioned fragment
/// came from, when doing the layout of a block container. Used to compute /// came from, when doing the layout of a block container. Used to compute
/// static positions when going up the tree. /// static positions when going up the tree.
pub(super) tree_rank: usize, pub(crate) tree_rank: usize,
pub(super) inline_start: AbsoluteBoxOffsets<LengthOrPercentage>, pub(crate) inline_start: AbsoluteBoxOffsets<LengthPercentage>,
inline_size: LengthOrPercentageOrAuto, inline_size: LengthPercentageOrAuto,
pub(super) block_start: AbsoluteBoxOffsets<LengthOrPercentage>, pub(crate) block_start: AbsoluteBoxOffsets<LengthPercentage>,
block_size: LengthOrPercentageOrAuto, block_size: LengthPercentageOrAuto,
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub(super) enum AbsoluteBoxOffsets<NonStatic> { pub(crate) enum AbsoluteBoxOffsets<NonStatic> {
StaticStart { start: Length }, StaticStart { start: Length },
Start { start: NonStatic }, Start { start: NonStatic },
End { end: NonStatic }, End { end: NonStatic },
@ -32,7 +43,7 @@ pub(super) enum AbsoluteBoxOffsets<NonStatic> {
} }
impl AbsolutelyPositionedBox { impl AbsolutelyPositionedBox {
pub(super) fn layout<'a>( pub(crate) fn layout<'a>(
&'a self, &'a self,
initial_start_corner: Vec2<Length>, initial_start_corner: Vec2<Length>,
tree_rank: usize, tree_rank: usize,
@ -46,9 +57,9 @@ impl AbsolutelyPositionedBox {
fn absolute_box_offsets( fn absolute_box_offsets(
initial_static_start: Length, initial_static_start: Length,
start: LengthOrPercentageOrAuto, start: LengthPercentageOrAuto,
end: LengthOrPercentageOrAuto, end: LengthPercentageOrAuto,
) -> AbsoluteBoxOffsets<LengthOrPercentage> { ) -> AbsoluteBoxOffsets<LengthPercentage> {
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,
@ -82,7 +93,7 @@ impl AbsolutelyPositionedBox {
} }
impl<'a> AbsolutelyPositionedFragment<'a> { impl<'a> AbsolutelyPositionedFragment<'a> {
pub(super) fn in_positioned_containing_block( pub(crate) fn in_positioned_containing_block(
absolute: &[Self], absolute: &[Self],
fragments: &mut Vec<Fragment>, fragments: &mut Vec<Fragment>,
content_rect_size: &Vec2<Length>, content_rect_size: &Vec2<Length>,
@ -112,13 +123,13 @@ impl<'a> AbsolutelyPositionedFragment<'a> {
})) }))
} }
pub(super) fn layout(&self, containing_block: &DefiniteContainingBlock) -> Fragment { pub(crate) fn layout(&self, containing_block: &DefiniteContainingBlock) -> Fragment {
let style = &self.absolutely_positioned_box.style; let style = &self.absolutely_positioned_box.style;
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 padding = style.padding().percentages_relative_to(cbis); let padding = style.padding().percentages_relative_to(cbis);
let border = style.border_width().percentages_relative_to(cbis); 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;
@ -133,8 +144,8 @@ impl<'a> AbsolutelyPositionedFragment<'a> {
computed_margin_start: LengthOrAuto, computed_margin_start: LengthOrAuto,
computed_margin_end: LengthOrAuto, computed_margin_end: LengthOrAuto,
solve_margins: impl FnOnce(Length) -> (Length, Length), solve_margins: impl FnOnce(Length) -> (Length, Length),
box_offsets: AbsoluteBoxOffsets<LengthOrPercentage>, box_offsets: AbsoluteBoxOffsets<LengthPercentage>,
size: LengthOrPercentageOrAuto, size: LengthPercentageOrAuto,
) -> (Anchor, LengthOrAuto, Length, Length) { ) -> (Anchor, LengthOrAuto, Length, Length) {
let size = size.percentage_relative_to(containing_size); let size = size.percentage_relative_to(containing_size);
match box_offsets { match box_offsets {
@ -163,40 +174,42 @@ impl<'a> AbsolutelyPositionedFragment<'a> {
let mut margin_start = computed_margin_start.auto_is(Length::zero); let mut margin_start = computed_margin_start.auto_is(Length::zero);
let mut margin_end = computed_margin_end.auto_is(Length::zero); let mut margin_end = computed_margin_end.auto_is(Length::zero);
let size = if let LengthOrAuto::Length(size) = size { let size = if let LengthOrAuto::LengthPercentage(size) = size {
use LengthOrAuto::Auto;
let margins = containing_size - start - end - padding_border_sum - size; let margins = containing_size - start - end - padding_border_sum - size;
match (computed_margin_start, computed_margin_end) { match (computed_margin_start, computed_margin_end) {
(Auto, Auto) => { (LengthOrAuto::Auto, LengthOrAuto::Auto) => {
let (s, e) = solve_margins(margins); let (s, e) = solve_margins(margins);
margin_start = s; margin_start = s;
margin_end = e; margin_end = e;
} },
(Auto, LengthOrAuto::Length(end)) => { (LengthOrAuto::Auto, LengthOrAuto::LengthPercentage(end)) => {
margin_start = margins - end; margin_start = margins - end;
} },
(LengthOrAuto::Length(start), Auto) => { (LengthOrAuto::LengthPercentage(start), LengthOrAuto::Auto) => {
margin_end = margins - start; margin_end = margins - start;
} },
(LengthOrAuto::Length(_), LengthOrAuto::Length(_)) => {} (
LengthOrAuto::LengthPercentage(_),
LengthOrAuto::LengthPercentage(_),
) => {},
} }
size size
} else { } else {
// FIXME(nox): What happens if that is negative? // FIXME(nox): What happens if that is negative?
containing_size containing_size -
- start start -
- end end -
- padding_border_sum padding_border_sum -
- margin_start margin_start -
- margin_end margin_end
}; };
( (
Anchor::Start(start), Anchor::Start(start),
LengthOrAuto::Length(size), LengthOrAuto::LengthPercentage(size),
margin_start, margin_start,
margin_end, margin_end,
) )
} },
} }
} }
@ -206,7 +219,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> {
computed_margin.inline_start, computed_margin.inline_start,
computed_margin.inline_end, computed_margin.inline_end,
|margins| { |margins| {
if margins.px >= 0. { if margins.px() >= 0. {
(margins / 2., margins / 2.) (margins / 2., margins / 2.)
} else { } else {
(Length::zero(), margins) (Length::zero(), margins)
@ -303,7 +316,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> {
} }
} }
pub(super) 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],
tree_rank_in_parent: usize, tree_rank_in_parent: usize,

View file

@ -1,28 +0,0 @@
use crate::text;
/// Origin at top-left corner, unit `1px`
pub struct CssPx;
pub use euclid::point2 as point;
pub use euclid::rect;
pub type Length<U> = euclid::Length<f32, U>;
pub type Point<U> = euclid::TypedPoint2D<f32, U>;
pub type Size<U> = euclid::TypedSize2D<f32, U>;
pub type Rect<U> = euclid::TypedRect<f32, U>;
pub type SideOffsets<U> = euclid::TypedSideOffsets2D<f32, U>;
pub type Scale<Src, Dest> = euclid::TypedScale<f32, Src, Dest>;
#[derive(Copy, Clone, PartialEq)]
pub struct RGBA(pub f32, pub f32, pub f32, pub f32);
pub struct TextRun<'a> {
pub segment: &'a text::ShapedSegment,
pub font_size: Length<CssPx>,
pub origin: Point<CssPx>,
}
impl From<cssparser::RGBA> for RGBA {
fn from(c: cssparser::RGBA) -> Self {
RGBA(c.red_f32(), c.green_f32(), c.blue_f32(), c.alpha_f32())
}
}

View file

@ -1,5 +1,9 @@
use super::*; /* This Source Code Form is subject to the terms of the Mozilla Public
use crate::dom::NodeId; * 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/. */
use crate::dom_traversal::NodeExt;
use style::context::SharedStyleContext;
#[derive(Debug)] #[derive(Debug)]
pub(super) enum ReplacedContent { pub(super) enum ReplacedContent {
@ -7,7 +11,10 @@ pub(super) enum ReplacedContent {
} }
impl ReplacedContent { impl ReplacedContent {
pub fn for_element(_element: NodeId, _context: &Context) -> Option<Self> { pub fn for_element<'dom, Node>(element: Node, _context: &SharedStyleContext) -> Option<Self>
where
Node: NodeExt<'dom>,
{
// FIXME: implement <img> etc. // FIXME: implement <img> etc.
None None
} }

View file

@ -0,0 +1,129 @@
/* 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/. */
use crate::geom::{flow_relative, physical};
use style::properties::ComputedValues;
use style::values::computed::{
Display as PackedDisplay, Length, LengthPercentage, LengthPercentageOrAuto, Size,
};
pub use style::computed_values::direction::T as Direction;
pub use style::computed_values::position::T as Position;
pub use style::computed_values::writing_mode::T as WritingMode;
pub use style::values::specified::box_::{DisplayInside, DisplayOutside};
#[derive(Clone, Copy, Eq, PartialEq)]
pub(crate) enum Display {
None,
Contents,
GeneratingBox(DisplayGeneratingBox),
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub(crate) enum DisplayGeneratingBox {
OutsideInside {
outside: DisplayOutside,
inside: DisplayInside,
// list_item: bool,
},
// Layout-internal display types go here:
// https://drafts.csswg.org/css-display-3/#layout-specific-display
}
pub(crate) trait ComputedValuesExt {
fn writing_mode(&self) -> (WritingMode, Direction);
fn box_offsets(&self) -> flow_relative::Sides<LengthPercentageOrAuto>;
fn box_size(&self) -> flow_relative::Vec2<LengthPercentageOrAuto>;
fn padding(&self) -> flow_relative::Sides<LengthPercentage>;
fn border_width(&self) -> flow_relative::Sides<Length>;
fn margin(&self) -> flow_relative::Sides<LengthPercentageOrAuto>;
}
impl ComputedValuesExt for ComputedValues {
fn writing_mode(&self) -> (WritingMode, Direction) {
let inherited_box = self.get_inherited_box();
let writing_mode = inherited_box.writing_mode;
let direction = inherited_box.direction;
(writing_mode, direction)
}
#[inline]
fn box_offsets(&self) -> flow_relative::Sides<LengthPercentageOrAuto> {
let position = self.get_position();
physical::Sides {
top: position.top,
left: position.left,
bottom: position.bottom,
right: position.right,
}
.to_flow_relative(self.writing_mode())
}
#[inline]
fn box_size(&self) -> flow_relative::Vec2<LengthPercentageOrAuto> {
let position = self.get_position();
physical::Vec2 {
x: size_to_length(position.width),
y: size_to_length(position.height),
}
.size_to_flow_relative(self.writing_mode())
}
#[inline]
fn padding(&self) -> flow_relative::Sides<LengthPercentage> {
let padding = self.get_padding();
physical::Sides {
top: padding.padding_top.0,
left: padding.padding_left.0,
bottom: padding.padding_bottom.0,
right: padding.padding_right.0,
}
.to_flow_relative(self.writing_mode())
}
fn border_width(&self) -> flow_relative::Sides<Length> {
let border = self.get_border();
physical::Sides {
top: border.border_top_width.0,
left: border.border_left_width.0,
bottom: border.border_bottom_width.0,
right: border.border_right_width.0,
}
.to_flow_relative(self.writing_mode())
}
fn margin(&self) -> flow_relative::Sides<LengthPercentageOrAuto> {
let margin = self.get_margin();
physical::Sides {
top: margin.margin_top,
left: margin.margin_left,
bottom: margin.margin_bottom,
right: margin.margin_right,
}
.to_flow_relative(self.writing_mode())
}
}
impl From<PackedDisplay> for Display {
fn from(packed_display: PackedDisplay) -> Self {
if packed_display == PackedDisplay::None {
return Self::None;
}
if packed_display == PackedDisplay::Contents {
return Self::Contents;
}
Self::GeneratingBox(DisplayGeneratingBox::OutsideInside {
outside: packed_display.outside(),
inside: packed_display.inside(),
// list_item: packed_display.is_list_item(),
})
}
}
fn size_to_length(size: Size) -> LengthPercentageOrAuto {
match size {
Size::LengthPercentage(length) => LengthPercentageOrAuto::LengthPercentage(length.0),
Size::Auto => LengthPercentageOrAuto::Auto,
}
}

View file

@ -565,6 +565,7 @@ impl LayoutThread {
traversal_flags: TraversalFlags::empty(), traversal_flags: TraversalFlags::empty(),
snapshot_map: snapshot_map, snapshot_map: snapshot_map,
}, },
font_cache_thread: Mutex::new(self.font_cache_thread.clone()),
} }
} }

View file

@ -12,6 +12,7 @@ use crate::properties::longhands::float::computed_value::T as Float;
use crate::properties::longhands::overflow_x::computed_value::T as Overflow; use crate::properties::longhands::overflow_x::computed_value::T as Overflow;
use crate::properties::longhands::position::computed_value::T as Position; use crate::properties::longhands::position::computed_value::T as Position;
use crate::properties::{self, ComputedValues, StyleBuilder}; use crate::properties::{self, ComputedValues, StyleBuilder};
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
use crate::values::specified::box_::DisplayInside; use crate::values::specified::box_::DisplayInside;
use app_units::Au; use app_units::Au;
@ -183,6 +184,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
where where
E: TElement, E: TElement,
{ {
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
use crate::computed_values::list_style_position::T as ListStylePosition; use crate::computed_values::list_style_position::T as ListStylePosition;
let mut blockify = false; let mut blockify = false;
@ -205,6 +207,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
blockify_if!(self.style.floated()); blockify_if!(self.style.floated());
blockify_if!(self.style.out_of_flow_positioned()); blockify_if!(self.style.out_of_flow_positioned());
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
blockify_if!( blockify_if!(
self.style.pseudo.map_or(false, |p| p.is_marker()) && self.style.pseudo.map_or(false, |p| p.is_marker()) &&
self.style.get_parent_list().clone_list_style_position() == self.style.get_parent_list().clone_list_style_position() ==

View file

@ -20,9 +20,9 @@ use crate::Zero;
use app_units::Au; use app_units::Au;
use ordered_float::NotNan; use ordered_float::NotNan;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use std::ops::{Add, Neg}; use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub};
use style_traits::values::specified::AllowedNumericType; use style_traits::values::specified::AllowedNumericType;
use style_traits::{CssWriter, ToCss}; use style_traits::{CSSPixel, CssWriter, ToCss};
pub use super::image::Image; pub use super::image::Image;
pub use crate::values::specified::url::UrlOrNone; pub use crate::values::specified::url::UrlOrNone;
@ -206,20 +206,26 @@ impl LengthPercentage {
Some(Percentage(self.clamping_mode.clamp(self.percentage.0))) Some(Percentage(self.clamping_mode.clamp(self.percentage.0)))
} }
/// Resolves the percentage.
#[inline]
pub fn percentage_relative_to(&self, basis: Length) -> Length {
let length = self.unclamped_length().0 + basis.0 * self.percentage.0;
Length::new(self.clamping_mode.clamp(length))
}
/// Convert the computed value into used value. /// Convert the computed value into used value.
#[inline] #[inline]
pub fn maybe_to_used_value(&self, container_len: Option<Au>) -> Option<Au> { pub fn maybe_to_used_value(&self, container_len: Option<Length>) -> Option<Au> {
self.maybe_to_pixel_length(container_len).map(Au::from) self.maybe_percentage_relative_to(container_len)
.map(Au::from)
} }
/// If there are special rules for computing percentages in a value (e.g. /// If there are special rules for computing percentages in a value (e.g.
/// the height property), they apply whenever a calc() expression contains /// the height property), they apply whenever a calc() expression contains
/// percentages. /// percentages.
pub fn maybe_to_pixel_length(&self, container_len: Option<Au>) -> Option<Length> { pub fn maybe_percentage_relative_to(&self, container_len: Option<Length>) -> Option<Length> {
if self.has_percentage { if self.has_percentage {
let length = self.unclamped_length().px() + return Some(self.percentage_relative_to(container_len?));
container_len?.scale_by(self.percentage.0).to_f32_px();
return Some(Length::new(self.clamping_mode.clamp(length)));
} }
Some(self.length()) Some(self.length())
} }
@ -371,7 +377,7 @@ impl LengthPercentage {
/// Returns the used value as CSSPixelLength. /// Returns the used value as CSSPixelLength.
pub fn to_pixel_length(&self, containing_length: Au) -> Length { pub fn to_pixel_length(&self, containing_length: Au) -> Length {
self.maybe_to_pixel_length(Some(containing_length)).unwrap() self.percentage_relative_to(containing_length.into())
} }
/// Returns the clamped non-negative values. /// Returns the clamped non-negative values.
@ -488,6 +494,30 @@ impl LengthPercentageOrAuto {
} }
computed_length_percentage_or_auto!(LengthPercentage); computed_length_percentage_or_auto!(LengthPercentage);
/// Resolves the percentage.
#[inline]
pub fn percentage_relative_to(&self, basis: Length) -> LengthOrAuto {
use values::generics::length::LengthPercentageOrAuto::*;
match self {
LengthPercentage(length_percentage) => {
LengthPercentage(length_percentage.percentage_relative_to(basis))
},
Auto => Auto,
}
}
/// Maybe resolves the percentage.
#[inline]
pub fn maybe_percentage_relative_to(&self, basis: Option<Length>) -> LengthOrAuto {
use values::generics::length::LengthPercentageOrAuto::*;
match self {
LengthPercentage(length_percentage) => length_percentage
.maybe_percentage_relative_to(basis)
.map_or(Auto, LengthPercentage),
Auto => Auto,
}
}
} }
/// A wrapper of LengthPercentageOrAuto, whose value must be >= 0. /// A wrapper of LengthPercentageOrAuto, whose value must be >= 0.
@ -555,7 +585,9 @@ impl NonNegativeLengthPercentage {
/// Convert the computed value into used value. /// Convert the computed value into used value.
#[inline] #[inline]
pub fn maybe_to_used_value(&self, containing_length: Option<Au>) -> Option<Au> { pub fn maybe_to_used_value(&self, containing_length: Option<Au>) -> Option<Au> {
let resolved = self.0.maybe_to_used_value(containing_length)?; let resolved = self
.0
.maybe_to_used_value(containing_length.map(|v| v.into()))?;
Some(::std::cmp::max(resolved, Au(0))) Some(::std::cmp::max(resolved, Au(0)))
} }
} }
@ -644,6 +676,23 @@ impl CSSPixelLength {
pub fn clamp_to_non_negative(self) -> Self { pub fn clamp_to_non_negative(self) -> Self {
CSSPixelLength::new(self.0.max(0.)) CSSPixelLength::new(self.0.max(0.))
} }
/// Returns the minimum between `self` and `other`.
#[inline]
pub fn min(self, other: Self) -> Self {
CSSPixelLength::new(self.0.min(other.0))
}
/// Returns the maximum between `self` and `other`.
#[inline]
pub fn max(self, other: Self) -> Self {
CSSPixelLength::new(self.0.max(other.0))
}
/// Sets `self` to the maximum between `self` and `other`.
pub fn max_assign(&mut self, other: Self) {
*self = self.max(other);
}
} }
impl Zero for CSSPixelLength { impl Zero for CSSPixelLength {
@ -676,6 +725,31 @@ impl Add for CSSPixelLength {
} }
} }
impl AddAssign for CSSPixelLength {
#[inline]
fn add_assign(&mut self, other: Self) {
self.0 += other.0;
}
}
impl Div<CSSFloat> for CSSPixelLength {
type Output = Self;
#[inline]
fn div(self, other: CSSFloat) -> Self {
Self::new(self.px() / other)
}
}
impl Mul<CSSFloat> for CSSPixelLength {
type Output = Self;
#[inline]
fn mul(self, other: CSSFloat) -> Self {
Self::new(self.px() * other)
}
}
impl Neg for CSSPixelLength { impl Neg for CSSPixelLength {
type Output = Self; type Output = Self;
@ -685,6 +759,15 @@ impl Neg for CSSPixelLength {
} }
} }
impl Sub for CSSPixelLength {
type Output = Self;
#[inline]
fn sub(self, other: Self) -> Self {
Self::new(self.px() - other.px())
}
}
impl From<CSSPixelLength> for Au { impl From<CSSPixelLength> for Au {
#[inline] #[inline]
fn from(len: CSSPixelLength) -> Self { fn from(len: CSSPixelLength) -> Self {
@ -699,6 +782,13 @@ impl From<Au> for CSSPixelLength {
} }
} }
impl From<CSSPixelLength> for euclid::Length<CSSFloat, CSSPixel> {
#[inline]
fn from(length: CSSPixelLength) -> Self {
Self::new(length.0)
}
}
/// An alias of computed `<length>` value. /// An alias of computed `<length>` value.
pub type Length = CSSPixelLength; pub type Length = CSSPixelLength;

View file

@ -69,6 +69,39 @@ impl<LengthPercentage> LengthPercentageOrAuto<LengthPercentage> {
} }
} }
impl<LengthPercentage> LengthPercentageOrAuto<LengthPercentage>
where
LengthPercentage: Clone,
{
/// Resolves `auto` values by calling `f`.
#[inline]
pub fn auto_is(&self, f: impl Fn() -> LengthPercentage) -> LengthPercentage {
match self {
LengthPercentageOrAuto::LengthPercentage(length) => length.clone(),
LengthPercentageOrAuto::Auto => f(),
}
}
/// Returns the non-`auto` value, if any.
#[inline]
pub fn non_auto(&self) -> Option<LengthPercentage> {
match self {
LengthPercentageOrAuto::LengthPercentage(length) => Some(length.clone()),
LengthPercentageOrAuto::Auto => None,
}
}
/// Maps the length of this value.
pub fn map(&self, f: impl FnOnce(LengthPercentage) -> LengthPercentage) -> Self {
match self {
LengthPercentageOrAuto::LengthPercentage(l) => {
LengthPercentageOrAuto::LengthPercentage(f(l.clone()))
},
LengthPercentageOrAuto::Auto => LengthPercentageOrAuto::Auto,
}
}
}
impl<LengthPercentage: Zero> Zero for LengthPercentageOrAuto<LengthPercentage> { impl<LengthPercentage: Zero> Zero for LengthPercentageOrAuto<LengthPercentage> {
fn zero() -> Self { fn zero() -> Self {
LengthPercentageOrAuto::LengthPercentage(Zero::zero()) LengthPercentageOrAuto::LengthPercentage(Zero::zero())

View file

@ -44,7 +44,9 @@ pub enum DisplayOutside {
None = 0, None = 0,
Inline, Inline,
Block, Block,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
TableCaption, TableCaption,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
InternalTable, InternalTable,
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
InternalRuby, InternalRuby,
@ -57,21 +59,32 @@ pub enum DisplayOutside {
#[repr(u8)] #[repr(u8)]
pub enum DisplayInside { pub enum DisplayInside {
None = 0, None = 0,
#[cfg(feature = "gecko")] #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
Contents, Contents,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
Block, Block,
FlowRoot, FlowRoot,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
Inline, Inline,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
Flex, Flex,
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
Grid, Grid,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
Table, Table,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
TableRowGroup, TableRowGroup,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
TableColumn, TableColumn,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
TableColumnGroup, TableColumnGroup,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
TableHeaderGroup, TableHeaderGroup,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
TableFooterGroup, TableFooterGroup,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
TableRow, TableRow,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
TableCell, TableCell,
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
Ruby, Ruby,
@ -138,21 +151,32 @@ impl Display {
/// https://drafts.csswg.org/css-display/#the-display-properties /// https://drafts.csswg.org/css-display/#the-display-properties
pub const None: Self = Self::new(DisplayOutside::None, DisplayInside::None); pub const None: Self = Self::new(DisplayOutside::None, DisplayInside::None);
#[cfg(feature = "gecko")] #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
pub const Contents: Self = Self::new(DisplayOutside::None, DisplayInside::Contents); pub const Contents: Self = Self::new(DisplayOutside::None, DisplayInside::Contents);
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const Inline: Self = Self::new(DisplayOutside::Inline, DisplayInside::Inline); pub const Inline: Self = Self::new(DisplayOutside::Inline, DisplayInside::Inline);
#[cfg(any(feature = "servo-layout-2020"))]
pub const Inline: Self = Self::new(DisplayOutside::Inline, DisplayInside::Flow);
pub const InlineBlock: Self = Self::new(DisplayOutside::Inline, DisplayInside::FlowRoot); pub const InlineBlock: Self = Self::new(DisplayOutside::Inline, DisplayInside::FlowRoot);
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const Block: Self = Self::new(DisplayOutside::Block, DisplayInside::Block); pub const Block: Self = Self::new(DisplayOutside::Block, DisplayInside::Block);
#[cfg(any(feature = "servo-layout-2020"))]
pub const Block: Self = Self::new(DisplayOutside::Block, DisplayInside::Flow);
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub const FlowRoot: Self = Self::new(DisplayOutside::Block, DisplayInside::FlowRoot); pub const FlowRoot: Self = Self::new(DisplayOutside::Block, DisplayInside::FlowRoot);
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const Flex: Self = Self::new(DisplayOutside::Block, DisplayInside::Flex); pub const Flex: Self = Self::new(DisplayOutside::Block, DisplayInside::Flex);
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const InlineFlex: Self = Self::new(DisplayOutside::Inline, DisplayInside::Flex); pub const InlineFlex: Self = Self::new(DisplayOutside::Inline, DisplayInside::Flex);
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub const Grid: Self = Self::new(DisplayOutside::Block, DisplayInside::Grid); pub const Grid: Self = Self::new(DisplayOutside::Block, DisplayInside::Grid);
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub const InlineGrid: Self = Self::new(DisplayOutside::Inline, DisplayInside::Grid); pub const InlineGrid: Self = Self::new(DisplayOutside::Inline, DisplayInside::Grid);
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const Table: Self = Self::new(DisplayOutside::Block, DisplayInside::Table); pub const Table: Self = Self::new(DisplayOutside::Block, DisplayInside::Table);
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const InlineTable: Self = Self::new(DisplayOutside::Inline, DisplayInside::Table); pub const InlineTable: Self = Self::new(DisplayOutside::Inline, DisplayInside::Table);
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const TableCaption: Self = Self::new(DisplayOutside::TableCaption, DisplayInside::Block); pub const TableCaption: Self = Self::new(DisplayOutside::TableCaption, DisplayInside::Block);
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub const Ruby: Self = Self::new(DisplayOutside::Inline, DisplayInside::Ruby); pub const Ruby: Self = Self::new(DisplayOutside::Inline, DisplayInside::Ruby);
@ -160,25 +184,39 @@ impl Display {
pub const WebkitBox: Self = Self::new(DisplayOutside::Block, DisplayInside::WebkitBox); pub const WebkitBox: Self = Self::new(DisplayOutside::Block, DisplayInside::WebkitBox);
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub const WebkitInlineBox: Self = Self::new(DisplayOutside::Inline, DisplayInside::WebkitBox); pub const WebkitInlineBox: Self = Self::new(DisplayOutside::Inline, DisplayInside::WebkitBox);
/// Internal table boxes.
// Internal table boxes.
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const TableRowGroup: Self = pub const TableRowGroup: Self =
Self::new(DisplayOutside::InternalTable, DisplayInside::TableRowGroup); Self::new(DisplayOutside::InternalTable, DisplayInside::TableRowGroup);
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const TableHeaderGroup: Self = Self::new( pub const TableHeaderGroup: Self = Self::new(
DisplayOutside::InternalTable, DisplayOutside::InternalTable,
DisplayInside::TableHeaderGroup, DisplayInside::TableHeaderGroup,
); );
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const TableFooterGroup: Self = Self::new( pub const TableFooterGroup: Self = Self::new(
DisplayOutside::InternalTable, DisplayOutside::InternalTable,
DisplayInside::TableFooterGroup, DisplayInside::TableFooterGroup,
); );
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const TableColumn: Self = pub const TableColumn: Self =
Self::new(DisplayOutside::InternalTable, DisplayInside::TableColumn); Self::new(DisplayOutside::InternalTable, DisplayInside::TableColumn);
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const TableColumnGroup: Self = Self::new( pub const TableColumnGroup: Self = Self::new(
DisplayOutside::InternalTable, DisplayOutside::InternalTable,
DisplayInside::TableColumnGroup, DisplayInside::TableColumnGroup,
); );
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const TableRow: Self = Self::new(DisplayOutside::InternalTable, DisplayInside::TableRow); pub const TableRow: Self = Self::new(DisplayOutside::InternalTable, DisplayInside::TableRow);
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const TableCell: Self = Self::new(DisplayOutside::InternalTable, DisplayInside::TableCell); pub const TableCell: Self = Self::new(DisplayOutside::InternalTable, DisplayInside::TableCell);
/// Internal ruby boxes. /// Internal ruby boxes.
@ -235,6 +273,7 @@ impl Display {
#[inline] #[inline]
fn from3(outside: DisplayOutside, inside: DisplayInside, list_item: bool) -> Self { fn from3(outside: DisplayOutside, inside: DisplayInside, list_item: bool) -> Self {
let inside = match inside { let inside = match inside {
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
DisplayInside::Flow => match outside { DisplayInside::Flow => match outside {
DisplayOutside::Inline => DisplayInside::Inline, DisplayOutside::Inline => DisplayInside::Inline,
_ => DisplayInside::Block, _ => DisplayInside::Block,
@ -307,6 +346,7 @@ impl Display {
pub fn is_atomic_inline_level(&self) -> bool { pub fn is_atomic_inline_level(&self) -> bool {
match *self { match *self {
Display::InlineBlock => true, Display::InlineBlock => true,
#[cfg(any(feature = "servo-layout-2013"))]
Display::InlineFlex | Display::InlineTable => true, Display::InlineFlex | Display::InlineTable => true,
_ => false, _ => false,
} }
@ -318,6 +358,7 @@ impl Display {
/// This is used to implement various style fixups. /// This is used to implement various style fixups.
pub fn is_item_container(&self) -> bool { pub fn is_item_container(&self) -> bool {
match self.inside() { match self.inside() {
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
DisplayInside::Flex => true, DisplayInside::Flex => true,
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
DisplayInside::Grid => true, DisplayInside::Grid => true,
@ -352,12 +393,16 @@ impl Display {
match self.outside() { match self.outside() {
DisplayOutside::Inline => { DisplayOutside::Inline => {
let inside = match self.inside() { let inside = match self.inside() {
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
DisplayInside::Inline | DisplayInside::FlowRoot => DisplayInside::Block, DisplayInside::Inline | DisplayInside::FlowRoot => DisplayInside::Block,
#[cfg(feature = "servo-layout-2020")]
DisplayInside::FlowRoot => DisplayInside::Flow,
inside => inside, inside => inside,
}; };
Display::from3(DisplayOutside::Block, inside, self.is_list_item()) Display::from3(DisplayOutside::Block, inside, self.is_list_item())
}, },
DisplayOutside::Block | DisplayOutside::None => *self, DisplayOutside::Block | DisplayOutside::None => *self,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
_ => Display::Block, _ => Display::Block,
} }
} }
@ -413,6 +458,7 @@ impl ToCss for Display {
); );
let outside = self.outside(); let outside = self.outside();
let inside = match self.inside() { let inside = match self.inside() {
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
DisplayInside::Block | DisplayInside::Inline => DisplayInside::Flow, DisplayInside::Block | DisplayInside::Inline => DisplayInside::Flow,
inside => inside, inside => inside,
}; };
@ -427,10 +473,12 @@ impl ToCss for Display {
Display::MozInlineGrid => dest.write_str("-moz-inline-grid"), Display::MozInlineGrid => dest.write_str("-moz-inline-grid"),
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
Display::MozInlineStack => dest.write_str("-moz-inline-stack"), Display::MozInlineStack => dest.write_str("-moz-inline-stack"),
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
Display::TableCaption => dest.write_str("table-caption"), Display::TableCaption => dest.write_str("table-caption"),
_ => match (outside, inside) { _ => match (outside, inside) {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
(DisplayOutside::Inline, DisplayInside::Grid) => dest.write_str("inline-grid"), (DisplayOutside::Inline, DisplayInside::Grid) => dest.write_str("inline-grid"),
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
(DisplayOutside::Inline, DisplayInside::Flex) | (DisplayOutside::Inline, DisplayInside::Flex) |
(DisplayOutside::Inline, DisplayInside::Table) => { (DisplayOutside::Inline, DisplayInside::Table) => {
dest.write_str("inline-")?; dest.write_str("inline-")?;
@ -465,9 +513,11 @@ fn parse_display_inside<'i, 't>(
) -> Result<DisplayInside, ParseError<'i>> { ) -> Result<DisplayInside, ParseError<'i>> {
Ok(try_match_ident_ignore_ascii_case! { input, Ok(try_match_ident_ignore_ascii_case! { input,
"flow" => DisplayInside::Flow, "flow" => DisplayInside::Flow,
#[cfg(feature = "gecko")] #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
"flow-root" => DisplayInside::FlowRoot, "flow-root" => DisplayInside::FlowRoot,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"table" => DisplayInside::Table, "table" => DisplayInside::Table,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"flex" => DisplayInside::Flex, "flex" => DisplayInside::Flex,
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
"grid" => DisplayInside::Grid, "grid" => DisplayInside::Grid,
@ -569,18 +619,29 @@ impl Parse for Display {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
"contents" => Display::Contents, "contents" => Display::Contents,
"inline-block" => Display::InlineBlock, "inline-block" => Display::InlineBlock,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"inline-table" => Display::InlineTable, "inline-table" => Display::InlineTable,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"-webkit-flex" => Display::Flex, "-webkit-flex" => Display::Flex,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"inline-flex" | "-webkit-inline-flex" => Display::InlineFlex, "inline-flex" | "-webkit-inline-flex" => Display::InlineFlex,
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
"inline-grid" => Display::InlineGrid, "inline-grid" => Display::InlineGrid,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"table-caption" => Display::TableCaption, "table-caption" => Display::TableCaption,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"table-row-group" => Display::TableRowGroup, "table-row-group" => Display::TableRowGroup,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"table-header-group" => Display::TableHeaderGroup, "table-header-group" => Display::TableHeaderGroup,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"table-footer-group" => Display::TableFooterGroup, "table-footer-group" => Display::TableFooterGroup,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"table-column" => Display::TableColumn, "table-column" => Display::TableColumn,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"table-column-group" => Display::TableColumnGroup, "table-column-group" => Display::TableColumnGroup,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"table-row" => Display::TableRow, "table-row" => Display::TableRow,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"table-cell" => Display::TableCell, "table-cell" => Display::TableCell,
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
"ruby-base" => Display::RubyBase, "ruby-base" => Display::RubyBase,