aboutsummaryrefslogtreecommitdiffstats
path: root/components/layout/construct.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/layout/construct.rs')
-rw-r--r--components/layout/construct.rs135
1 files changed, 107 insertions, 28 deletions
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<Fragment>)
+ -> 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.
+ /// * `<input>` and `<textarea>` elements get their content.
+ ///
+ /// FIXME(pcwalton): It is not clear to me that there isn't a cleaner way to handle
+ /// `<textarea>`.
+ fn build_flow_for_block(&mut self, flow: FlowRef, node: &ThreadSafeLayoutNode)
+ -> ConstructionResult {
+ let initial_fragment = 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.
+ if node.type_id() == Some(ElementNodeTypeId(HTMLTextAreaElementTypeId)) {
+ for kid in node.children() {
+ kid.set_flow_construction_result(NoConstructionResult)
+ }
+ }
+ Some(Fragment::new_from_specific_info(
+ node,
+ UnscannedTextFragment(UnscannedTextFragmentInfo::new(node))))
+ } else {
+ None
+ };
+
+ self.build_flow_for_block_starting_with_fragment(flow, node, initial_fragment)
+ }
+
/// Builds a flow for a node with `display: block`. This yields a `BlockFlow` with possibly
/// other `BlockFlow`s or `InlineFlow`s underneath it, depending on whether {ib} splits needed
/// to happen.
@@ -905,6 +926,59 @@ impl<'a> FlowConstructor<'a> {
self.build_flow_for_block(FlowRef::new(flow), node)
}
+ /// Builds a flow for a node with `display: list-item`. This yields a `ListItemFlow` with
+ /// possibly other `BlockFlow`s or `InlineFlow`s underneath it.
+ fn build_flow_for_list_item(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
+ let marker_fragment = match node.style().get_list().list_style_image {
+ Some(ref url) => {
+ Some(Fragment::new_from_specific_info(
+ node,
+ self.build_fragment_info_for_image(node, Some((*url).clone()))))
+ }
+ None => {
+ match list_item::static_text_for_list_style_type(node.style()
+ .get_list()
+ .list_style_type) {
+ None => None,
+ Some(text) => {
+ let text = text.to_string();
+ let mut unscanned_marker_fragments = DList::new();
+ unscanned_marker_fragments.push_back(Fragment::new_from_specific_info(
+ node,
+ UnscannedTextFragment(UnscannedTextFragmentInfo::from_text(text))));
+ let marker_fragments = TextRunScanner::new().scan_for_runs(
+ self.layout_context.font_context(),
+ unscanned_marker_fragments);
+ debug_assert!(marker_fragments.len() == 1);
+ marker_fragments.fragments.into_iter().next()
+ }
+ }
+ }
+ };
+
+ // If the list marker is outside, it becomes the special "outside fragment" that list item
+ // flows have. If it's inside, it's just a plain old fragment. Note that this means that
+ // we adopt Gecko's behavior rather than WebKit's when the marker causes an {ib} split,
+ // which has caused some malaise (Bugzilla #36854) but CSS 2.1 § 12.5.1 lets me do it, so
+ // there.
+ let flow;
+ let initial_fragment;
+ match node.style().get_list().list_style_position {
+ list_style_position::outside => {
+ flow = box ListItemFlow::from_node_and_marker(self, node, marker_fragment);
+ initial_fragment = None;
+ }
+ list_style_position::inside => {
+ flow = box ListItemFlow::from_node_and_marker(self, node, None);
+ initial_fragment = marker_fragment;
+ }
+ }
+
+ self.build_flow_for_block_starting_with_fragment(FlowRef::new(flow as Box<Flow>),
+ node,
+ initial_fragment)
+ }
+
/// Creates a fragment for a node with `display: table-column`.
fn build_fragments_for_table_column(&mut self, node: &ThreadSafeLayoutNode)
-> ConstructionResult {
@@ -1058,6 +1132,11 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
node.set_flow_construction_result(self.build_flow_for_nonfloated_block(node))
}
+ // List items contribute their own special flows.
+ (display::list_item, _, _) => {
+ node.set_flow_construction_result(self.build_flow_for_list_item(node))
+ }
+
// Inline items that are absolutely-positioned contribute inline fragment construction
// results with a hypothetical fragment.
(display::inline, _, position::absolute) => {