mirror of
https://github.com/servo/servo.git
synced 2025-07-23 07:13:52 +01:00
layout: Rewrite the anonymous table object generation code.
This patch introduces a "legalizer", which encapsulates all the anonymous flow generation logic in one place. The legalizer knows how to, for example, place adjacent blocks with `display: table-cell` in the same table row. Improves etsy.com. Closes #13782.
This commit is contained in:
parent
3d645a9253
commit
bc2f5864bc
2 changed files with 284 additions and 66 deletions
|
@ -19,8 +19,9 @@ use context::LayoutContext;
|
||||||
use data::{HAS_NEWLY_CONSTRUCTED_FLOW, PersistentLayoutData};
|
use data::{HAS_NEWLY_CONSTRUCTED_FLOW, PersistentLayoutData};
|
||||||
use flex::FlexFlow;
|
use flex::FlexFlow;
|
||||||
use floats::FloatKind;
|
use floats::FloatKind;
|
||||||
use flow::{self, AbsoluteDescendants, IS_ABSOLUTELY_POSITIONED, ImmutableFlowUtils};
|
use flow::{self, AbsoluteDescendants, Flow, FlowClass, ImmutableFlowUtils};
|
||||||
use flow::{CAN_BE_FRAGMENTED, MutableFlowUtils, MutableOwnedFlowUtils};
|
use flow::{CAN_BE_FRAGMENTED, IS_ABSOLUTELY_POSITIONED, MARGINS_CANNOT_COLLAPSE};
|
||||||
|
use flow::{MutableFlowUtils, MutableOwnedFlowUtils};
|
||||||
use flow_ref::{self, FlowRef};
|
use flow_ref::{self, FlowRef};
|
||||||
use fragment::{CanvasFragmentInfo, ImageFragmentInfo, InlineAbsoluteFragmentInfo, SvgFragmentInfo};
|
use fragment::{CanvasFragmentInfo, ImageFragmentInfo, InlineAbsoluteFragmentInfo, SvgFragmentInfo};
|
||||||
use fragment::{Fragment, GeneratedContentInfo, IframeFragmentInfo};
|
use fragment::{Fragment, GeneratedContentInfo, IframeFragmentInfo};
|
||||||
|
@ -48,6 +49,7 @@ use style::computed_values::content::ContentItem;
|
||||||
use style::computed_values::position;
|
use style::computed_values::position;
|
||||||
use style::context::SharedStyleContext;
|
use style::context::SharedStyleContext;
|
||||||
use style::properties::{self, ServoComputedValues};
|
use style::properties::{self, ServoComputedValues};
|
||||||
|
use style::selector_matching::Stylist;
|
||||||
use style::servo_selector_impl::PseudoElement;
|
use style::servo_selector_impl::PseudoElement;
|
||||||
use table::TableFlow;
|
use table::TableFlow;
|
||||||
use table_caption::TableCaptionFlow;
|
use table_caption::TableCaptionFlow;
|
||||||
|
@ -409,12 +411,12 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
||||||
/// `#[inline(always)]` because this is performance critical and LLVM will not inline it
|
/// `#[inline(always)]` because this is performance critical and LLVM will not inline it
|
||||||
/// otherwise.
|
/// otherwise.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn flush_inline_fragments_to_flow_or_list(&mut self,
|
fn flush_inline_fragments_to_flow(&mut self,
|
||||||
fragment_accumulator: InlineFragmentsAccumulator,
|
fragment_accumulator: InlineFragmentsAccumulator,
|
||||||
flow: &mut FlowRef,
|
flow: &mut FlowRef,
|
||||||
flow_list: &mut Vec<FlowRef>,
|
absolute_descendants: &mut AbsoluteDescendants,
|
||||||
absolute_descendants: &mut AbsoluteDescendants,
|
legalizer: &mut Legalizer,
|
||||||
node: &ConcreteThreadSafeLayoutNode) {
|
node: &ConcreteThreadSafeLayoutNode) {
|
||||||
let mut fragments = fragment_accumulator.to_intermediate_inline_fragments();
|
let mut fragments = fragment_accumulator.to_intermediate_inline_fragments();
|
||||||
if fragments.is_empty() {
|
if fragments.is_empty() {
|
||||||
return
|
return
|
||||||
|
@ -482,33 +484,26 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
||||||
}
|
}
|
||||||
|
|
||||||
inline_flow_ref.finish();
|
inline_flow_ref.finish();
|
||||||
|
legalizer.add_child(&self.style_context().stylist, flow, inline_flow_ref)
|
||||||
if flow.need_anonymous_flow(&*inline_flow_ref) {
|
|
||||||
flow_list.push(inline_flow_ref)
|
|
||||||
} else {
|
|
||||||
flow.add_new_child(inline_flow_ref)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_block_flow_using_construction_result_of_child(
|
fn build_block_flow_using_construction_result_of_child(
|
||||||
&mut self,
|
&mut self,
|
||||||
flow: &mut FlowRef,
|
flow: &mut FlowRef,
|
||||||
consecutive_siblings: &mut Vec<FlowRef>,
|
|
||||||
node: &ConcreteThreadSafeLayoutNode,
|
node: &ConcreteThreadSafeLayoutNode,
|
||||||
kid: ConcreteThreadSafeLayoutNode,
|
kid: ConcreteThreadSafeLayoutNode,
|
||||||
inline_fragment_accumulator: &mut InlineFragmentsAccumulator,
|
inline_fragment_accumulator: &mut InlineFragmentsAccumulator,
|
||||||
abs_descendants: &mut AbsoluteDescendants) {
|
abs_descendants: &mut AbsoluteDescendants,
|
||||||
|
legalizer: &mut Legalizer) {
|
||||||
match kid.swap_out_construction_result() {
|
match kid.swap_out_construction_result() {
|
||||||
ConstructionResult::None => {}
|
ConstructionResult::None => {}
|
||||||
ConstructionResult::Flow(mut kid_flow, kid_abs_descendants) => {
|
ConstructionResult::Flow(kid_flow, kid_abs_descendants) => {
|
||||||
// If kid_flow is TableCaptionFlow, kid_flow should be added under
|
// If kid_flow is TableCaptionFlow, kid_flow should be added under
|
||||||
// TableWrapperFlow.
|
// TableWrapperFlow.
|
||||||
if flow.is_table() && kid_flow.is_table_caption() {
|
if flow.is_table() && kid_flow.is_table_caption() {
|
||||||
self.set_flow_construction_result(&kid,
|
let construction_result =
|
||||||
ConstructionResult::Flow(kid_flow,
|
ConstructionResult::Flow(kid_flow, AbsoluteDescendants::new());
|
||||||
AbsoluteDescendants::new()))
|
self.set_flow_construction_result(&kid, construction_result)
|
||||||
} else if flow.need_anonymous_flow(&*kid_flow) {
|
|
||||||
consecutive_siblings.push(kid_flow)
|
|
||||||
} else {
|
} else {
|
||||||
if !flow::base(&*kid_flow).flags.contains(IS_ABSOLUTELY_POSITIONED) {
|
if !flow::base(&*kid_flow).flags.contains(IS_ABSOLUTELY_POSITIONED) {
|
||||||
// Flush any inline fragments that we were gathering up. This allows us to
|
// Flush any inline fragments that we were gathering up. This allows us to
|
||||||
|
@ -516,20 +511,13 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
||||||
let old_inline_fragment_accumulator =
|
let old_inline_fragment_accumulator =
|
||||||
mem::replace(inline_fragment_accumulator,
|
mem::replace(inline_fragment_accumulator,
|
||||||
InlineFragmentsAccumulator::new());
|
InlineFragmentsAccumulator::new());
|
||||||
self.flush_inline_fragments_to_flow_or_list(
|
self.flush_inline_fragments_to_flow(old_inline_fragment_accumulator,
|
||||||
old_inline_fragment_accumulator,
|
flow,
|
||||||
flow,
|
abs_descendants,
|
||||||
consecutive_siblings,
|
legalizer,
|
||||||
abs_descendants,
|
node);
|
||||||
node);
|
|
||||||
}
|
}
|
||||||
|
legalizer.add_child(&self.style_context().stylist, flow, kid_flow)
|
||||||
if !consecutive_siblings.is_empty() {
|
|
||||||
let consecutive_siblings = mem::replace(consecutive_siblings, vec!());
|
|
||||||
self.generate_anonymous_missing_child(consecutive_siblings, flow, node);
|
|
||||||
}
|
|
||||||
self.generate_anonymous_table_flows_if_necessary(flow, &mut kid_flow, &kid);
|
|
||||||
flow.add_new_child(kid_flow);
|
|
||||||
}
|
}
|
||||||
abs_descendants.push_descendants(kid_abs_descendants);
|
abs_descendants.push_descendants(kid_abs_descendants);
|
||||||
}
|
}
|
||||||
|
@ -554,20 +542,16 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
||||||
let old_inline_fragment_accumulator =
|
let old_inline_fragment_accumulator =
|
||||||
mem::replace(inline_fragment_accumulator,
|
mem::replace(inline_fragment_accumulator,
|
||||||
InlineFragmentsAccumulator::new());
|
InlineFragmentsAccumulator::new());
|
||||||
self.flush_inline_fragments_to_flow_or_list(
|
let absolute_descendants =
|
||||||
old_inline_fragment_accumulator,
|
&mut inline_fragment_accumulator.fragments.absolute_descendants;
|
||||||
flow,
|
self.flush_inline_fragments_to_flow(old_inline_fragment_accumulator,
|
||||||
consecutive_siblings,
|
flow,
|
||||||
&mut inline_fragment_accumulator.fragments.absolute_descendants,
|
absolute_descendants,
|
||||||
node);
|
legalizer,
|
||||||
|
node);
|
||||||
|
|
||||||
// Push the flow generated by the {ib} split onto our list of
|
// Push the flow generated by the {ib} split onto our list of flows.
|
||||||
// flows.
|
legalizer.add_child(&self.style_context().stylist, flow, kid_flow)
|
||||||
if flow.need_anonymous_flow(&*kid_flow) {
|
|
||||||
consecutive_siblings.push(kid_flow)
|
|
||||||
} else {
|
|
||||||
flow.add_new_child(kid_flow)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the fragments to the list we're maintaining.
|
// Add the fragments to the list we're maintaining.
|
||||||
|
@ -611,12 +595,12 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
||||||
-> ConstructionResult {
|
-> ConstructionResult {
|
||||||
// Gather up fragments for the inline flows we might need to create.
|
// Gather up fragments for the inline flows we might need to create.
|
||||||
let mut inline_fragment_accumulator = InlineFragmentsAccumulator::new();
|
let mut inline_fragment_accumulator = InlineFragmentsAccumulator::new();
|
||||||
let mut consecutive_siblings = vec!();
|
|
||||||
|
|
||||||
inline_fragment_accumulator.fragments.push_all(initial_fragments);
|
inline_fragment_accumulator.fragments.push_all(initial_fragments);
|
||||||
|
|
||||||
// List of absolute descendants, in tree order.
|
// List of absolute descendants, in tree order.
|
||||||
let mut abs_descendants = AbsoluteDescendants::new();
|
let mut abs_descendants = AbsoluteDescendants::new();
|
||||||
|
let mut legalizer = Legalizer::new();
|
||||||
if !node.is_replaced_content() {
|
if !node.is_replaced_content() {
|
||||||
for kid in node.children() {
|
for kid in node.children() {
|
||||||
if kid.get_pseudo_element_type() != PseudoElementType::Normal {
|
if kid.get_pseudo_element_type() != PseudoElementType::Normal {
|
||||||
|
@ -625,26 +609,24 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
||||||
|
|
||||||
self.build_block_flow_using_construction_result_of_child(
|
self.build_block_flow_using_construction_result_of_child(
|
||||||
&mut flow,
|
&mut flow,
|
||||||
&mut consecutive_siblings,
|
|
||||||
node,
|
node,
|
||||||
kid,
|
kid,
|
||||||
&mut inline_fragment_accumulator,
|
&mut inline_fragment_accumulator,
|
||||||
&mut abs_descendants);
|
&mut abs_descendants,
|
||||||
|
&mut legalizer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform a final flush of any inline fragments that we were gathering up to handle {ib}
|
// Perform a final flush of any inline fragments that we were gathering up to handle {ib}
|
||||||
// splits, after stripping ignorable whitespace.
|
// splits, after stripping ignorable whitespace.
|
||||||
self.flush_inline_fragments_to_flow_or_list(inline_fragment_accumulator,
|
self.flush_inline_fragments_to_flow(inline_fragment_accumulator,
|
||||||
&mut flow,
|
&mut flow,
|
||||||
&mut consecutive_siblings,
|
&mut abs_descendants,
|
||||||
&mut abs_descendants,
|
&mut legalizer,
|
||||||
node);
|
node);
|
||||||
if !consecutive_siblings.is_empty() {
|
|
||||||
self.generate_anonymous_missing_child(consecutive_siblings, &mut flow, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The flow is done.
|
// The flow is done.
|
||||||
|
legalizer.finish(&mut flow);
|
||||||
flow.finish();
|
flow.finish();
|
||||||
|
|
||||||
// Set up the absolute descendants.
|
// Set up the absolute descendants.
|
||||||
|
@ -1162,11 +1144,26 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
||||||
|
|
||||||
/// Builds a flow for a node with `display: table`. This yields a `TableWrapperFlow` with
|
/// Builds a flow for a node with `display: table`. This yields a `TableWrapperFlow` with
|
||||||
/// possibly other `TableCaptionFlow`s or `TableFlow`s underneath it.
|
/// possibly other `TableCaptionFlow`s or `TableFlow`s underneath it.
|
||||||
fn build_flow_for_table_wrapper(&mut self, node: &ConcreteThreadSafeLayoutNode, float_value: float::T)
|
fn build_flow_for_table(&mut self, node: &ConcreteThreadSafeLayoutNode, float_value: float::T)
|
||||||
-> ConstructionResult {
|
-> ConstructionResult {
|
||||||
let fragment = Fragment::new(node, SpecificFragmentInfo::TableWrapper, self.layout_context);
|
let mut legalizer = Legalizer::new();
|
||||||
let mut wrapper_flow: FlowRef = Arc::new(
|
|
||||||
TableWrapperFlow::from_fragment_and_float_kind(fragment, FloatKind::from_property(float_value)));
|
let table_style = node.style(self.style_context());
|
||||||
|
let wrapper_style = self.style_context()
|
||||||
|
.stylist
|
||||||
|
.style_for_anonymous_box(&PseudoElement::ServoTableWrapper,
|
||||||
|
&table_style);
|
||||||
|
let wrapper_fragment =
|
||||||
|
Fragment::from_opaque_node_and_style(node.opaque(),
|
||||||
|
PseudoElementType::Normal,
|
||||||
|
wrapper_style,
|
||||||
|
node.selected_style(self.style_context()),
|
||||||
|
node.restyle_damage(),
|
||||||
|
SpecificFragmentInfo::TableWrapper);
|
||||||
|
let wrapper_float_kind = FloatKind::from_property(float_value);
|
||||||
|
let mut wrapper_flow: FlowRef =
|
||||||
|
Arc::new(TableWrapperFlow::from_fragment_and_float_kind(wrapper_fragment,
|
||||||
|
wrapper_float_kind));
|
||||||
|
|
||||||
let table_fragment = Fragment::new(node, SpecificFragmentInfo::Table, self.layout_context);
|
let table_fragment = Fragment::new(node, SpecificFragmentInfo::Table, self.layout_context);
|
||||||
let table_flow = Arc::new(TableFlow::from_fragment(table_fragment));
|
let table_flow = Arc::new(TableFlow::from_fragment(table_fragment));
|
||||||
|
@ -1184,7 +1181,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
||||||
caption_side::T::top);
|
caption_side::T::top);
|
||||||
|
|
||||||
if let ConstructionResult::Flow(table_flow, table_abs_descendants) = construction_result {
|
if let ConstructionResult::Flow(table_flow, table_abs_descendants) = construction_result {
|
||||||
wrapper_flow.add_new_child(table_flow);
|
legalizer.add_child(&self.style_context().stylist, &mut wrapper_flow, table_flow);
|
||||||
abs_descendants.push_descendants(table_abs_descendants);
|
abs_descendants.push_descendants(table_abs_descendants);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1194,6 +1191,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
||||||
caption_side::T::bottom);
|
caption_side::T::bottom);
|
||||||
|
|
||||||
// The flow is done.
|
// The flow is done.
|
||||||
|
legalizer.finish(&mut wrapper_flow);
|
||||||
wrapper_flow.finish();
|
wrapper_flow.finish();
|
||||||
let contains_positioned_fragments = wrapper_flow.contains_positioned_fragments();
|
let contains_positioned_fragments = wrapper_flow.contains_positioned_fragments();
|
||||||
if contains_positioned_fragments {
|
if contains_positioned_fragments {
|
||||||
|
@ -1570,7 +1568,7 @@ impl<'a, ConcreteThreadSafeLayoutNode> PostorderNodeMutTraversal<ConcreteThreadS
|
||||||
|
|
||||||
// Table items contribute table flow construction results.
|
// Table items contribute table flow construction results.
|
||||||
(display::T::table, float_value, _) => {
|
(display::T::table, float_value, _) => {
|
||||||
let construction_result = self.build_flow_for_table_wrapper(node, float_value);
|
let construction_result = self.build_flow_for_table(node, float_value);
|
||||||
self.set_flow_construction_result(node, construction_result)
|
self.set_flow_construction_result(node, construction_result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1924,3 +1922,199 @@ impl ComputedValueUtils for ServoComputedValues {
|
||||||
border.border_left_width != Au(0)
|
border.border_left_width != Au(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Maintains a stack of anonymous boxes needed to ensure that the flow tree is *legal*. The tree
|
||||||
|
/// is legal if it follows the rules in CSS 2.1 § 17.2.1.
|
||||||
|
///
|
||||||
|
/// As an example, the legalizer makes sure that table row flows contain only table cells. If the
|
||||||
|
/// flow constructor attempts to place, say, a block flow directly underneath the table row, the
|
||||||
|
/// legalizer generates an anonymous table cell in between to hold the block.
|
||||||
|
///
|
||||||
|
/// Generally, the flow constructor should use `Legalizer::add_child()` instead of calling
|
||||||
|
/// `Flow::add_new_child()` directly. This ensures that the flow tree remains legal at all times
|
||||||
|
/// and centralizes the anonymous flow generation logic in one place.
|
||||||
|
struct Legalizer {
|
||||||
|
/// A stack of anonymous flows that have yet to be finalized (i.e. that still could acquire new
|
||||||
|
/// children).
|
||||||
|
stack: Vec<FlowRef>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Legalizer {
|
||||||
|
/// Creates a new legalizer.
|
||||||
|
fn new() -> Legalizer {
|
||||||
|
Legalizer {
|
||||||
|
stack: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Makes the `child` flow a new child of `parent`. Anonymous flows are automatically inserted
|
||||||
|
/// to keep the tree legal.
|
||||||
|
fn add_child(&mut self, stylist: &Stylist, parent: &mut FlowRef, mut child: FlowRef) {
|
||||||
|
while !self.stack.is_empty() {
|
||||||
|
if self.try_to_add_child(stylist, parent, &mut child) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.flush_top_of_stack(parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
while !self.try_to_add_child(stylist, parent, &mut child) {
|
||||||
|
self.push_next_anonymous_flow(stylist, parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flushes all flows we've been gathering up.
|
||||||
|
fn finish(mut self, parent: &mut FlowRef) {
|
||||||
|
while !self.stack.is_empty() {
|
||||||
|
self.flush_top_of_stack(parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempts to make `child` a child of `parent`. On success, this returns true. If this would
|
||||||
|
/// make the tree illegal, this method does nothing and returns false.
|
||||||
|
///
|
||||||
|
/// This method attempts to create anonymous blocks in between `parent` and `child` if and only
|
||||||
|
/// if those blocks will only ever have `child` as their sole child. At present, this is only
|
||||||
|
/// true for anonymous block children of flex flows.
|
||||||
|
fn try_to_add_child(&mut self, stylist: &Stylist, parent: &mut FlowRef, child: &mut FlowRef)
|
||||||
|
-> bool {
|
||||||
|
let mut parent = self.stack.last_mut().unwrap_or(parent);
|
||||||
|
let (parent_class, child_class) = (parent.class(), child.class());
|
||||||
|
match (parent_class, child_class) {
|
||||||
|
(FlowClass::TableWrapper, FlowClass::Table) |
|
||||||
|
(FlowClass::Table, FlowClass::TableColGroup) |
|
||||||
|
(FlowClass::Table, FlowClass::TableRowGroup) |
|
||||||
|
(FlowClass::Table, FlowClass::TableRow) |
|
||||||
|
(FlowClass::Table, FlowClass::TableCaption) |
|
||||||
|
(FlowClass::TableRowGroup, FlowClass::TableRow) |
|
||||||
|
(FlowClass::TableRow, FlowClass::TableCell) => {
|
||||||
|
parent.add_new_child((*child).clone());
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
(FlowClass::TableWrapper, _) |
|
||||||
|
(FlowClass::Table, _) |
|
||||||
|
(FlowClass::TableRowGroup, _) |
|
||||||
|
(FlowClass::TableRow, _) |
|
||||||
|
(_, FlowClass::Table) |
|
||||||
|
(_, FlowClass::TableColGroup) |
|
||||||
|
(_, FlowClass::TableRowGroup) |
|
||||||
|
(_, FlowClass::TableRow) |
|
||||||
|
(_, FlowClass::TableCaption) |
|
||||||
|
(_, FlowClass::TableCell) => {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
(FlowClass::Flex, FlowClass::Inline) => {
|
||||||
|
flow::mut_base(flow_ref::deref_mut(child)).flags.insert(MARGINS_CANNOT_COLLAPSE);
|
||||||
|
let mut block_wrapper =
|
||||||
|
Legalizer::create_anonymous_flow(stylist,
|
||||||
|
parent,
|
||||||
|
&[PseudoElement::ServoAnonymousBlock],
|
||||||
|
SpecificFragmentInfo::Generic,
|
||||||
|
BlockFlow::from_fragment);
|
||||||
|
flow::mut_base(flow_ref::deref_mut(&mut
|
||||||
|
block_wrapper)).flags
|
||||||
|
.insert(MARGINS_CANNOT_COLLAPSE);
|
||||||
|
block_wrapper.add_new_child((*child).clone());
|
||||||
|
block_wrapper.finish();
|
||||||
|
parent.add_new_child(block_wrapper);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
(FlowClass::Flex, _) => {
|
||||||
|
flow::mut_base(flow_ref::deref_mut(child)).flags.insert(MARGINS_CANNOT_COLLAPSE);
|
||||||
|
parent.add_new_child((*child).clone());
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
parent.add_new_child((*child).clone());
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finalizes the flow on the top of the stack.
|
||||||
|
fn flush_top_of_stack(&mut self, parent: &mut FlowRef) {
|
||||||
|
let mut child = self.stack.pop().expect("flush_top_of_stack(): stack empty");
|
||||||
|
child.finish();
|
||||||
|
self.stack.last_mut().unwrap_or(parent).add_new_child(child)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds the anonymous flow that would be necessary to make an illegal child of `parent` legal
|
||||||
|
/// to the stack.
|
||||||
|
fn push_next_anonymous_flow(&mut self, stylist: &Stylist, parent: &FlowRef) {
|
||||||
|
let parent_class = self.stack.last().unwrap_or(parent).class();
|
||||||
|
match parent_class {
|
||||||
|
FlowClass::TableRow => {
|
||||||
|
self.push_new_anonymous_flow(stylist,
|
||||||
|
parent,
|
||||||
|
&[PseudoElement::ServoAnonymousTableCell],
|
||||||
|
SpecificFragmentInfo::TableCell,
|
||||||
|
TableCellFlow::from_fragment)
|
||||||
|
}
|
||||||
|
FlowClass::Table | FlowClass::TableRowGroup => {
|
||||||
|
self.push_new_anonymous_flow(stylist,
|
||||||
|
parent,
|
||||||
|
&[PseudoElement::ServoAnonymousTableRow],
|
||||||
|
SpecificFragmentInfo::TableRow,
|
||||||
|
TableRowFlow::from_fragment)
|
||||||
|
}
|
||||||
|
FlowClass::TableWrapper => {
|
||||||
|
self.push_new_anonymous_flow(stylist,
|
||||||
|
parent,
|
||||||
|
&[PseudoElement::ServoAnonymousTable],
|
||||||
|
SpecificFragmentInfo::Table,
|
||||||
|
TableFlow::from_fragment)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.push_new_anonymous_flow(stylist,
|
||||||
|
parent,
|
||||||
|
&[PseudoElement::ServoTableWrapper,
|
||||||
|
PseudoElement::ServoAnonymousTableWrapper],
|
||||||
|
SpecificFragmentInfo::TableWrapper,
|
||||||
|
TableWrapperFlow::from_fragment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an anonymous flow and pushes it onto the stack.
|
||||||
|
fn push_new_anonymous_flow<F>(&mut self,
|
||||||
|
stylist: &Stylist,
|
||||||
|
reference: &FlowRef,
|
||||||
|
pseudos: &[PseudoElement],
|
||||||
|
specific_fragment_info: SpecificFragmentInfo,
|
||||||
|
constructor: extern "Rust" fn(Fragment) -> F)
|
||||||
|
where F: Flow {
|
||||||
|
let new_flow = Legalizer::create_anonymous_flow(stylist,
|
||||||
|
reference,
|
||||||
|
pseudos,
|
||||||
|
specific_fragment_info,
|
||||||
|
constructor);
|
||||||
|
self.stack.push(new_flow)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new anonymous flow. The new flow is identical to `reference` except with all
|
||||||
|
/// styles applying to every pseudo-element in `pseudos` applied.
|
||||||
|
///
|
||||||
|
/// This method invokes the supplied constructor function on the given specific fragment info
|
||||||
|
/// in order to actually generate the flow.
|
||||||
|
fn create_anonymous_flow<F>(stylist: &Stylist,
|
||||||
|
reference: &FlowRef,
|
||||||
|
pseudos: &[PseudoElement],
|
||||||
|
specific_fragment_info: SpecificFragmentInfo,
|
||||||
|
constructor: extern "Rust" fn(Fragment) -> F)
|
||||||
|
-> FlowRef
|
||||||
|
where F: Flow {
|
||||||
|
let reference_block = reference.as_block();
|
||||||
|
let mut new_style = reference_block.fragment.style.clone();
|
||||||
|
for pseudo in pseudos {
|
||||||
|
new_style = stylist.style_for_anonymous_box(pseudo, &new_style)
|
||||||
|
}
|
||||||
|
let fragment = reference_block.fragment
|
||||||
|
.create_similar_anonymous_fragment(new_style,
|
||||||
|
specific_fragment_info);
|
||||||
|
Arc::new(constructor(fragment))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -949,6 +949,30 @@ impl Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates an anonymous fragment just like this one but with the given style and fragment
|
||||||
|
/// type. For the new anonymous fragment, layout-related values (border box, etc.) are reset to
|
||||||
|
/// initial values.
|
||||||
|
pub fn create_similar_anonymous_fragment(&self,
|
||||||
|
style: Arc<ServoComputedValues>,
|
||||||
|
specific: SpecificFragmentInfo)
|
||||||
|
-> Fragment {
|
||||||
|
let writing_mode = style.writing_mode;
|
||||||
|
Fragment {
|
||||||
|
node: self.node,
|
||||||
|
style: style,
|
||||||
|
selected_style: self.selected_style.clone(),
|
||||||
|
restyle_damage: self.restyle_damage,
|
||||||
|
border_box: LogicalRect::zero(writing_mode),
|
||||||
|
border_padding: LogicalMargin::zero(writing_mode),
|
||||||
|
margin: LogicalMargin::zero(writing_mode),
|
||||||
|
specific: specific,
|
||||||
|
inline_context: None,
|
||||||
|
pseudo: self.pseudo,
|
||||||
|
debug_id: DebugId::new(),
|
||||||
|
stacking_context_id: StackingContextId::new(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Transforms this fragment into another fragment of the given type, with the given size,
|
/// Transforms this fragment into another fragment of the given type, with the given size,
|
||||||
/// preserving all the other data.
|
/// preserving all the other data.
|
||||||
pub fn transform(&self, size: LogicalSize<Au>, info: SpecificFragmentInfo)
|
pub fn transform(&self, size: LogicalSize<Au>, info: SpecificFragmentInfo)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue