diff options
author | Patrick Walton <pcwalton@mimiga.net> | 2014-06-03 14:03:32 -0700 |
---|---|---|
committer | Patrick Walton <pcwalton@mimiga.net> | 2014-06-03 22:21:11 -0700 |
commit | 735cc2865488936ec94c8b6be01c0d420368abe4 (patch) | |
tree | b67d2865c2ed20bb1ae1f018a406f75fd1a3c83d /src | |
parent | 5a97f5fd79cf55f3c9803511f9e08c0ff5d216ee (diff) | |
download | servo-735cc2865488936ec94c8b6be01c0d420368abe4.tar.gz servo-735cc2865488936ec94c8b6be01c0d420368abe4.zip |
layout: Reference count flows, and forbid unsafe code in many places.
Diffstat (limited to 'src')
20 files changed, 485 insertions, 453 deletions
diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs index 24f60acf7d1..d0187afda8e 100644 --- a/src/components/main/layout/block.rs +++ b/src/components/main/layout/block.rs @@ -12,6 +12,8 @@ //! //! CB: Containing Block of the current flow. +#![deny(unsafe_block)] + use layout::construct::FlowConstructor; use layout::context::LayoutContext; use layout::floats::{ClearBoth, ClearLeft, ClearRight, FloatKind, Floats, PlacementInfo}; @@ -656,17 +658,12 @@ impl BlockFlow { let mut descendant_offset_iter = mut_base(flow).abs_descendants.iter_with_offset(); // Pass in the respective static y offset for each descendant. for (ref mut descendant_link, ref y_offset) in descendant_offset_iter { - match descendant_link.resolve() { - Some(flow) => { - let block = flow.as_block(); - // The stored y_offset is wrt to the flow box. - // Translate it to the CB (which is the padding box). - block.static_y_offset = **y_offset - cb_top_edge_offset; - if !block.traverse_preorder_absolute_flows(traversal) { - return false - } - } - None => fail!("empty Rawlink to a descendant") + let block = descendant_link.as_block(); + // The stored y_offset is wrt to the flow box. + // Translate it to the CB (which is the padding box). + block.static_y_offset = **y_offset - cb_top_edge_offset; + if !block.traverse_preorder_absolute_flows(traversal) { + return false } } @@ -685,14 +682,9 @@ impl BlockFlow { } for descendant_link in mut_base(flow).abs_descendants.iter() { - match descendant_link.resolve() { - Some(abs_flow) => { - let block = abs_flow.as_block(); - if !block.traverse_postorder_absolute_flows(traversal) { - return false - } - } - None => fail!("empty Rawlink to a descendant") + let block = descendant_link.as_block(); + if !block.traverse_postorder_absolute_flows(traversal) { + return false } } @@ -1125,15 +1117,10 @@ impl BlockFlow { // Process absolute descendant links. for abs_descendant_link in self.base.abs_descendants.iter() { - match abs_descendant_link.resolve() { - Some(kid) => { - // TODO(pradeep): Send in our absolute position directly. - accumulator.push_child(&mut display_list, kid); - child_layers.append(mem::replace(&mut flow::mut_base(kid).layers, - DList::new())); - } - None => fail!("empty Rawlink to a descendant") - } + // TODO(pradeep): Send in our absolute position directly. + accumulator.push_child(&mut display_list, abs_descendant_link); + child_layers.append(mem::replace(&mut flow::mut_base(abs_descendant_link).layers, + DList::new())); } accumulator.finish(&mut *self, display_list); @@ -1634,14 +1621,8 @@ impl Flow for BlockFlow { } // Process absolute descendant links. - for absolute_descendant_link in self.base.abs_descendants.iter() { - match absolute_descendant_link.resolve() { - Some(absolute_descendant) => { - flow::mut_base(absolute_descendant).absolute_position_info = - absolute_position_info - } - None => fail!("empty Rawlink to a descendant") - } + for absolute_descendant in self.base.abs_descendants.iter() { + flow::mut_base(absolute_descendant).absolute_position_info = absolute_position_info } } diff --git a/src/components/main/layout/construct.rs b/src/components/main/layout/construct.rs index e84fa3e7350..a6f7e97698e 100644 --- a/src/components/main/layout/construct.rs +++ b/src/components/main/layout/construct.rs @@ -16,9 +16,8 @@ //! apart" a flow tree and have the flows migrate "home" to their respective DOM nodes while we //! perform flow tree construction. The precise mechanism for this will take some experimentation //! to get right. -//! -//! TODO(pcwalton): This scheme should be amenable to parallelization, but, of course, that's not -//! yet implemented. + +#![deny(unsafe_block)] use css::node_style::StyledNode; use layout::block::BlockFlow; @@ -26,11 +25,15 @@ use layout::context::LayoutContext; use layout::floats::FloatKind; use layout::flow::{Flow, ImmutableFlowUtils, MutableOwnedFlowUtils}; use layout::flow::{Descendants, AbsDescendants}; -use layout::flow_list::{Rawlink}; -use layout::fragment::{Fragment, GenericFragment, IframeFragment, IframeFragmentInfo, ImageFragment, ImageFragmentInfo}; -use layout::fragment::{SpecificFragmentInfo, TableFragment, TableCellFragment, TableColumnFragment, TableColumnFragmentInfo}; -use layout::fragment::{TableRowFragment, TableWrapperFragment, UnscannedTextFragment, UnscannedTextFragmentInfo}; +use layout::flow; +use layout::flow_ref::FlowRef; +use layout::fragment::{Fragment, GenericFragment, IframeFragment, IframeFragmentInfo}; +use layout::fragment::{ImageFragment, ImageFragmentInfo, SpecificFragmentInfo, TableFragment}; +use layout::fragment::{TableCellFragment, TableColumnFragment, TableColumnFragmentInfo}; +use layout::fragment::{TableRowFragment, TableWrapperFragment, UnscannedTextFragment}; +use layout::fragment::{UnscannedTextFragmentInfo}; use layout::inline::{FragmentIndex, InlineFragments, InlineFlow}; +use layout::parallel; use layout::table_wrapper::TableWrapperFlow; use layout::table::TableFlow; use layout::table_caption::TableCaptionFlow; @@ -45,7 +48,6 @@ use layout::wrapper::{Before, BeforeBlock, After, AfterBlock, Normal}; use gfx::display_list::OpaqueNode; use gfx::font_context::FontContext; -use script::dom::bindings::js::JS; use script::dom::element::{HTMLIFrameElementTypeId, HTMLImageElementTypeId}; use script::dom::element::{HTMLObjectElementTypeId}; use script::dom::element::{HTMLTableColElementTypeId, HTMLTableDataCellElementTypeId}; @@ -54,14 +56,13 @@ use script::dom::element::{HTMLTableRowElementTypeId, HTMLTableSectionElementTyp use script::dom::node::{CommentNodeTypeId, DoctypeNodeTypeId, DocumentFragmentNodeTypeId}; use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, ProcessingInstructionNodeTypeId}; use script::dom::node::{TextNodeTypeId}; -use script::dom::text::Text; use servo_util::namespace; use servo_util::range::Range; -use servo_util::str::is_whitespace; use servo_util::url::{is_image_data, parse_url}; use std::mem; +use std::sync::atomics::Relaxed; use style::ComputedValues; -use style::computed_values::{display, position, float, white_space}; +use style::computed_values::{display, position, float}; use sync::Arc; use url::Url; @@ -74,23 +75,13 @@ pub enum ConstructionResult { /// This node contributed a flow at the proper position in the tree. /// Nothing more needs to be done for this node. It has bubbled up fixed /// and absolute descendant flows that have a CB above it. - FlowConstructionResult(Box<Flow:Share>, AbsDescendants), + FlowConstructionResult(FlowRef, AbsDescendants), /// This node contributed some object or objects that will be needed to construct a proper flow /// later up the tree, but these objects have not yet found their home. ConstructionItemConstructionResult(ConstructionItem), } -impl ConstructionResult { - fn destroy(&mut self) { - match *self { - NoConstructionResult => {} - FlowConstructionResult(ref mut flow, _) => flow.destroy(), - ConstructionItemConstructionResult(ref mut item) => item.destroy(), - } - } -} - /// Represents the output of flow construction for a DOM node that has not yet resulted in a /// complete flow. Construction items bubble up the tree until they find a `Flow` to be /// attached to. @@ -103,20 +94,6 @@ pub enum ConstructionItem { TableColumnFragmentConstructionItem(Fragment), } -impl ConstructionItem { - fn destroy(&mut self) { - match *self { - InlineFragmentsConstructionItem(ref mut result) => { - for split in result.splits.mut_iter() { - split.destroy() - } - } - WhitespaceConstructionItem(..) => {} - TableColumnFragmentConstructionItem(_) => {} - } - } -} - /// Represents inline fragments and {ib} splits that are bubbling up from an inline. pub struct InlineFragmentsConstructionResult { /// Any {ib} splits that we're bubbling up. @@ -156,13 +133,7 @@ pub struct InlineBlockSplit { pub predecessors: InlineFragments, /// The flow that caused this {ib} split. - pub flow: Box<Flow:Share>, -} - -impl InlineBlockSplit { - fn destroy(&mut self) { - self.flow.destroy() - } + pub flow: FlowRef, } /// Holds inline fragments that we're gathering for children of an inline node. @@ -302,8 +273,8 @@ impl<'a> FlowConstructor<'a> { #[inline(always)] fn flush_inline_fragments_to_flow_or_list(&mut self, fragment_accumulator: InlineFragmentsAccumulator, - flow: &mut Box<Flow:Share>, - flow_list: &mut Vec<Box<Flow:Share>>, + flow: &mut FlowRef, + flow_list: &mut Vec<FlowRef>, whitespace_stripping: WhitespaceStrippingMode, node: &ThreadSafeLayoutNode) { let mut fragments = fragment_accumulator.finish(); @@ -323,11 +294,12 @@ impl<'a> FlowConstructor<'a> { let mut inline_flow = box InlineFlow::from_fragments((*node).clone(), fragments); inline_flow.compute_minimum_ascent_and_descent(self.font_context(), &**node.style()); - let mut inline_flow = inline_flow as Box<Flow:Share>; + let mut inline_flow = inline_flow as Box<Flow>; TextRunScanner::new().scan_for_runs(self.font_context(), inline_flow); + let mut inline_flow = FlowRef::new(inline_flow); inline_flow.finish(self.layout_context); - if flow.need_anonymous_flow(inline_flow) { + if flow.get().need_anonymous_flow(inline_flow.get()) { flow_list.push(inline_flow) } else { flow.add_new_child(inline_flow) @@ -335,9 +307,8 @@ impl<'a> FlowConstructor<'a> { } fn build_block_flow_using_children_construction_result(&mut self, - flow: &mut Box<Flow:Share>, - consecutive_siblings: - &mut Vec<Box<Flow:Share>>, + flow: &mut FlowRef, + consecutive_siblings: &mut Vec<FlowRef>, node: &ThreadSafeLayoutNode, kid: ThreadSafeLayoutNode, inline_fragment_accumulator: @@ -349,16 +320,16 @@ impl<'a> FlowConstructor<'a> { FlowConstructionResult(kid_flow, kid_abs_descendants) => { // If kid_flow is TableCaptionFlow, kid_flow should be added under // TableWrapperFlow. - if flow.is_table() && kid_flow.is_table_caption() { + if flow.get().is_table() && kid_flow.get().is_table_caption() { kid.set_flow_construction_result(FlowConstructionResult( kid_flow, Descendants::new())) - } else if flow.need_anonymous_flow(kid_flow) { + } else if flow.get().need_anonymous_flow(kid_flow.get()) { consecutive_siblings.push(kid_flow) } else { // Strip ignorable whitespace from the start of this flow per CSS 2.1 § // 9.2.1.1. - let whitespace_stripping = if flow.is_table_kind() || *first_fragment { + let whitespace_stripping = if flow.get().is_table_kind() || *first_fragment { *first_fragment = false; StripWhitespaceFromStart } else { @@ -423,7 +394,7 @@ impl<'a> FlowConstructor<'a> { // Push the flow generated by the {ib} split onto our list of // flows. - if flow.need_anonymous_flow(kid_flow) { + if flow.get().need_anonymous_flow(kid_flow.get()) { consecutive_siblings.push(kid_flow) } else { flow.add_new_child(kid_flow) @@ -451,9 +422,7 @@ impl<'a> FlowConstructor<'a> { /// this block flow. /// Also, deal with the absolute and fixed descendants bubbled up by /// children nodes. - fn build_flow_using_children(&mut self, - mut flow: Box<Flow:Share>, - node: &ThreadSafeLayoutNode) + fn build_flow_using_children(&mut self, mut flow: FlowRef, node: &ThreadSafeLayoutNode) -> ConstructionResult { // Gather up fragments for the inline flows we might need to create. let mut inline_fragment_accumulator = InlineFragmentsAccumulator::new(); @@ -489,18 +458,19 @@ impl<'a> FlowConstructor<'a> { // The flow is done. flow.finish(self.layout_context); - let is_positioned = flow.as_block().is_positioned(); - let is_fixed_positioned = flow.as_block().is_fixed(); - let is_absolutely_positioned = flow.as_block().is_absolutely_positioned(); + let is_positioned = flow.get_mut().as_block().is_positioned(); + let is_fixed_positioned = flow.get_mut().as_block().is_fixed(); + let is_absolutely_positioned = flow.get_mut().as_block().is_absolutely_positioned(); if is_positioned { // This is the CB for all the absolute descendants. flow.set_abs_descendants(abs_descendants); + abs_descendants = Descendants::new(); if is_fixed_positioned || is_absolutely_positioned { // This is now the only absolute flow in the subtree which hasn't yet // reached its CB. - abs_descendants.push(Rawlink::some(flow)); + abs_descendants.push(flow.clone()); } } FlowConstructionResult(flow, abs_descendants) @@ -510,16 +480,16 @@ impl<'a> FlowConstructor<'a> { /// other `BlockFlow`s or `InlineFlow`s underneath it, depending on whether {ib} splits needed /// to happen. fn build_flow_for_block(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult { - let flow = box BlockFlow::from_node(self, node) as Box<Flow:Share>; - self.build_flow_using_children(flow, node) + let flow = box BlockFlow::from_node(self, node) as Box<Flow>; + self.build_flow_using_children(FlowRef::new(flow), node) } /// Builds the flow for a node with `float: {left|right}`. This yields a float `BlockFlow` with /// a `BlockFlow` underneath it. fn build_flow_for_floated_block(&mut self, node: &ThreadSafeLayoutNode, float_kind: FloatKind) -> ConstructionResult { - let flow = box BlockFlow::float_from_node(self, node, float_kind) as Box<Flow:Share>; - self.build_flow_using_children(flow, node) + let flow = box BlockFlow::float_from_node(self, node, float_kind) as Box<Flow>; + self.build_flow_using_children(FlowRef::new(flow), node) } /// Concatenates the fragments of kids, adding in our own borders/padding/margins if necessary. @@ -655,14 +625,14 @@ impl<'a> FlowConstructor<'a> { /// TableCaptionFlow is populated underneath TableWrapperFlow fn place_table_caption_under_table_wrapper(&mut self, - table_wrapper_flow: &mut Box<Flow:Share>, + table_wrapper_flow: &mut FlowRef, node: &ThreadSafeLayoutNode) { for kid in node.children() { match kid.swap_out_construction_result() { NoConstructionResult | ConstructionItemConstructionResult(_) => {} FlowConstructionResult(kid_flow, _) => { // Only kid flows with table-caption are matched here. - assert!(kid_flow.is_table_caption()); + assert!(kid_flow.get().is_table_caption()); table_wrapper_flow.add_new_child(kid_flow); } } @@ -672,18 +642,20 @@ impl<'a> FlowConstructor<'a> { /// Generates an anonymous table flow according to CSS 2.1 § 17.2.1, step 2. /// If necessary, generate recursively another anonymous table flow. fn generate_anonymous_missing_child(&mut self, - child_flows: Vec<Box<Flow:Share>>, - flow: &mut Box<Flow:Share>, + child_flows: Vec<FlowRef>, + flow: &mut FlowRef, node: &ThreadSafeLayoutNode) { - let mut anonymous_flow = flow.generate_missing_child_flow(node); + let mut anonymous_flow = flow.get().generate_missing_child_flow(node); let mut consecutive_siblings = vec!(); for kid_flow in child_flows.move_iter() { - if anonymous_flow.need_anonymous_flow(kid_flow) { + if anonymous_flow.get().need_anonymous_flow(kid_flow.get()) { consecutive_siblings.push(kid_flow); continue; } if !consecutive_siblings.is_empty() { - self.generate_anonymous_missing_child(consecutive_siblings, &mut anonymous_flow, node); + self.generate_anonymous_missing_child(consecutive_siblings, + &mut anonymous_flow, + node); consecutive_siblings = vec!(); } anonymous_flow.add_new_child(kid_flow); @@ -700,10 +672,12 @@ impl<'a> FlowConstructor<'a> { /// other `TableCaptionFlow`s or `TableFlow`s underneath it. fn build_flow_for_table_wrapper(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult { let fragment = Fragment::new_from_specific_info(node, TableWrapperFragment); - let mut wrapper_flow = box TableWrapperFlow::from_node_and_fragment(node, fragment) as Box<Flow:Share>; + let wrapper_flow = box TableWrapperFlow::from_node_and_fragment(node, fragment); + let mut wrapper_flow = FlowRef::new(wrapper_flow as Box<Flow>); let table_fragment = Fragment::new_from_specific_info(node, TableFragment); - let table_flow = box TableFlow::from_node_and_fragment(node, table_fragment) as Box<Flow:Share>; + let table_flow = box TableFlow::from_node_and_fragment(node, table_fragment); + let table_flow = FlowRef::new(table_flow as Box<Flow>); // We first populate the TableFlow with other flows than TableCaptionFlow. // We then populate the TableWrapperFlow with TableCaptionFlow, and attach @@ -726,21 +700,24 @@ impl<'a> FlowConstructor<'a> { // The flow is done. wrapper_flow.finish(self.layout_context); - let is_positioned = wrapper_flow.as_block().is_positioned(); - let is_fixed_positioned = wrapper_flow.as_block().is_fixed(); - let is_absolutely_positioned = wrapper_flow.as_block().is_absolutely_positioned(); + let is_positioned = wrapper_flow.get_mut().as_block().is_positioned(); + let is_fixed_positioned = wrapper_flow.get_mut().as_block().is_fixed(); + let is_absolutely_positioned = wrapper_flow.get_mut() + .as_block() + .is_absolutely_positioned(); if is_positioned { // This is the CB for all the absolute descendants. wrapper_flow.set_abs_descendants(abs_descendants); + abs_descendants = Descendants::new(); if is_fixed_positioned { // Send itself along with the other fixed descendants. - fixed_descendants.push(Rawlink::some(wrapper_flow)); + fixed_descendants.push(wrapper_flow.clone()); } else if is_absolutely_positioned { // This is now the only absolute flow in the subtree which hasn't yet // reached its CB. - abs_descendants.push(Rawlink::some(wrapper_flow)); + abs_descendants.push(wrapper_flow.clone()); } } FlowConstructionResult(wrapper_flow, abs_descendants) @@ -749,32 +726,33 @@ impl<'a> FlowConstructor<'a> { /// Builds a flow for a node with `display: table-caption`. This yields a `TableCaptionFlow` /// with possibly other `BlockFlow`s or `InlineFlow`s underneath it. fn build_flow_for_table_caption(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult { - let flow = box TableCaptionFlow::from_node(self, node) as Box<Flow:Share>; - self.build_flow_using_children(flow, node) + let flow = box TableCaptionFlow::from_node(self, node) as Box<Flow>; + self.build_flow_using_children(FlowRef::new(flow), node) } /// Builds a flow for a node with `display: table-row-group`. This yields a `TableRowGroupFlow` /// with possibly other `TableRowFlow`s underneath it. fn build_flow_for_table_rowgroup(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult { let fragment = Fragment::new_from_specific_info(node, TableRowFragment); - let flow = box TableRowGroupFlow::from_node_and_fragment(node, fragment) as Box<Flow:Share>; - self.build_flow_using_children(flow, node) + let flow = box TableRowGroupFlow::from_node_and_fragment(node, fragment); + let flow = flow as Box<Flow>; + self.build_flow_using_children(FlowRef::new(flow), node) } /// Builds a flow for a node with `display: table-row`. This yields a `TableRowFlow` with /// possibly other `TableCellFlow`s underneath it. fn build_flow_for_table_row(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult { let fragment = Fragment::new_from_specific_info(node, TableRowFragment); - let flow = box TableRowFlow::from_node_and_fragment(node, fragment) as Box<Flow:Share>; - self.build_flow_using_children(flow, node) + let flow = box TableRowFlow::from_node_and_fragment(node, fragment) as Box<Flow>; + self.build_flow_using_children(FlowRef::new(flow), node) } /// Builds a flow for a node with `display: table-cell`. This yields a `TableCellFlow` with /// possibly other `BlockFlow`s or `InlineFlow`s underneath it. fn build_flow_for_table_cell(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult { let fragment = Fragment::new_from_specific_info(node, TableCellFragment); - let flow = box TableCellFlow::from_node_and_fragment(node, fragment) as Box<Flow:Share>; - self.build_flow_using_children(flow, node) + let flow = box TableCellFlow::from_node_and_fragment(node, fragment) as Box<Flow>; + self.build_flow_using_children(FlowRef::new(flow), node) } /// Creates a fragment for a node with `display: table-column`. @@ -812,7 +790,8 @@ impl<'a> FlowConstructor<'a> { let specific = TableColumnFragment(TableColumnFragmentInfo::new(node)); col_fragments.push(Fragment::new_from_specific_info(node, specific)); } - let mut flow = box TableColGroupFlow::from_node_and_fragments(node, fragment, col_fragments) as Box<Flow:Share>; + let flow = box TableColGroupFlow::from_node_and_fragments(node, fragment, col_fragments); + let mut flow = FlowRef::new(flow as Box<Flow>); flow.finish(self.layout_context); FlowConstructionResult(flow, Descendants::new()) @@ -860,8 +839,7 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> { // results of children. (display::none, _, _) => { for child in node.children() { - let mut old_result = child.swap_out_construction_result(); - old_result.destroy() + drop(child.swap_out_construction_result()) } } @@ -950,9 +928,6 @@ trait NodeUtils { /// Returns true if this node doesn't render its kids and false otherwise. fn is_replaced_content(&self) -> bool; - /// Returns true if this node is ignorable whitespace. - fn is_ignorable_whitespace(&self) -> bool; - /// Sets the construction result of a flow. fn set_flow_construction_result(&self, result: ConstructionResult); @@ -977,31 +952,6 @@ impl<'ln> NodeUtils for ThreadSafeLayoutNode<'ln> { } } - fn is_ignorable_whitespace(&self) -> bool { - match self.type_id() { - Some(TextNodeTypeId) => { - unsafe { - let text: JS<Text> = self.get_jsmanaged().transmute_copy(); - if !is_whitespace((*text.unsafe_get()).characterdata.data) { - return false - } - - // NB: See the rules for `white-space` here: - // - // http://www.w3.org/TR/CSS21/text.html#propdef-white-space - // - // If you implement other values for this property, you will almost certainly - // want to update this check. - match self.style().get_inheritedtext().white_space { - white_space::normal => true, - _ => false, - } - } - } - _ => false - } - } - #[inline(always)] fn set_flow_construction_result(&self, result: ConstructionResult) { let mut layout_data_ref = self.mutate_layout_data(); @@ -1078,3 +1028,49 @@ impl<'ln> ObjectElement for ThreadSafeLayoutNode<'ln> { } } } + +pub trait FlowConstructionUtils { + /// Adds a new flow as a child of this flow. Removes the flow from the given leaf set if + /// it's present. + fn add_new_child(&mut self, new_child: FlowRef); + + /// Finishes a flow. Once a flow is finished, no more child flows or boxes may be added to it. + /// This will normally run the bubble-widths (minimum and preferred -- i.e. intrinsic -- width) + /// calculation, unless the global `bubble_widths_separately` flag is on. + /// + /// All flows must be finished at some point, or they will not have their intrinsic widths + /// properly computed. (This is not, however, a memory safety problem.) + fn finish(&mut self, context: &mut LayoutContext); +} + +impl FlowConstructionUtils for FlowRef { + /// Adds a new flow as a child of this flow. Fails if this flow is marked as a leaf. + /// + /// This must not be public because only the layout constructor can do this. + fn add_new_child(&mut self, mut new_child: FlowRef) { + { + let kid_base = flow::mut_base(new_child.get_mut()); + kid_base.parallel.parent = parallel::mut_owned_flow_to_unsafe_flow(self); + } + + let base = flow::mut_base(self.get_mut()); + base.children.push_back(new_child); + let _ = base.parallel.children_count.fetch_add(1, Relaxed); + let _ = base.parallel.children_and_absolute_descendant_count.fetch_add(1, Relaxed); + } + + /// Finishes a flow. Once a flow is finished, no more child flows or fragments may be added to + /// it. This will normally run the bubble-widths (minimum and preferred -- i.e. intrinsic -- + /// width) calculation, unless the global `bubble_widths_separately` flag is on. + /// + /// All flows must be finished at some point, or they will not have their intrinsic widths + /// properly computed. (This is not, however, a memory safety problem.) + /// + /// This must not be public because only the layout constructor can do this. + fn finish(&mut self, context: &mut LayoutContext) { + if !context.opts.bubble_widths_separately { + self.get_mut().bubble_widths(context) + } + } +} + diff --git a/src/components/main/layout/flow.rs b/src/components/main/layout/flow.rs index d50f19272d4..4cf331473bb 100644 --- a/src/components/main/layout/flow.rs +++ b/src/components/main/layout/flow.rs @@ -29,13 +29,13 @@ use css::node_style::StyledNode; use layout::block::BlockFlow; use layout::context::LayoutContext; use layout::floats::Floats; -use layout::flow_list::{FlowList, Link, Rawlink, FlowListIterator, MutFlowListIterator}; +use layout::flow_list::{FlowList, Link, FlowListIterator, MutFlowListIterator}; +use layout::flow_ref::FlowRef; use layout::fragment::{Fragment, TableRowFragment, TableCellFragment}; use layout::incremental::RestyleDamage; use layout::inline::InlineFlow; use layout::model::{CollapsibleMargins, IntrinsicWidths, MarginCollapseInfo}; use layout::parallel::FlowParallelInfo; -use layout::parallel; use layout::table_wrapper::TableWrapperFlow; use layout::table::TableFlow; use layout::table_colgroup::TableColGroupFlow; @@ -45,7 +45,6 @@ use layout::table_caption::TableCaptionFlow; use layout::table_cell::TableCellFlow; use layout::wrapper::ThreadSafeLayoutNode; -use collections::Deque; use collections::dlist::DList; use geom::point::Point2D; use geom::rect::Rect; @@ -58,7 +57,7 @@ use std::cast; use std::fmt; use std::iter::Zip; use std::num::Zero; -use std::sync::atomics::Relaxed; +use std::sync::atomics::{AtomicUint, Relaxed, SeqCst}; use std::slice::MutItems; use style::computed_values::{clear, position, text_align}; @@ -66,7 +65,7 @@ use style::computed_values::{clear, position, text_align}; /// /// Note that virtual methods have a cost; we should not overuse them in Servo. Consider adding /// methods to `ImmutableFlowUtils` or `MutableFlowUtils` before adding more methods here. -pub trait Flow: fmt::Show + ToStr { +pub trait Flow: fmt::Show + ToStr + Share { // RTTI // // TODO(pcwalton): Use Rust's RTTI, once that works. @@ -293,11 +292,6 @@ pub fn mut_base<'a>(this: &'a mut Flow) -> &'a mut BaseFlow { } } -/// Returns the last child of this flow. -pub fn last_child<'a>(flow: &'a mut Flow) -> Option<&'a mut Flow> { - mut_base(flow).children.back_mut() -} - /// Iterates over the children of this flow. pub fn child_iter<'a>(flow: &'a mut Flow) -> MutFlowListIterator<'a> { mut_base(flow).children.mut_iter() @@ -337,7 +331,7 @@ pub trait ImmutableFlowUtils { fn need_anonymous_flow(self, child: &Flow) -> bool; /// Generates missing child flow of this flow. - fn generate_missing_child_flow(self, node: &ThreadSafeLayoutNode) -> Box<Flow:Share>; + fn generate_missing_child_flow(self, node: &ThreadSafeLayoutNode) -> FlowRef; /// Returns true if this flow has no children. fn is_leaf(self) -> bool; @@ -372,42 +366,18 @@ pub trait MutableFlowUtils { // Mutators - /// Invokes a closure with the first child of this flow. - fn with_first_child<R>(self, f: |Option<&mut Flow>| -> R) -> R; - - /// Invokes a closure with the last child of this flow. - fn with_last_child<R>(self, f: |Option<&mut Flow>| -> R) -> R; - /// Computes the overflow region for this flow. fn store_overflow(self, _: &mut LayoutContext); /// Builds the display lists for this flow. fn build_display_list(self, layout_context: &LayoutContext); - - /// Destroys the flow. - fn destroy(self); } pub trait MutableOwnedFlowUtils { - /// Adds a new flow as a child of this flow. Removes the flow from the given leaf set if - /// it's present. - fn add_new_child(&mut self, new_child: Box<Flow:Share>); - - /// Finishes a flow. Once a flow is finished, no more child flows or boxes may be added to it. - /// This will normally run the bubble-widths (minimum and preferred -- i.e. intrinsic -- width) - /// calculation, unless the global `bubble_widths_separately` flag is on. - /// - /// All flows must be finished at some point, or they will not have their intrinsic widths - /// properly computed. (This is not, however, a memory safety problem.) - fn finish(&mut self, context: &mut LayoutContext); - /// Set absolute descendants for this flow. /// /// Set this flow as the Containing Block for all the absolute descendants. fn set_abs_descendants(&mut self, abs_descendants: AbsDescendants); - - /// Destroys the flow. - fn destroy(&mut self); } #[deriving(Eq, Show)] @@ -550,10 +520,11 @@ impl FlowFlags { /// The Descendants of a flow. /// /// Also, details about their position wrt this flow. -/// FIXME: This should use @pcwalton's reference counting scheme (Coming Soon). pub struct Descendants { - /// Links to every Descendant. - pub descendant_links: Vec<Rawlink>, + /// Links to every descendant. This must be private because it is unsafe to leak `FlowRef`s to + /// layout. + descendant_links: Vec<FlowRef>, + /// Static y offsets of all descendants from the start of this flow box. pub static_y_offsets: Vec<Au>, } @@ -570,7 +541,7 @@ impl Descendants { self.descendant_links.len() } - pub fn push(&mut self, given_descendant: Rawlink) { + pub fn push(&mut self, given_descendant: FlowRef) { self.descendant_links.push(given_descendant); } @@ -585,21 +556,41 @@ impl Descendants { /// Return an iterator over the descendant flows. pub fn iter<'a>(&'a mut self) -> DescendantIter<'a> { - self.descendant_links.mut_slice_from(0).mut_iter() + DescendantIter { + iter: self.descendant_links.mut_slice_from(0).mut_iter(), + } } /// Return an iterator over (descendant, static y offset). pub fn iter_with_offset<'a>(&'a mut self) -> DescendantOffsetIter<'a> { - self.descendant_links.mut_slice_from(0).mut_iter().zip( - self.static_y_offsets.mut_slice_from(0).mut_iter()) + let descendant_iter = DescendantIter { + iter: self.descendant_links.mut_slice_from(0).mut_iter(), + }; + descendant_iter.zip(self.static_y_offsets.mut_slice_from(0).mut_iter()) } } pub type AbsDescendants = Descendants; -pub type DescendantIter<'a> = MutItems<'a, Rawlink>; +pub struct DescendantIter<'a> { + iter: MutItems<'a, FlowRef>, +} + +impl<'a> Iterator<&'a mut Flow> for DescendantIter<'a> { + fn next(&mut self) -> Option<&'a mut Flow> { + match self.iter.next() { + None => None, + Some(ref mut flow) => { + unsafe { + let result: &'a mut Flow = cast::transmute(flow.get_mut()); + Some(result) + } + } + } + } +} -pub type DescendantOffsetIter<'a> = Zip<MutItems<'a, Rawlink>, MutItems<'a, Au>>; +pub type DescendantOffsetIter<'a> = Zip<DescendantIter<'a>, MutItems<'a, Au>>; /// Information needed to compute absolute (i.e. viewport-relative) flow positions (not to be /// confused with absolutely-positioned flows). @@ -628,12 +619,17 @@ impl AbsolutePositionInfo { /// Data common to all flows. pub struct BaseFlow { + /// NB: Must be the first element. + /// + /// The necessity of this will disappear once we have dynamically-sized types. + ref_count: AtomicUint, + pub restyle_damage: RestyleDamage, /// The children of this flow. pub children: FlowList, pub next_sibling: Link, - pub prev_sibling: Rawlink, + pub prev_sibling: Link, /* layout computations */ // TODO: min/pref and position are used during disjoint phases of @@ -694,12 +690,6 @@ pub struct BaseFlow { /// Any layers that we're bubbling up, in a linked list. pub layers: DList<RenderLayer>, - /// Whether this flow has been destroyed. - /// - /// TODO(pcwalton): Pack this into the flags? Need to be careful because manipulation of this - /// flag can have memory safety implications. - destroyed: bool, - /// Various flags for flows, tightly packed to save space. pub flags: FlowFlags, } @@ -707,8 +697,8 @@ pub struct BaseFlow { #[unsafe_destructor] impl Drop for BaseFlow { fn drop(&mut self) { - if !self.destroyed { - fail!("Flow destroyed by going out of scope—this is unsafe! Use `destroy()` instead!") + if self.ref_count.load(SeqCst) != 0 { + fail!("Flow destroyed before its ref count hit zero—this is unsafe!") } } } @@ -717,11 +707,13 @@ impl BaseFlow { #[inline] pub fn new(node: ThreadSafeLayoutNode) -> BaseFlow { BaseFlow { + ref_count: AtomicUint::new(1), + restyle_damage: node.restyle_damage(), children: FlowList::new(), next_sibling: None, - prev_sibling: Rawlink::none(), + prev_sibling: None, intrinsic_widths: IntrinsicWidths::new(), position: Rect::zero(), @@ -740,8 +732,6 @@ impl BaseFlow { layers: DList::new(), absolute_position_info: AbsolutePositionInfo::new(), - destroyed: false, - flags: FlowFlags::new(), } } @@ -749,6 +739,10 @@ impl BaseFlow { pub fn child_iter<'a>(&'a mut self) -> MutFlowListIterator<'a> { self.children.mut_iter() } + + pub unsafe fn ref_count<'a>(&'a self) -> &'a AtomicUint { + &self.ref_count + } } impl<'a> ImmutableFlowUtils for &'a Flow { @@ -841,20 +835,21 @@ impl<'a> ImmutableFlowUtils for &'a Flow { } /// Generates missing child flow of this flow. - fn generate_missing_child_flow(self, node: &ThreadSafeLayoutNode) -> Box<Flow:Share> { - match self.class() { + fn generate_missing_child_flow(self, node: &ThreadSafeLayoutNode) -> FlowRef { + let flow = match self.class() { TableFlowClass | TableRowGroupFlowClass => { let fragment = Fragment::new_anonymous_table_fragment(node, TableRowFragment); - box TableRowFlow::from_node_and_fragment(node, fragment) as Box<Flow:Share> + box TableRowFlow::from_node_and_fragment(node, fragment) as Box<Flow> }, TableRowFlowClass => { let fragment = Fragment::new_anonymous_table_fragment(node, TableCellFragment); - box TableCellFlow::from_node_and_fragment(node, fragment) as Box<Flow:Share> + box TableCellFlow::from_node_and_fragment(node, fragment) as Box<Flow> }, _ => { fail!("no need to generate a missing child") } - } + }; + FlowRef::new(flow) } /// Returns true if this flow has no children. @@ -957,16 +952,6 @@ impl<'a> MutableFlowUtils for &'a mut Flow { traversal.process(self) } - /// Invokes a closure with the first child of this flow. - fn with_first_child<R>(self, f: |Option<&mut Flow>| -> R) -> R { - f(mut_base(self).children.front_mut()) - } - - /// Invokes a closure with the last child of this flow. - fn with_last_child<R>(self, f: |Option<&mut Flow>| -> R) -> R { - f(mut_base(self).children.back_mut()) - } - /// Calculate and set overflow for current flow. /// /// CSS Section 11.1 @@ -994,14 +979,9 @@ impl<'a> MutableFlowUtils for &'a mut Flow { // FIXME(#2004, pcwalton): This is wrong for `position: fixed`. for descendant_link in mut_base(self).abs_descendants.iter() { - match descendant_link.resolve() { - Some(flow) => { - let mut kid_overflow = base(flow).overflow; - kid_overflow = kid_overflow.translate(&my_position.origin); - overflow = overflow.union(&kid_overflow) - } - None => fail!("empty Rawlink to a descendant") - } + let mut kid_overflow = base(descendant_link).overflow; + kid_overflow = kid_overflow.translate(&my_position.origin); + overflow = overflow.union(&kid_overflow) } } mut_base(self).overflow = overflow; @@ -1040,52 +1020,20 @@ impl<'a> MutableFlowUtils for &'a mut Flow { } } } - - /// Destroys the flow. - fn destroy(self) { - for kid in child_iter(self) { - kid.destroy() - } - - mut_base(self).destroyed = true - } } -impl MutableOwnedFlowUtils for Box<Flow:Share> { - /// Adds a new flow as a child of this flow. Fails if this flow is marked as a leaf. - fn add_new_child(&mut self, mut new_child: Box<Flow:Share>) { - { - let kid_base = mut_base(new_child); - kid_base.parallel.parent = parallel::mut_owned_flow_to_unsafe_flow(self); - } - - let base = mut_base(*self); - base.children.push_back(new_child); - let _ = base.parallel.children_count.fetch_add(1, Relaxed); - let _ = base.parallel.children_and_absolute_descendant_count.fetch_add(1, Relaxed); - } - - /// Finishes a flow. Once a flow is finished, no more child flows or fragments may be added to it. - /// This will normally run the bubble-widths (minimum and preferred -- i.e. intrinsic -- width) - /// calculation, unless the global `bubble_widths_separately` flag is on. - /// - /// All flows must be finished at some point, or they will not have their intrinsic widths - /// properly computed. (This is not, however, a memory safety problem.) - fn finish(&mut self, context: &mut LayoutContext) { - if !context.opts.bubble_widths_separately { - self.bubble_widths(context) - } - } - +impl MutableOwnedFlowUtils for FlowRef { /// Set absolute descendants for this flow. /// /// Set yourself as the Containing Block for all the absolute descendants. /// - /// Assumption: This is called in a bottom-up traversal, so that nothing - /// else is accessing the descendant flows. + /// This is called during flow construction, so nothing else can be accessing the descendant + /// flows. This is enforced by the fact that we have a mutable `FlowRef`, which only flow + /// construction is allowed to possess. fn set_abs_descendants(&mut self, abs_descendants: AbsDescendants) { - let self_link = Rawlink::some(*self); - let block = self.as_block(); + let this = self.clone(); + + let block = self.get_mut().as_block(); block.base.abs_descendants = abs_descendants; block.base .parallel @@ -1093,21 +1041,10 @@ impl MutableOwnedFlowUtils for Box<Flow:Share> { .fetch_add(block.base.abs_descendants.len() as int, Relaxed); for descendant_link in block.base.abs_descendants.iter() { - match descendant_link.resolve() { - Some(flow) => { - let base = mut_base(flow); - base.absolute_cb.set(self_link.clone()); - } - None => fail!("empty Rawlink to a descendant") - } + let base = mut_base(descendant_link); + base.absolute_cb.set(this.clone()); } } - - /// Destroys the flow. - fn destroy(&mut self) { - let self_borrowed: &mut Flow = *self; - self_borrowed.destroy(); - } } /// A link to a flow's containing block. @@ -1116,29 +1053,34 @@ impl MutableOwnedFlowUtils for Box<Flow:Share> { /// tree. A pointer up the tree is unsafe during layout because it can be used to access a node /// with an immutable reference while that same node is being laid out, causing possible iterator /// invalidation and use-after-free. +/// +/// FIXME(pcwalton): I think this would be better with a borrow flag instead of `unsafe`. pub struct ContainingBlockLink { - /// TODO(pcwalton): Reference count. - link: Rawlink, + /// The pointer up to the containing block. + link: Option<FlowRef>, } impl ContainingBlockLink { fn new() -> ContainingBlockLink { ContainingBlockLink { - link: Rawlink::none(), + link: None, } } - fn set(&mut self, link: Rawlink) { - self.link = link + fn set(&mut self, link: FlowRef) { + self.link = Some(link) } - pub unsafe fn resolve(&mut self) -> Option<&mut Flow> { - self.link.resolve() + pub unsafe fn get<'a>(&'a mut self) -> &'a mut Option<FlowRef> { + &mut self.link } #[inline] pub fn generated_containing_block_rect(&mut self) -> Rect<Au> { - self.link.resolve().unwrap().generated_containing_block_rect() + match self.link { + None => fail!("haven't done it"), + Some(ref mut link) => link.get_mut().generated_containing_block_rect(), + } } } diff --git a/src/components/main/layout/flow_list.rs b/src/components/main/layout/flow_list.rs index d812db5b740..e0646433e71 100644 --- a/src/components/main/layout/flow_list.rs +++ b/src/components/main/layout/flow_list.rs @@ -5,13 +5,14 @@ //! A variant of `DList` specialized to store `Flow`s without an extra //! indirection. +use layout::flow::{Flow, base, mut_base}; +use layout::flow_ref::FlowRef; + use std::cast; use std::mem; use std::ptr; -use layout::flow::{Flow, base, mut_base}; - -pub type Link = Option<Box<Flow:Share>>; +pub type Link = Option<FlowRef>; #[deriving(Clone)] pub struct Rawlink { @@ -26,7 +27,7 @@ pub struct Rawlink { pub struct FlowList { length: uint, list_head: Link, - list_tail: Rawlink, + list_tail: Link, } /// Double-ended FlowList iterator @@ -54,42 +55,30 @@ impl Rawlink { } /// Like Option::Some for Rawlink - pub fn some(n: &mut Flow) -> Rawlink { + pub fn some(n: &Flow) -> Rawlink { unsafe { cast::transmute(n) } } - /// Convert the `Rawlink` into an Option value - fn resolve_immut(&self) -> Option<&Flow> { - if self.obj.is_null() { - None - } else { - let me: &Flow = unsafe { cast::transmute_copy(self) }; - Some(me) + fn from_optional_flow_ref(flow_ref: &Option<FlowRef>) -> Rawlink { + match *flow_ref { + None => Rawlink::none(), + Some(ref flow_ref) => Rawlink::some(flow_ref.get()), } } - pub fn resolve(&mut self) -> Option<&mut Flow> { + pub unsafe fn resolve_mut(&self) -> Option<&mut Flow> { if self.obj.is_null() { None } else { - let me: &mut Flow = unsafe { cast::transmute_copy(self) }; + let me: &mut Flow = cast::transmute_copy(self); Some(me) } } - - fn is_none(&self) -> bool { - self.obj.is_null() - } - - unsafe fn get<'a>(&'a mut self) -> &'a mut Flow { - assert!(self.obj.is_not_null()); - cast::transmute_copy(self) - } } /// Set the .prev field on `next`, then return `Some(next)` -fn link_with_prev(mut next: Box<Flow:Share>, prev: Rawlink) -> Link { - mut_base(next).prev_sibling = prev; +unsafe fn link_with_prev(mut next: FlowRef, prev: Option<FlowRef>) -> Link { + mut_base(next.get_mut()).prev_sibling = prev; Some(next) } @@ -112,33 +101,34 @@ impl FlowList { /// Provide a reference to the front element, or None if the list is empty #[inline] pub fn front<'a>(&'a self) -> Option<&'a Flow> { - self.list_head.as_ref().map(|head| { let x: &Flow = *head; x }) + self.list_head.as_ref().map(|head| head.get()) } /// Provide a mutable reference to the front element, or None if the list is empty #[inline] - pub fn front_mut<'a>(&'a mut self) -> Option<&'a mut Flow> { - self.list_head.as_mut().map(|head| { let x: &mut Flow = *head; x }) + pub unsafe fn front_mut<'a>(&'a mut self) -> Option<&'a mut Flow> { + self.list_head.as_mut().map(|head| head.get_mut()) } /// Provide a reference to the back element, or None if the list is empty #[inline] pub fn back<'a>(&'a self) -> Option<&'a Flow> { - let tmp = self.list_tail.resolve_immut(); - tmp.as_ref().map(|tail| { let x: &Flow = *tail; x }) + match self.list_tail { + None => None, + Some(ref list_tail) => Some(list_tail.get()) + } } /// Provide a mutable reference to the back element, or None if the list is empty #[inline] - pub fn back_mut<'a>(&'a mut self) -> Option<&'a mut Flow> { + pub unsafe fn back_mut<'a>(&'a mut self) -> Option<&'a mut Flow> { // Can't use map() due to error: // lifetime of `tail` is too short to guarantee its contents can be safely reborrowed - let tmp = self.list_tail.resolve(); - match tmp { + match self.list_tail { None => None, - Some(tail) => { - let x: &mut Flow = tail; - Some(x) + Some(ref mut tail) => { + let x: &mut Flow = tail.get_mut(); + Some(cast::transmute_copy(&x)) } } } @@ -146,31 +136,35 @@ impl FlowList { /// Add an element first in the list /// /// O(1) - pub fn push_front(&mut self, mut new_head: Box<Flow:Share>) { - match self.list_head { - None => { - self.list_tail = Rawlink::some(new_head); - self.list_head = link_with_prev(new_head, Rawlink::none()); - } - Some(ref mut head) => { - mut_base(new_head).prev_sibling = Rawlink::none(); - mut_base(*head).prev_sibling = Rawlink::some(new_head); - mem::swap(head, &mut new_head); - mut_base(*head).next_sibling = Some(new_head); + pub fn push_front(&mut self, mut new_head: FlowRef) { + unsafe { + match self.list_head { + None => { + self.list_tail = Some(new_head.clone()); + self.list_head = link_with_prev(new_head, None); + } + Some(ref mut head) => { + mut_base(new_head.get_mut()).prev_sibling = None; + mut_base(head.get_mut()).prev_sibling = Some(new_head.clone()); + mem::swap(head, &mut new_head); + mut_base(head.get_mut()).next_sibling = Some(new_head); + } } + self.length += 1; } - self.length += 1; } /// Remove the first element and return it, or None if the list is empty /// /// O(1) - pub fn pop_front(&mut self) -> Option<Box<Flow:Share>> { + pub fn pop_front(&mut self) -> Option<FlowRef> { self.list_head.take().map(|mut front_node| { self.length -= 1; - match mut_base(front_node).next_sibling.take() { - Some(node) => self.list_head = link_with_prev(node, Rawlink::none()), - None => self.list_tail = Rawlink::none() + unsafe { + match mut_base(front_node.get_mut()).next_sibling.take() { + Some(node) => self.list_head = link_with_prev(node, None), + None => self.list_tail = None, + } } front_node }) @@ -179,34 +173,19 @@ impl FlowList { /// Add an element last in the list /// /// O(1) - pub fn push_back(&mut self, mut new_tail: Box<Flow:Share>) { + pub fn push_back(&mut self, new_tail: FlowRef) { if self.list_tail.is_none() { return self.push_front(new_tail); - } else { - let mut old_tail = self.list_tail; - self.list_tail = Rawlink::some(new_tail); - let tail = unsafe { old_tail.get() }; - mut_base(tail).next_sibling = link_with_prev(new_tail, Rawlink::some(tail)); } - self.length += 1; - } - - /// Remove the last element and return it, or None if the list is empty - /// - /// O(1) - pub fn pop_back(&mut self) -> Option<Box<Flow:Share>> { - if self.list_tail.is_none() { - None - } else { - self.length -= 1; - self.list_tail = base(unsafe { self.list_tail.get() }).prev_sibling; - if self.list_tail.is_none() { - self.list_head.take() - } else { - mut_base(unsafe { self.list_tail.get() }).next_sibling.take() - } + let old_tail = self.list_tail.clone(); + self.list_tail = Some(new_tail.clone()); + let mut tail = (*old_tail.as_ref().unwrap()).clone(); + let tail_clone = Some(tail.clone()); + unsafe { + mut_base(tail.get_mut()).next_sibling = link_with_prev(new_tail, tail_clone); } + self.length += 1; } /// Create an empty list @@ -214,7 +193,7 @@ impl FlowList { pub fn new() -> FlowList { FlowList { list_head: None, - list_tail: Rawlink::none(), + list_tail: None, length: 0, } } @@ -225,7 +204,7 @@ impl FlowList { FlowListIterator { nelem: self.len(), head: &self.list_head, - tail: self.list_tail + tail: Rawlink::from_optional_flow_ref(&self.list_tail) } } @@ -233,13 +212,13 @@ impl FlowList { #[inline] pub fn mut_iter<'a>(&'a mut self) -> MutFlowListIterator<'a> { let head_raw = match self.list_head { - Some(ref mut h) => Rawlink::some(*h), + Some(ref mut h) => Rawlink::some(h.get()), None => Rawlink::none(), }; MutFlowListIterator { nelem: self.len(), head: head_raw, - tail: self.list_tail, + tail: Rawlink::from_optional_flow_ref(&self.list_tail), list: self } } @@ -251,20 +230,20 @@ impl Drop for FlowList { // Dissolve the list in backwards direction // Just dropping the list_head can lead to stack exhaustion // when length is >> 1_000_000 - let mut tail = self.list_tail; + let mut tail = mem::replace(&mut self.list_tail, None); loop { - match tail.resolve() { + let new_tail = match tail { None => break, - Some(prev) => { - let prev_base = mut_base(prev); + Some(ref mut prev) => { + let prev_base = mut_base(prev.get_mut()); prev_base.next_sibling.take(); - tail = prev_base.prev_sibling; + prev_base.prev_sibling.clone() } - } + }; + tail = new_tail } self.length = 0; self.list_head = None; - self.list_tail = Rawlink::none(); } } @@ -275,10 +254,10 @@ impl<'a> Iterator<&'a Flow> for FlowListIterator<'a> { return None; } self.head.as_ref().map(|head| { - let head_base = base(*head); + let head_base = base(head.get()); self.nelem -= 1; self.head = &head_base.next_sibling; - let ret: &Flow = *head; + let ret: &Flow = head.get(); ret }) } @@ -295,17 +274,19 @@ impl<'a> Iterator<&'a mut Flow> for MutFlowListIterator<'a> { if self.nelem == 0 { return None; } - self.head.resolve().map(|next| { - self.nelem -= 1; - self.head = match mut_base(next).next_sibling { - Some(ref mut node) => { - let x: &mut Flow = *node; - Rawlink::some(x) - } - None => Rawlink::none(), - }; - next - }) + unsafe { + self.head.resolve_mut().map(|next| { + self.nelem -= 1; + self.head = match mut_base(next).next_sibling { + Some(ref mut node) => { + let x: &mut Flow = node.get_mut(); + Rawlink::some(x) + } + None => Rawlink::none(), + }; + next + }) + } } #[inline] diff --git a/src/components/main/layout/flow_ref.rs b/src/components/main/layout/flow_ref.rs new file mode 100644 index 00000000000..8b10d5a41f6 --- /dev/null +++ b/src/components/main/layout/flow_ref.rs @@ -0,0 +1,79 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +/// Reference-counted pointers to flows. +/// +/// Eventually, with dynamically sized types in Rust, much of this code will be superfluous. + +use layout::flow::Flow; +use layout::flow; + +use std::cast; +use std::mem; +use std::ptr; +use std::sync::atomics::SeqCst; + +#[unsafe_no_drop_flag] +pub struct FlowRef { + vtable: *u8, + ptr: *u8, +} + +impl FlowRef { + pub fn new(mut flow: Box<Flow>) -> FlowRef { + unsafe { + let result = { + let flow_ref: &mut Flow = flow; + cast::transmute(flow_ref) + }; + cast::forget(flow); + result + } + } + + pub fn get<'a>(&'a self) -> &'a Flow { + unsafe { + cast::transmute_copy(self) + } + } + + pub fn get_mut<'a>(&'a mut self) -> &'a mut Flow { + unsafe { + cast::transmute_copy(self) + } + } +} + +impl Drop for FlowRef { + fn drop(&mut self) { + unsafe { + if self.vtable == ptr::null() { + return + } + if flow::base(self.get()).ref_count().fetch_sub(1, SeqCst) > 1 { + return + } + let flow_ref: FlowRef = mem::replace(self, FlowRef { + vtable: ptr::null(), + ptr: ptr::null(), + }); + drop(cast::transmute::<FlowRef,Box<Flow>>(flow_ref)); + self.vtable = ptr::null(); + self.ptr = ptr::null(); + } + } +} + +impl Clone for FlowRef { + fn clone(&self) -> FlowRef { + unsafe { + drop(flow::base(self.get()).ref_count().fetch_add(1, SeqCst)); + FlowRef { + vtable: self.vtable, + ptr: self.ptr, + } + } + } +} + diff --git a/src/components/main/layout/fragment.rs b/src/components/main/layout/fragment.rs index a64d7d50290..713daf72368 100644 --- a/src/components/main/layout/fragment.rs +++ b/src/components/main/layout/fragment.rs @@ -4,6 +4,8 @@ //! The `Box` type, which represents the leaves of the layout tree. +#![deny(unsafe_block)] + use css::node_style::StyledNode; use layout::construct::FlowConstructor; use layout::context::LayoutContext; @@ -38,7 +40,6 @@ use servo_util::range::*; use servo_util::namespace; use servo_util::smallvec::SmallVec; use servo_util::str::is_whitespace; -use std::cast; use std::fmt; use std::from_str::FromStr; use std::iter::AdditiveIterator; @@ -381,9 +382,7 @@ impl Fragment { /// Returns a debug ID of this fragment. This ID should not be considered stable across multiple /// layouts or fragment manipulations. pub fn debug_id(&self) -> uint { - unsafe { - cast::transmute(self) - } + self as *Fragment as uint } /// Transforms this fragment into another fragment of the given type, with the given size, preserving all diff --git a/src/components/main/layout/inline.rs b/src/components/main/layout/inline.rs index 01d544c3ec0..3318aa62ab2 100644 --- a/src/components/main/layout/inline.rs +++ b/src/components/main/layout/inline.rs @@ -2,6 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#![deny(unsafe_block)] + use css::node_style::StyledNode; use layout::context::LayoutContext; use layout::floats::{FloatLeft, Floats, PlacementInfo}; diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index 08252df5417..6cede7b1d9c 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -14,6 +14,7 @@ use layout::context::LayoutContext; use layout::flow::{Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils}; use layout::flow::{PreorderFlowTraversal, PostorderFlowTraversal}; use layout::flow; +use layout::flow_ref::FlowRef; use layout::incremental::RestyleDamage; use layout::parallel::PaddedUnsafeFlow; use layout::parallel; @@ -246,7 +247,7 @@ impl<'a> BuildDisplayListTraversal<'a> { } for absolute_descendant_link in flow::mut_base(flow).abs_descendants.iter() { - self.process(absolute_descendant_link.resolve().unwrap()) + self.process(absolute_descendant_link) } flow.build_display_list(self.layout_context) @@ -455,7 +456,7 @@ impl LayoutTask { } /// Retrieves the flow tree root from the root node. - fn get_layout_root(&self, node: LayoutNode) -> Box<Flow:Share> { + fn get_layout_root(&self, node: LayoutNode) -> FlowRef { let mut layout_data_ref = node.mutate_layout_data(); let result = match &mut *layout_data_ref { &Some(ref mut layout_data) => { @@ -475,7 +476,7 @@ impl LayoutTask { } _ => fail!("Flow construction didn't result in a flow at the root of the tree!"), }; - flow.mark_as_root(); + flow.get_mut().mark_as_root(); flow } @@ -521,13 +522,13 @@ impl LayoutTask { /// benchmarked against those two. It is marked `#[inline(never)]` to aid profiling. #[inline(never)] fn solve_constraints_parallel(&mut self, - layout_root: &mut Box<Flow:Share>, + layout_root: &mut FlowRef, layout_context: &mut LayoutContext) { if layout_context.opts.bubble_widths_separately { let mut traversal = BubbleWidthsTraversal { layout_context: layout_context, }; - layout_root.traverse_postorder(&mut traversal); + layout_root.get_mut().traverse_postorder(&mut traversal); } match self.parallel_traversal { @@ -547,13 +548,13 @@ impl LayoutTask { /// This is only on in debug builds. #[inline(never)] #[cfg(debug)] - fn verify_flow_tree(&mut self, layout_root: &mut Box<Flow:Share>) { + fn verify_flow_tree(&mut self, layout_root: &mut FlowRef) { let mut traversal = FlowTreeVerificationTraversal; layout_root.traverse_preorder(&mut traversal); } #[cfg(not(debug))] - fn verify_flow_tree(&mut self, _: &mut Box<Flow:Share>) { + fn verify_flow_tree(&mut self, _: &mut FlowRef) { } /// The high-level routine that performs layout tasks. @@ -634,10 +635,10 @@ impl LayoutTask { // Propagate damage. profile(time::LayoutDamagePropagateCategory, self.profiler_chan.clone(), || { - layout_root.traverse_preorder(&mut PropagateDamageTraversal { + layout_root.get_mut().traverse_preorder(&mut PropagateDamageTraversal { all_style_damage: all_style_damage }); - layout_root.traverse_postorder(&mut ComputeDamageTraversal.clone()); + layout_root.get_mut().traverse_postorder(&mut ComputeDamageTraversal.clone()); }); // Perform the primary layout passes over the flow tree to compute the locations of all @@ -646,7 +647,7 @@ impl LayoutTask { match self.parallel_traversal { None => { // Sequential mode. - self.solve_constraints(layout_root, &mut layout_ctx) + self.solve_constraints(layout_root.get_mut(), &mut layout_ctx) } Some(_) => { // Parallel mode. @@ -658,14 +659,14 @@ impl LayoutTask { // Build the display list if necessary, and send it to the renderer. if data.goal == ReflowForDisplay { profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone(), || { - layout_ctx.dirty = flow::base(layout_root).position.clone(); + layout_ctx.dirty = flow::base(layout_root.get()).position.clone(); match self.parallel_traversal { None => { let mut traversal = BuildDisplayListTraversal { layout_context: &layout_ctx, }; - traversal.process(layout_root); + traversal.process(layout_root.get_mut()); } Some(ref mut traversal) => { parallel::build_display_list_for_subtree(&mut layout_root, @@ -675,8 +676,9 @@ impl LayoutTask { } } - let root_display_list = mem::replace(&mut flow::mut_base(layout_root).display_list, - DisplayList::new()); + let root_display_list = + mem::replace(&mut flow::mut_base(layout_root.get_mut()).display_list, + DisplayList::new()); let display_list = Arc::new(root_display_list.flatten(ContentStackingLevel)); // FIXME(pcwalton): This is really ugly and can't handle overflow: scroll. Refactor @@ -703,11 +705,11 @@ impl LayoutTask { } } - let root_size = flow::base(layout_root).position.size; + let root_size = flow::base(layout_root.get()).position.size; let root_size = Size2D(root_size.width.to_nearest_px() as uint, root_size.height.to_nearest_px() as uint); let render_layer = RenderLayer { - id: layout_root.layer_id(0), + id: layout_root.get().layer_id(0), display_list: display_list.clone(), position: Rect(Point2D(0u, 0u), root_size), background_color: color, @@ -721,7 +723,7 @@ impl LayoutTask { // reflow. let mut layers = SmallVec1::new(); layers.push(render_layer); - for layer in mem::replace(&mut flow::mut_base(layout_root).layers, + for layer in mem::replace(&mut flow::mut_base(layout_root.get_mut()).layers, DList::new()).move_iter() { layers.push(layer) } @@ -732,8 +734,6 @@ impl LayoutTask { }); } - layout_root.destroy(); - // Tell script that we're done. // // FIXME(pcwalton): This should probably be *one* channel, but we can't fix this without diff --git a/src/components/main/layout/model.rs b/src/components/main/layout/model.rs index 4b35f3aee18..b0adfbabc82 100644 --- a/src/components/main/layout/model.rs +++ b/src/components/main/layout/model.rs @@ -4,6 +4,8 @@ //! Borders, padding, and margins. +#![deny(unsafe_block)] + use layout::fragment::Fragment; use computed = style::computed_values; diff --git a/src/components/main/layout/parallel.rs b/src/components/main/layout/parallel.rs index 13ea72cf22e..6c4c2292c39 100644 --- a/src/components/main/layout/parallel.rs +++ b/src/components/main/layout/parallel.rs @@ -12,6 +12,7 @@ use layout::context::LayoutContext; use layout::extra::LayoutAuxMethods; use layout::flow::{Flow, MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal}; use layout::flow; +use layout::flow_ref::FlowRef; use layout::layout_task::{AssignHeightsAndStoreOverflowTraversal, AssignWidthsTraversal}; use layout::layout_task::{BubbleWidthsTraversal}; use layout::util::{LayoutDataAccess, OpaqueNodeMethods}; @@ -63,13 +64,13 @@ fn null_unsafe_flow() -> UnsafeFlow { (0, 0) } -pub fn owned_flow_to_unsafe_flow(flow: *Box<Flow:Share>) -> UnsafeFlow { +pub fn owned_flow_to_unsafe_flow(flow: *FlowRef) -> UnsafeFlow { unsafe { cast::transmute_copy(&*flow) } } -pub fn mut_owned_flow_to_unsafe_flow(flow: *mut Box<Flow:Share>) -> UnsafeFlow { +pub fn mut_owned_flow_to_unsafe_flow(flow: *mut FlowRef) -> UnsafeFlow { unsafe { cast::transmute_copy(&*flow) } @@ -141,14 +142,14 @@ trait ParallelPostorderFlowTraversal : PostorderFlowTraversal { loop { unsafe { // Get a real flow. - let flow: &mut Box<Flow:Share> = cast::transmute(&unsafe_flow); + let flow: &mut FlowRef = cast::transmute(&unsafe_flow); // Perform the appropriate traversal. - if self.should_process(*flow) { - self.process(*flow); + if self.should_process(flow.get_mut()) { + self.process(flow.get_mut()); } - let base = flow::mut_base(*flow); + let base = flow::mut_base(flow.get_mut()); // Reset the count of children for the next layout traversal. base.parallel.children_count.store(base.children.len() as int, Relaxed); @@ -163,8 +164,8 @@ trait ParallelPostorderFlowTraversal : PostorderFlowTraversal { // No, we're not at the root yet. Then are we the last child // of our parent to finish processing? If so, we can continue // on with our parent; otherwise, we've gotta wait. - let parent: &mut Box<Flow:Share> = cast::transmute(&unsafe_parent); - let parent_base = flow::mut_base(*parent); + let parent: &mut FlowRef = cast::transmute(&unsafe_parent); + let parent_base = flow::mut_base(parent.get_mut()); if parent_base.parallel.children_count.fetch_sub(1, SeqCst) == 1 { // We were the last child of our parent. Reflow our parent. unsafe_flow = unsafe_parent @@ -196,13 +197,13 @@ trait ParallelPreorderFlowTraversal : PreorderFlowTraversal { let mut had_children = false; unsafe { // Get a real flow. - let flow: &mut Box<Flow:Share> = cast::transmute(&unsafe_flow); + let flow: &mut FlowRef = cast::transmute(&unsafe_flow); // Perform the appropriate traversal. - self.process(*flow); + self.process(flow.get_mut()); // Possibly enqueue the children. - for kid in flow::child_iter(*flow) { + for kid in flow::child_iter(flow.get_mut()) { had_children = true; proxy.push(WorkUnit { fun: top_down_func, @@ -421,27 +422,28 @@ fn compute_absolute_position(unsafe_flow: PaddedUnsafeFlow, let mut had_descendants = false; unsafe { // Get a real flow. - let flow: &mut Box<Flow:Share> = cast::transmute(&unsafe_flow); + let flow: &mut FlowRef = cast::transmute(&unsafe_flow); // Compute the absolute position for the flow. - flow.compute_absolute_position(); + flow.get_mut().compute_absolute_position(); // Count the number of absolutely-positioned children, so that we can subtract it from // from `children_and_absolute_descendant_count` to get the number of real children. let mut absolutely_positioned_child_count = 0; - for kid in flow::child_iter(*flow) { + for kid in flow::child_iter(flow.get_mut()) { if kid.is_absolutely_positioned() { absolutely_positioned_child_count += 1; } } // Don't enqueue absolutely positioned children. - drop(flow::mut_base(*flow).parallel - .children_and_absolute_descendant_count - .fetch_sub(absolutely_positioned_child_count as int, SeqCst)); + drop(flow::mut_base(flow.get_mut()).parallel + .children_and_absolute_descendant_count + .fetch_sub(absolutely_positioned_child_count as int, + SeqCst)); // Possibly enqueue the children. - for kid in flow::child_iter(*flow) { + for kid in flow::child_iter(flow.get_mut()) { if !kid.is_absolutely_positioned() { had_descendants = true; proxy.push(WorkUnit { @@ -452,9 +454,9 @@ fn compute_absolute_position(unsafe_flow: PaddedUnsafeFlow, } // Possibly enqueue absolute descendants. - for absolute_descendant_link in flow::mut_base(*flow).abs_descendants.iter() { + for absolute_descendant_link in flow::mut_base(flow.get_mut()).abs_descendants.iter() { had_descendants = true; - let descendant = absolute_descendant_link.resolve().unwrap(); + let descendant = absolute_descendant_link; proxy.push(WorkUnit { fun: compute_absolute_position, data: UnsafeFlowConversions::from_flow(&borrowed_flow_to_unsafe_flow(descendant)), @@ -479,13 +481,13 @@ fn build_display_list(mut unsafe_flow: PaddedUnsafeFlow, loop { unsafe { // Get a real flow. - let flow: &mut Box<Flow:Share> = cast::transmute(&unsafe_flow); + let flow: &mut FlowRef = cast::transmute(&unsafe_flow); // Build display lists. - flow.build_display_list(layout_context); + flow.get_mut().build_display_list(layout_context); { - let base = flow::mut_base(*flow); + let base = flow::mut_base(flow.get_mut()); // Reset the count of children and absolute descendants for the next layout // traversal. @@ -497,12 +499,15 @@ fn build_display_list(mut unsafe_flow: PaddedUnsafeFlow, } // Possibly enqueue the parent. - let unsafe_parent = if flow.is_absolutely_positioned() { - mut_borrowed_flow_to_unsafe_flow(flow::mut_base(*flow).absolute_cb - .resolve() - .unwrap()) + let unsafe_parent = if flow.get().is_absolutely_positioned() { + match *flow::mut_base(flow.get_mut()).absolute_cb.get() { + None => fail!("no absolute containing block for absolutely positioned?!"), + Some(ref mut absolute_cb) => { + mut_borrowed_flow_to_unsafe_flow(absolute_cb.get_mut()) + } + } } else { - flow::mut_base(*flow).parallel.parent + flow::mut_base(flow.get_mut()).parallel.parent }; if unsafe_parent == null_unsafe_flow() { // We're done! @@ -512,8 +517,8 @@ fn build_display_list(mut unsafe_flow: PaddedUnsafeFlow, // No, we're not at the root yet. Then are we the last child // of our parent to finish processing? If so, we can continue // on with our parent; otherwise, we've gotta wait. - let parent: &mut Box<Flow:Share> = cast::transmute(&unsafe_parent); - let parent_base = flow::mut_base(*parent); + let parent: &mut FlowRef = cast::transmute(&unsafe_parent); + let parent_base = flow::mut_base(parent.get_mut()); if parent_base.parallel .children_and_absolute_descendant_count .fetch_sub(1, SeqCst) == 1 { @@ -545,7 +550,7 @@ pub fn recalc_style_for_subtree(root_node: &LayoutNode, queue.data = ptr::mut_null() } -pub fn traverse_flow_tree_preorder(root: &mut Box<Flow:Share>, +pub fn traverse_flow_tree_preorder(root: &mut FlowRef, profiler_chan: ProfilerChan, layout_context: &mut LayoutContext, queue: &mut WorkQueue<*mut LayoutContext,PaddedUnsafeFlow>) { @@ -565,7 +570,7 @@ pub fn traverse_flow_tree_preorder(root: &mut Box<Flow:Share>, queue.data = ptr::mut_null() } -pub fn build_display_list_for_subtree(root: &mut Box<Flow:Share>, +pub fn build_display_list_for_subtree(root: &mut FlowRef, profiler_chan: ProfilerChan, layout_context: &mut LayoutContext, queue: &mut WorkQueue<*mut LayoutContext,PaddedUnsafeFlow>) { diff --git a/src/components/main/layout/table.rs b/src/components/main/layout/table.rs index 6d203023e54..daef27fbc76 100644 --- a/src/components/main/layout/table.rs +++ b/src/components/main/layout/table.rs @@ -4,6 +4,8 @@ //! CSS table formatting contexts. +#![deny(unsafe_block)] + use layout::block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer}; use layout::block::{WidthConstraintInput, WidthConstraintSolution}; use layout::construct::FlowConstructor; diff --git a/src/components/main/layout/table_caption.rs b/src/components/main/layout/table_caption.rs index d53bf0e52b1..dc6f62e8b59 100644 --- a/src/components/main/layout/table_caption.rs +++ b/src/components/main/layout/table_caption.rs @@ -4,6 +4,8 @@ //! CSS table formatting contexts. +#![deny(unsafe_block)] + use layout::block::BlockFlow; use layout::construct::FlowConstructor; use layout::context::LayoutContext; diff --git a/src/components/main/layout/table_cell.rs b/src/components/main/layout/table_cell.rs index b5c29db9f5c..8c26118ae92 100644 --- a/src/components/main/layout/table_cell.rs +++ b/src/components/main/layout/table_cell.rs @@ -4,6 +4,8 @@ //! CSS table formatting contexts. +#![deny(unsafe_block)] + use layout::block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer}; use layout::context::LayoutContext; use layout::flow::{TableCellFlowClass, FlowClass, Flow}; diff --git a/src/components/main/layout/table_colgroup.rs b/src/components/main/layout/table_colgroup.rs index 5adde1fe8fa..ed281703357 100644 --- a/src/components/main/layout/table_colgroup.rs +++ b/src/components/main/layout/table_colgroup.rs @@ -4,6 +4,8 @@ //! CSS table formatting contexts. +#![deny(unsafe_block)] + use layout::context::LayoutContext; use layout::flow::{BaseFlow, TableColGroupFlowClass, FlowClass, Flow}; use layout::fragment::{Fragment, TableColumnFragment}; diff --git a/src/components/main/layout/table_row.rs b/src/components/main/layout/table_row.rs index 80b6dac0013..06594e60bba 100644 --- a/src/components/main/layout/table_row.rs +++ b/src/components/main/layout/table_row.rs @@ -4,6 +4,8 @@ //! CSS table formatting contexts. +#![deny(unsafe_block)] + use layout::block::BlockFlow; use layout::block::WidthAndMarginsComputer; use layout::construct::FlowConstructor; diff --git a/src/components/main/layout/table_rowgroup.rs b/src/components/main/layout/table_rowgroup.rs index 980ebd6d084..644f2ee5125 100644 --- a/src/components/main/layout/table_rowgroup.rs +++ b/src/components/main/layout/table_rowgroup.rs @@ -4,6 +4,8 @@ //! CSS table formatting contexts. +#![deny(unsafe_block)] + use layout::block::BlockFlow; use layout::block::WidthAndMarginsComputer; use layout::construct::FlowConstructor; diff --git a/src/components/main/layout/table_wrapper.rs b/src/components/main/layout/table_wrapper.rs index df64b8a1534..c83dabaa30c 100644 --- a/src/components/main/layout/table_wrapper.rs +++ b/src/components/main/layout/table_wrapper.rs @@ -4,6 +4,8 @@ //! CSS table formatting contexts. +#![deny(unsafe_block)] + use layout::block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer}; use layout::block::{WidthConstraintInput, WidthConstraintSolution}; use layout::construct::FlowConstructor; diff --git a/src/components/main/layout/text.rs b/src/components/main/layout/text.rs index 61f9dd655c9..ca27a542a82 100644 --- a/src/components/main/layout/text.rs +++ b/src/components/main/layout/text.rs @@ -4,6 +4,8 @@ //! Text layout. +#![deny(unsafe_block)] + use layout::flow::Flow; use layout::fragment::{Fragment, ScannedTextFragment, ScannedTextFragmentInfo, UnscannedTextFragment}; diff --git a/src/components/main/layout/wrapper.rs b/src/components/main/layout/wrapper.rs index 3fa7b918785..779416f8de8 100644 --- a/src/components/main/layout/wrapper.rs +++ b/src/components/main/layout/wrapper.rs @@ -33,6 +33,9 @@ //! o Instead of `html_element_in_html_document()`, use //! `html_element_in_html_document_for_layout()`. +use css::node_style::StyledNode; +use layout::util::LayoutDataWrapper; + use script::dom::bindings::codegen::InheritTypes::{HTMLIFrameElementDerived}; use script::dom::bindings::codegen::InheritTypes::{HTMLImageElementDerived, TextDerived}; use script::dom::bindings::js::JS; @@ -41,18 +44,18 @@ use script::dom::element::{HTMLLinkElementTypeId, LayoutElementHelpers, RawLayou use script::dom::htmliframeelement::HTMLIFrameElement; use script::dom::htmlimageelement::{HTMLImageElement, LayoutHTMLImageElementHelpers}; use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, Node, NodeTypeId}; -use script::dom::node::{LayoutNodeHelpers, RawLayoutNodeHelpers}; +use script::dom::node::{LayoutNodeHelpers, RawLayoutNodeHelpers, TextNodeTypeId}; use script::dom::text::Text; use servo_msg::constellation_msg::{PipelineId, SubpageId}; -use servo_util::namespace; use servo_util::namespace::Namespace; +use servo_util::namespace; +use servo_util::str::is_whitespace; use std::cast; use std::cell::{Ref, RefMut}; use std::kinds::marker::ContravariantLifetime; -use style::{PropertyDeclarationBlock, TElement, TNode, AttrSelector, SpecificNamespace}; -use style::{AnyNamespace}; -use style::computed_values::{content, display}; -use layout::util::LayoutDataWrapper; +use style::computed_values::{content, display, white_space}; +use style::{AnyNamespace, AttrSelector, PropertyDeclarationBlock, SpecificNamespace, TElement}; +use style::{TNode}; use url::Url; /// Allows some convenience methods on generic layout nodes. @@ -630,6 +633,31 @@ impl<'ln> ThreadSafeLayoutNode<'ln> { traversal.process(self) } + + pub fn is_ignorable_whitespace(&self) -> bool { + match self.type_id() { + Some(TextNodeTypeId) => { + unsafe { + let text: JS<Text> = self.get_jsmanaged().transmute_copy(); + if !is_whitespace((*text.unsafe_get()).characterdata.data) { + return false + } + + // NB: See the rules for `white-space` here: + // + // http://www.w3.org/TR/CSS21/text.html#propdef-white-space + // + // If you implement other values for this property, you will almost certainly + // want to update this check. + match self.style().get_inheritedtext().white_space { + white_space::normal => true, + _ => false, + } + } + } + _ => false + } + } } pub struct ThreadSafeLayoutNodeChildrenIterator<'a> { diff --git a/src/components/main/servo.rs b/src/components/main/servo.rs index 835c1b727dc..14912a7e179 100644 --- a/src/components/main/servo.rs +++ b/src/components/main/servo.rs @@ -100,6 +100,7 @@ pub mod layout { pub mod floats; pub mod flow; pub mod flow_list; + pub mod flow_ref; pub mod fragment; pub mod layout_task; pub mod inline; |