From 5477a0d24d6053e15dceeb4f3bd4d5609f40c5c7 Mon Sep 17 00:00:00 2001 From: Eric Walker Date: Sat, 25 Nov 2023 12:40:15 -0700 Subject: [PATCH] Add skeleton for layout thread reflow --- src/compositing.rs | 51 +++++++++ src/css3.rs | 7 ++ src/css3/parser.rs | 218 ++++++++++++++++++++---------------- src/layout.rs | 124 ++++++++++++++++++++ src/layout/layout_thread.rs | 64 +++++++++++ src/lib.rs | 18 +-- 6 files changed, 379 insertions(+), 103 deletions(-) create mode 100644 src/compositing.rs create mode 100644 src/layout.rs create mode 100644 src/layout/layout_thread.rs diff --git a/src/compositing.rs b/src/compositing.rs new file mode 100644 index 000000000..f14ee9aa2 --- /dev/null +++ b/src/compositing.rs @@ -0,0 +1,51 @@ +use crate::layout::LayoutSize; +use crate::types::Result; + +// See https://github.com/servo/servo/blob/5d7ed76b79de359ef1de2bdee83b32bd497d7cd8/third_party/webrender/webrender_api/src/display_list.rs#L112 +pub struct BuiltDisplayList; + +impl BuiltDisplayList { + pub fn into_data(self) -> (Vec, BuiltDisplayListDescriptor) { + (Vec::new(), BuiltDisplayListDescriptor) + } +} + +// See https://github.com/servo/servo/blob/5d7ed76b79de359ef1de2bdee83b32bd497d7cd8/third_party/webrender/webrender_api/src/display_list.rs#L136 +pub struct BuiltDisplayListDescriptor; + +// See ipc_channel crate +pub struct IpcSender; + +impl IpcSender { + pub fn send(&self, _message: ScriptToCompositorMsg) -> Result<()> { + todo!() + } +} + +// See https://github.com/servo/servo/blob/5d7ed76b79de359ef1de2bdee83b32bd497d7cd8/components/shared/script/compositor.rs#L216 +pub struct CompositorDisplayListInfo { + pub content_size: LayoutSize, +} + +// See https://github.com/servo/servo/blob/5d7ed76b79de359ef1de2bdee83b32bd497d7cd8/components/shared/script/lib.rs#L1119 +pub enum ScriptToCompositorMsg { + SendDisplayList { + display_list_info: CompositorDisplayListInfo, + display_list_descriptor: BuiltDisplayListDescriptor, + }, +} + +// See https://github.com/servo/servo/blob/5d7ed76b79de359ef1de2bdee83b32bd497d7cd8/components/shared/compositing/lib.rs#L182 +pub enum ForwardedToCompositorMsg { + Layout(ScriptToCompositorMsg), +} + +// See https://github.com/servo/servo/blob/5d7ed76b79de359ef1de2bdee83b32bd497d7cd8/components/compositing/compositor.rs#L128 +pub struct IOCompositor; + +impl IOCompositor { + #[allow(unused)] + fn handle_webrender_message(&self, _message: ForwardedToCompositorMsg) { + todo!() + } +} diff --git a/src/css3.rs b/src/css3.rs index f2b04378b..858608d12 100644 --- a/src/css3.rs +++ b/src/css3.rs @@ -111,6 +111,9 @@ //! ; //! ``` //! +use self::{node::StyleSheet, parser::CSS3Parser}; +use crate::types::Result; + pub mod new_parser; pub mod new_tokenizer; pub mod node; @@ -118,3 +121,7 @@ pub mod parser; pub mod tokenizer; pub mod tokens; pub mod unicode; + +pub fn parse(s: &str) -> Result { + CSS3Parser::new().parse(s) +} diff --git a/src/css3/parser.rs b/src/css3/parser.rs index 88de2a874..b69a408e1 100644 --- a/src/css3/parser.rs +++ b/src/css3/parser.rs @@ -1,13 +1,13 @@ +use super::node::{ + AttributeMatcher, AttributeSelector, ClassSelector, Combinator, CssString, TypeSelector, +}; use crate::css3::node::{ Block, BlockChild, Declaration, DeclarationList, Dimension, IdSelector, Identifier, Rule, Selector, SelectorList, StyleSheet, StyleSheetRule, Value, ValueList, }; use crate::css3::tokenizer::Tokenizer; use crate::css3::tokens::{Token, TokenType}; - -use super::node::{ - AttributeMatcher, AttributeSelector, ClassSelector, Combinator, CssString, TypeSelector, -}; +use crate::types::Result; macro_rules! unexpected_token { ($expecting:expr, $received:expr) => { @@ -47,12 +47,12 @@ impl CSS3Parser { } } - pub fn parse(&mut self, raw: &str) -> StyleSheet { + pub fn parse(&mut self, raw: &str) -> Result { self.raw = raw.to_string(); self.tokenizer.init(raw); self.lookahead = self.tokenizer.get_next_token(); self.skip_whitespace(); - self.style_sheet() + Ok(self.style_sheet()) } /// ```txt @@ -168,7 +168,7 @@ impl CSS3Parser { /// ```bnf /// TypeSelector /// : IDENT - /// ; + /// ; /// ``` fn type_selector(&mut self) -> TypeSelector { TypeSelector::new(self.consume_token(TokenType::Ident).value) @@ -177,7 +177,7 @@ impl CSS3Parser { /// ```bnf /// IdSelector /// : HASH IDENT - /// ; + /// ; /// ``` fn id_selector(&mut self) -> IdSelector { self.consume_token(TokenType::Hash); @@ -188,7 +188,7 @@ impl CSS3Parser { /// ```bnf /// ClassSelector /// : DOT IDENT - /// ; + /// ; /// ``` fn class_selector(&mut self) -> ClassSelector { self.consume_token(TokenType::Dot); @@ -199,7 +199,7 @@ impl CSS3Parser { /// ```bnf /// AttributeSelector /// : LBRACKET IDENT [AttributeMatcher String]? [IDENT]? RBRACKET - /// ; + /// ; /// ``` fn attribute_selector(&mut self) -> AttributeSelector { self.consume_token(TokenType::LBracket); @@ -245,7 +245,7 @@ impl CSS3Parser { /// | SUFFIX_MATCH /// | SUBSTRING_MATCH /// | EQUAL - /// ; + /// ; /// ``` fn attribute_matcher(&mut self) -> AttributeMatcher { if let Some(next_token_type) = self.get_next_token_type() { @@ -318,7 +318,7 @@ impl CSS3Parser { /// ```bnf /// Block /// : LCURLY [Rule | AtRule | DeclarationList]* RCURLY - /// ; + /// ; /// ``` fn block(&mut self) -> Block { // note: add support for 'DeclarationList' for now @@ -338,7 +338,7 @@ impl CSS3Parser { /// ```bnf /// DeclarationList /// : [Declaration]* - /// ; + /// ; /// ``` fn declaration_list(&mut self) -> DeclarationList { let mut declaration_list = DeclarationList::default(); @@ -353,7 +353,7 @@ impl CSS3Parser { /// ```bnf /// Declaration /// : IDENT COLON ValueList IMPORTANT SEMICOLON - /// ; + /// ; /// ``` fn declaration(&mut self) -> Declaration { let mut declaration = Declaration::default(); @@ -377,7 +377,7 @@ impl CSS3Parser { /// ```bnf /// ValueList /// : [Value]* - /// ; + /// ; /// ``` fn value_list(&mut self) -> ValueList { let mut value_list = ValueList::default(); @@ -394,7 +394,7 @@ impl CSS3Parser { /// ```bnf /// Value /// : [Dimension | Identifier | Function] - /// ; + /// ; /// ``` fn value(&mut self) -> Value { // note: support only "Identifier" and "Dimension" for now @@ -410,7 +410,7 @@ impl CSS3Parser { /// ```bnf /// Identifier /// : IDENT - /// ; + /// ; /// ``` fn identifier(&mut self) -> Identifier { Identifier::new(self.consume_token(TokenType::Ident).value) @@ -419,7 +419,7 @@ impl CSS3Parser { /// ```bnf /// Dimension /// : NUMBER IDENT - /// ; + /// ; /// ``` fn dimension(&mut self) -> Dimension { let value = self.consume_token(TokenType::Number).value; @@ -512,16 +512,18 @@ mod test { #[test] fn parse_css() { let mut parser = CSS3Parser::new(); - let style_sheet = parser.parse( - r#" - + let style_sheet = parser + .parse( + r#" + #header div > p { display: flex; width: 100px; font-size: 1rem !important; } "#, - ); + ) + .unwrap(); assert_eq!( style_sheet, @@ -563,15 +565,18 @@ mod test { #[test] fn parse_attribute_selectors() { let mut parser = CSS3Parser::new(); + let stylesheet = parser + .parse( + r##" + a { + color: blue; + } +"##, + ) + .unwrap(); assert_eq!( - parser.parse( - r##" - a { - color: blue; - } - "## - ), + stylesheet, StyleSheet::new(vec![StyleSheetRule::Rule(Rule::new( SelectorList::new(vec![Selector::TypeSelector(TypeSelector::new("a"))]), Block::new(vec![BlockChild::DeclarationList(DeclarationList::new( @@ -583,15 +588,19 @@ mod test { )),]) ); - assert_eq!( - parser.parse( + let stylesheet = parser + .parse( r##" - /* Internal links, beginning with "#" */ - a[href^="#"] { - background-color: gold; - } - "##, - ), + /* Internal links, beginning with "#" */ + a[href^="#"] { + background-color: gold; + } +"##, + ) + .unwrap(); + + assert_eq!( + stylesheet, StyleSheet::new(vec![StyleSheetRule::Rule(Rule::new( SelectorList::new(vec![ Selector::TypeSelector(TypeSelector::new("a")), @@ -611,15 +620,19 @@ mod test { )),]) ); - assert_eq!( - parser.parse( + let stylesheet = parser + .parse( r#" - /* Links with "example" anywhere in the URL */ - a[href*="example"] { - background-color: silver; - } - "# - ), + /* Links with "example" anywhere in the URL */ + a[href*="example"] { + background-color: silver; + } + "#, + ) + .unwrap(); + + assert_eq!( + stylesheet, StyleSheet::new(vec![StyleSheetRule::Rule(Rule::new( SelectorList::new(vec![ Selector::TypeSelector(TypeSelector::new("a")), @@ -639,16 +652,20 @@ mod test { )),]) ); - assert_eq!( - parser.parse( + let stylesheet = parser + .parse( r#" - /* Links with "insensitive" anywhere in the URL, - regardless of capitalization */ - a[href *= "insensitive" i] { - color: cyan; - } - "# - ), + /* Links with "insensitive" anywhere in the URL, + regardless of capitalization */ + a[href *= "insensitive" i] { + color: cyan; + } + "#, + ) + .unwrap(); + + assert_eq!( + stylesheet, StyleSheet::new(vec![StyleSheetRule::Rule(Rule::new( SelectorList::new(vec![ Selector::TypeSelector(TypeSelector::new("a")), @@ -668,16 +685,20 @@ mod test { )),]) ); - assert_eq!( - parser.parse( + let stylesheet = parser + .parse( r#" - /* Links with "cAsE" anywhere in the URL, - with matching capitalization */ - a[href*="cAsE" s] { - color: pink; - } - "#, - ), + /* Links with "cAsE" anywhere in the URL, + with matching capitalization */ + a[href*="cAsE" s] { + color: pink; + } + "#, + ) + .unwrap(); + + assert_eq!( + stylesheet, StyleSheet::new(vec![StyleSheetRule::Rule(Rule::new( SelectorList::new(vec![ Selector::TypeSelector(TypeSelector::new("a")), @@ -697,17 +718,21 @@ mod test { )),]) ); - assert_eq!( - parser.parse( + let stylesheet = parser + .parse( r#" - - /* Links that end in ".org" */ - a[href$=".org"] { - color: red; - } - "# - ), + /* Links that end in ".org" */ + a[href$=".org"] { + color: red; + } + + "#, + ) + .unwrap(); + + assert_eq!( + stylesheet, StyleSheet::new(vec![StyleSheetRule::Rule(Rule::new( SelectorList::new(vec![ Selector::TypeSelector(TypeSelector::new("a")), @@ -727,15 +752,19 @@ mod test { )),]) ); - assert_eq!( - parser.parse( + let stylesheet = parser + .parse( r#" - /* Links that start with "https://" and end in ".org" */ - a[href^="https://"][href$=".org"] { - color: green; - } - "# - ), + /* Links that start with "https://" and end in ".org" */ + a[href^="https://"][href$=".org"] { + color: green; + } + "#, + ) + .unwrap(); + + assert_eq!( + stylesheet, StyleSheet::new(vec![StyleSheetRule::Rule(Rule::new( SelectorList::new(vec![ Selector::TypeSelector(TypeSelector::new("a")), @@ -789,25 +818,26 @@ mod test { ))) } - assert_eq!( - parser.parse( + let stylesheet = parser + .parse( r##" - ul > li {} + ul > li {} - ul || li {} + ul || li {} - ul, li {} + ul, li {} - ul|li {} - - ul + li {} + ul|li {} - ul ~ li {} + ul + li {} - ul li {} - "## - ), - StyleSheet::new(rules) - ); + ul ~ li {} + + ul li {} +"##, + ) + .unwrap(); + + assert_eq!(stylesheet, StyleSheet::new(rules)); } } diff --git a/src/layout.rs b/src/layout.rs new file mode 100644 index 000000000..e4ed58f7d --- /dev/null +++ b/src/layout.rs @@ -0,0 +1,124 @@ +pub mod layout_thread; + +use crate::{ + compositing::{BuiltDisplayList, CompositorDisplayListInfo, IpcSender, ScriptToCompositorMsg}, + html5::node::Node, +}; +use log::warn; +use std::sync::mpsc::channel; + +// See https://github.com/servo/servo/blob/5d7ed76b79de359ef1de2bdee83b32bd497d7cd8/components/config/opts.rs#L149 +#[derive(Default)] +pub struct DebugOptions; + +// See https://github.com/servo/servo/blob/5d7ed76b79de359ef1de2bdee83b32bd497d7cd8/components/shared/script/lib.rs#L1150 +struct WebrenderIpcSender(IpcSender); + +impl WebrenderIpcSender { + fn new() -> Self { + Self(IpcSender) + } + + fn send_display_list( + &self, + display_list_info: CompositorDisplayListInfo, + list: BuiltDisplayList, + ) { + let (display_list_data, display_list_descriptor) = list.into_data(); + // TODO: Set up channel somewhere else + let (tx, _rx) = channel(); + + if let Err(err) = self.0.send(ScriptToCompositorMsg::SendDisplayList { + display_list_info, + display_list_descriptor, + }) { + warn!("error sending display list: {err}"); + } + + if let Err(err) = tx.send(display_list_data) { + warn!("error sending display data: {err}"); + } + } +} + +// See https://github.com/servo/servo/blob/5d7ed76b79de359ef1de2bdee83b32bd497d7cd8/third_party/webrender/webrender_api/src/display_list.rs#L1012 +struct WebrenderDisplayListBuilder; + +impl WebrenderDisplayListBuilder { + fn finalize(self) -> BuiltDisplayList { + BuiltDisplayList + } +} + +// See https://github.com/servo/servo/blob/5d7ed76b79de359ef1de2bdee83b32bd497d7cd8/components/layout_2020/fragment_tree/fragment_tree.rs#L22 +#[derive(Default)] +struct FragmentTree; + +impl FragmentTree { + fn scrollable_overflow(&self) -> LayoutSize { + LayoutSize + } +} + +// See https://github.com/servo/servo/blob/5d7ed76b79de359ef1de2bdee83b32bd497d7cd8/components/layout_2020/flow/root.rs#L32 +struct BoxTree; + +impl BoxTree { + fn construct(_context: &LayoutContext, _node: &Node) -> Self { + Self + } + + fn layout(&self) -> FragmentTree { + todo!() + } +} + +// See https://github.com/servo/servo/blob/5d7ed76b79de359ef1de2bdee83b32bd497d7cd8/third_party/webrender/webrender_api/src/units.rs#L91 +pub struct LayoutSize; + +// See https://github.com/servo/servo/blob/5d7ed76b79de359ef1de2bdee83b32bd497d7cd8/components/layout_2020/display_list/mod.rs#L57 +pub struct DisplayList { + compositor_info: CompositorDisplayListInfo, + builder: WebrenderDisplayListBuilder, +} + +impl DisplayList { + fn new(content_size: LayoutSize) -> Self { + Self { + compositor_info: CompositorDisplayListInfo { content_size }, + builder: WebrenderDisplayListBuilder, + } + } + + fn build_stacking_context_tree( + &self, + _fragment_tree: &FragmentTree, + _debug: &DebugOptions, + ) -> StackingContext { + StackingContext + } + + fn build( + &self, + _context: &LayoutContext, + _fratment_tree: &FragmentTree, + _stacking_context: &StackingContext, + ) { + todo!() + } +} + +// See https://github.com/servo/servo/blob/5d7ed76b79de359ef1de2bdee83b32bd497d7cd8/components/layout_2020/display_list/stacking_context.rs#L269 +pub struct StackingContext; + +// See https://github.com/servo/servo/blob/5d7ed76b79de359ef1de2bdee83b32bd497d7cd8/components/shared/script_layout/message.rs#L122 +#[derive(Debug, PartialEq)] +pub enum ReflowGoal { + Full, + TickAnimations, + LayoutQuery, + UpdateScrollNode, +} + +// See https://github.com/servo/servo/blob/5d7ed76b79de359ef1de2bdee83b32bd497d7cd8/components/layout_2020/context.rs#L23 +pub struct LayoutContext; diff --git a/src/layout/layout_thread.rs b/src/layout/layout_thread.rs new file mode 100644 index 000000000..ada50e1a3 --- /dev/null +++ b/src/layout/layout_thread.rs @@ -0,0 +1,64 @@ +use crate::html5::node::NodeId; +use crate::html5::parser::document::DocumentHandle; + +use super::{BoxTree, DebugOptions, DisplayList, LayoutContext, WebrenderIpcSender}; + +// See https://github.com/servo/servo/blob/5d7ed76b79de359ef1de2bdee83b32bd497d7cd8/components/layout_thread_2020/lib.rs#L275 +pub struct ScriptReflowResult; + +pub struct ScriptReflowRequest { + document: DocumentHandle, +} + +// See https://github.com/servo/servo/blob/5d7ed76b79de359ef1de2bdee83b32bd497d7cd8/components/layout_thread/lib.rs#L113 +pub struct LayoutThread { + webrender_api: WebrenderIpcSender, + debug: DebugOptions, +} + +impl Default for LayoutThread { + fn default() -> Self { + Self::new() + } +} + +impl LayoutThread { + pub fn new() -> Self { + Self { + webrender_api: WebrenderIpcSender::new(), + debug: Default::default(), + } + } + + // See https://github.com/servo/servo/blob/5d7ed76b79de359ef1de2bdee83b32bd497d7cd8/components/layout_thread_2020/lib.rs#L792 + #[allow(unused)] + fn handle_reflow(&self, data: &ScriptReflowRequest) -> ScriptReflowResult { + let mut context = self.build_layout_context(); + let document = data.document.get(); + let root_node = document + .get_node_by_id(NodeId::root()) + .expect("no root node"); + + let box_tree = BoxTree::construct(&context, root_node); + let fragment_tree = box_tree.layout(); + let mut display_list = DisplayList::new(fragment_tree.scrollable_overflow()); + + let root_stacking_context = + display_list.build_stacking_context_tree(&fragment_tree, &self.debug); + display_list.build(&context, &fragment_tree, &root_stacking_context); + + let DisplayList { + compositor_info, + builder, + } = display_list; + + self.webrender_api + .send_display_list(compositor_info, builder.finalize()); + + ScriptReflowResult + } + + fn build_layout_context(&self) -> LayoutContext { + LayoutContext + } +} diff --git a/src/lib.rs b/src/lib.rs index 9e17c4f17..d83610883 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,22 +3,22 @@ extern crate core; pub mod api; pub mod bytes; -#[allow(dead_code)] -pub mod css3; -#[allow(dead_code)] -pub mod html5; -pub mod render_tree; -pub mod testing; -pub mod types; - +pub mod compositing; #[allow(dead_code)] pub mod config; #[allow(dead_code)] +pub mod css3; +#[allow(dead_code)] mod dns; - #[allow(dead_code)] mod engine; #[allow(dead_code)] +pub mod html5; +pub mod layout; +#[allow(dead_code)] mod net; +pub mod render_tree; +pub mod testing; #[allow(dead_code)] mod timing; +pub mod types;