mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Import victor's layout system 🍷
This commit is contained in:
parent
86904757e6
commit
4444c5a2ad
25 changed files with 1270 additions and 712 deletions
1
.mailmap
1
.mailmap
|
@ -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
21
Cargo.lock
generated
|
@ -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"
|
||||||
|
|
|
@ -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())))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"}
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 don’t keep pointers to boxes generated by contents of pseudo-elements
|
// Contents::Replaced(contents),
|
||||||
BoxSlot::dummy(),
|
// // We don’t 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 wasn’t one, then descendants don’t have boxes either.
|
// If there wasn’t one, then descendants don’t 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 {
|
||||||
// Don’t go outside the subtree
|
// Don’t go outside the subtree.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 rayon’s `into_par_iter`.
|
/// Deferring allows using rayon’s `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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 we’re over-constrained and we do as if it were.
|
// or we’re 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,
|
||||||
|
|
|
@ -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 document’s mode:
|
// // FIXME: use the document’s mode:
|
||||||
// https://drafts.csswg.org/css-writing-modes/#principal-flow
|
// // https://drafts.csswg.org/css-writing-modes/#principal-flow
|
||||||
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
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 it’s 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())
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
129
components/layout_2020/style_ext.rs
Normal file
129
components/layout_2020/style_ext.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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() ==
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue