From 3029fbab922d7137f6701b4445c1a2cb1f7c7bfb Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 9 Dec 2014 12:12:50 -0800 Subject: [PATCH] layout: Implement basic lists and the CSS1 list properties. The exact rendering is ill-spec'd. Some things are ugly (especially the width and height of list style images) but they are infrequently used and I believe this implementation matches the spec. Numeric lists are not supported yet, since they will require a separate layout pass. The implementation is a subclass of `BlockFlow`, on advice from Robert O'Callahan. --- components/layout/block.rs | 14 +-- components/layout/construct.rs | 135 ++++++++++++++++----- components/layout/display_list_builder.rs | 76 ++++++++++-- components/layout/flow.rs | 1 + components/layout/lib.rs | 1 + components/layout/list_item.rs | 135 +++++++++++++++++++++ components/layout/table_cell.rs | 2 +- components/style/properties/mod.rs.mako | 137 +++++++++++++++++++++- tests/ref/basic.list | 2 + tests/ref/list_style_position_a.html | 29 +++++ tests/ref/list_style_position_ref.html | 28 +++++ tests/ref/list_style_type_a.html | 19 +++ tests/ref/list_style_type_ref.html | 14 +++ tests/ref/smiling.png | Bin 0 -> 3390 bytes 14 files changed, 540 insertions(+), 53 deletions(-) create mode 100644 components/layout/list_item.rs create mode 100644 tests/ref/list_style_position_a.html create mode 100644 tests/ref/list_style_position_ref.html create mode 100644 tests/ref/list_style_type_a.html create mode 100644 tests/ref/list_style_type_ref.html create mode 100644 tests/ref/smiling.png diff --git a/components/layout/block.rs b/components/layout/block.rs index 61fff95ec09..079a1961073 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -30,7 +30,7 @@ use construct::FlowConstructor; use context::LayoutContext; use css::node_style::StyledNode; -use display_list_builder::{BlockFlowDisplayListBuilding, BlockLevel, FragmentDisplayListBuilding}; +use display_list_builder::{BlockFlowDisplayListBuilding, FragmentDisplayListBuilding}; use floats::{ClearBoth, ClearLeft, ClearRight, FloatKind, FloatLeft, Floats, PlacementInfo}; use flow::{AbsolutePositionInfo, BaseFlow, BlockFlowClass, FloatIfNecessary, FlowClass, Flow}; use flow::{ForceNonfloated, ImmutableFlowUtils, MutableFlowUtils, PreorderFlowTraversal}; @@ -51,6 +51,7 @@ use table::ColumnComputedInlineSize; use wrapper::ThreadSafeLayoutNode; use geom::Size2D; +use gfx::display_list::DisplayList; use serialize::{Encoder, Encodable}; use servo_msg::compositor_msg::LayerId; use servo_util::geometry::{Au, MAX_AU, MAX_RECT, ZERO_POINT}; @@ -1853,16 +1854,7 @@ impl Flow for BlockFlow { } fn build_display_list(&mut self, layout_context: &LayoutContext) { - if self.base.flags.is_float() { - // TODO(#2009, pcwalton): This is a pseudo-stacking context. We need to merge `z-index: - // auto` kids into the parent stacking context, when that is supported. - self.build_display_list_for_floating_block(layout_context) - } else if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) { - self.build_display_list_for_absolutely_positioned_block(layout_context) - } else { - self.build_display_list_for_block(layout_context, BlockLevel) - } - + self.build_display_list_for_block(box DisplayList::new(), layout_context); if opts::get().validate_display_list_geometry { self.base.validate_display_list_geometry(); } diff --git a/components/layout/construct.rs b/components/layout/construct.rs index 21a24af7efb..ec8533058cd 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -30,6 +30,7 @@ use fragment::{TableColumnFragment, TableColumnFragmentInfo, TableFragment, Tabl use fragment::{TableWrapperFragment, UnscannedTextFragment, UnscannedTextFragmentInfo}; use incremental::{RECONSTRUCT_FLOW, RestyleDamage}; use inline::InlineFlow; +use list_item::{mod, ListItemFlow}; use parallel; use table_wrapper::TableWrapperFlow; use table::TableFlow; @@ -59,7 +60,7 @@ use std::collections::DList; use std::mem; use std::sync::atomic::Relaxed; use style::ComputedValues; -use style::computed_values::{display, position, float}; +use style::computed_values::{display, position, float, list_style_position}; use sync::Arc; use url::Url; @@ -471,38 +472,25 @@ impl<'a> FlowConstructor<'a> { } } - /// Build block flow for current node using information from children nodes. - /// - /// Consume results from children and combine them, handling {ib} splits. - /// Block flows and inline flows thus created will become the children of - /// this block flow. - /// Also, deal with the absolute and fixed descendants bubbled up by - /// children nodes. - fn build_flow_for_block(&mut self, mut flow: FlowRef, node: &ThreadSafeLayoutNode) - -> ConstructionResult { + /// Constructs a block flow, beginning with the given `initial_fragment` if present and then + /// appending the construction results of children to the child list of the block flow. {ib} + /// splits and absolutely-positioned descendants are handled correctly. + fn build_flow_for_block_starting_with_fragment(&mut self, + mut flow: FlowRef, + node: &ThreadSafeLayoutNode, + initial_fragment: Option) + -> ConstructionResult { // Gather up fragments for the inline flows we might need to create. let mut inline_fragment_accumulator = InlineFragmentsAccumulator::new(); let mut consecutive_siblings = vec!(); - let mut first_fragment = true; - // Special case: If this is generated content, then we need to initialize the accumulator - // with the fragment corresponding to that content. - if node.get_pseudo_element_type() != Normal || - node.type_id() == Some(ElementNodeTypeId(HTMLInputElementTypeId)) || - node.type_id() == Some(ElementNodeTypeId(HTMLTextAreaElementTypeId)) { - // A TextArea's text contents are displayed through the input text - // box, so don't construct them. - // TODO Maybe this belongs somewhere else? - if node.type_id() == Some(ElementNodeTypeId(HTMLTextAreaElementTypeId)) { - for kid in node.children() { - kid.set_flow_construction_result(NoConstructionResult) - } + let mut first_fragment = match initial_fragment { + None => true, + Some(initial_fragment) => { + inline_fragment_accumulator.fragments.push_back(initial_fragment); + false } - let fragment_info = UnscannedTextFragment(UnscannedTextFragmentInfo::new(node)); - let fragment = Fragment::new_from_specific_info(node, fragment_info); - inline_fragment_accumulator.fragments.push_back(fragment); - first_fragment = false; - } + }; // List of absolute descendants, in tree order. let mut abs_descendants = Descendants::new(); @@ -552,6 +540,39 @@ impl<'a> FlowConstructor<'a> { FlowConstructionResult(flow, abs_descendants) } + /// Constructs a flow for the given block node and its children. This method creates an + /// initial fragment as appropriate and then dispatches to + /// `build_flow_for_block_starting_with_fragment`. Currently the following kinds of flows get + /// initial content: + /// + /// * Generated content gets the initial content specified by the `content` attribute of the + /// CSS. + /// * `` and `