auto merge of #2570 : pcwalton/servo/ref-count-flows, r=pcwalton

r? @glennw
This commit is contained in:
bors-servo 2014-06-04 01:25:28 -04:00
commit 57ab420616
20 changed files with 486 additions and 454 deletions

View file

@ -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
}
}

View file

@ -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)
}
}
}

View file

@ -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>,
}
pub type DescendantOffsetIter<'a> = Zip<MutItems<'a, Rawlink>, MutItems<'a, Au>>;
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<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(),
}
}
}

View file

@ -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,42 +173,27 @@ 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));
}
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;
}
/// 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()
}
}
}
/// Create an empty list
#[inline]
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]

View file

@ -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,
}
}
}
}

View file

@ -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

View file

@ -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};

View file

@ -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

View file

@ -4,6 +4,8 @@
//! Borders, padding, and margins.
#![deny(unsafe_block)]
use layout::fragment::Fragment;
use computed = style::computed_values;

View file

@ -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>) {

View file

@ -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;

View file

@ -4,6 +4,8 @@
//! CSS table formatting contexts.
#![deny(unsafe_block)]
use layout::block::BlockFlow;
use layout::construct::FlowConstructor;
use layout::context::LayoutContext;

View file

@ -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};

View file

@ -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};

View file

@ -4,6 +4,8 @@
//! CSS table formatting contexts.
#![deny(unsafe_block)]
use layout::block::BlockFlow;
use layout::block::WidthAndMarginsComputer;
use layout::construct::FlowConstructor;

View file

@ -4,6 +4,8 @@
//! CSS table formatting contexts.
#![deny(unsafe_block)]
use layout::block::BlockFlow;
use layout::block::WidthAndMarginsComputer;
use layout::construct::FlowConstructor;

View file

@ -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;

View file

@ -4,6 +4,8 @@
//! Text layout.
#![deny(unsafe_block)]
use layout::flow::Flow;
use layout::fragment::{Fragment, ScannedTextFragment, ScannedTextFragmentInfo, UnscannedTextFragment};

View file

@ -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> {

View file

@ -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;