Auto merge of #13870 - pcwalton:anonymous-table-rewrite, r=mbrubeck

layout: Rewrite anonymous table code, simplify and fix table intrinsic width calculation, and improve safety of flexbox code.

Closes #13782.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/13870)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-10-26 20:14:04 -05:00 committed by GitHub
commit 6b40f97289
139 changed files with 932 additions and 896 deletions

View file

@ -36,7 +36,7 @@ use floats::{ClearType, FloatKind, Floats, PlacementInfo};
use flow::{self, BaseFlow, EarlyAbsolutePositionInfo, Flow, FlowClass, ForceNonfloatedFlag}; use flow::{self, BaseFlow, EarlyAbsolutePositionInfo, Flow, FlowClass, ForceNonfloatedFlag};
use flow::{BLOCK_POSITION_IS_STATIC, CLEARS_LEFT, CLEARS_RIGHT}; use flow::{BLOCK_POSITION_IS_STATIC, CLEARS_LEFT, CLEARS_RIGHT};
use flow::{CONTAINS_TEXT_OR_REPLACED_FRAGMENTS, INLINE_POSITION_IS_STATIC}; use flow::{CONTAINS_TEXT_OR_REPLACED_FRAGMENTS, INLINE_POSITION_IS_STATIC};
use flow::{FragmentationContext, PreorderFlowTraversal}; use flow::{FragmentationContext, MARGINS_CANNOT_COLLAPSE, PreorderFlowTraversal};
use flow::{ImmutableFlowUtils, LateAbsolutePositionInfo, MutableFlowUtils, OpaqueFlow}; use flow::{ImmutableFlowUtils, LateAbsolutePositionInfo, MutableFlowUtils, OpaqueFlow};
use flow::IS_ABSOLUTELY_POSITIONED; use flow::IS_ABSOLUTELY_POSITIONED;
use flow_list::FlowList; use flow_list::FlowList;
@ -533,7 +533,12 @@ impl Encodable for BlockFlowFlags {
} }
impl BlockFlow { impl BlockFlow {
pub fn from_fragment(fragment: Fragment, float_kind: Option<FloatKind>) -> BlockFlow { pub fn from_fragment(fragment: Fragment) -> BlockFlow {
BlockFlow::from_fragment_and_float_kind(fragment, None)
}
pub fn from_fragment_and_float_kind(fragment: Fragment, float_kind: Option<FloatKind>)
-> BlockFlow {
let writing_mode = fragment.style().writing_mode; let writing_mode = fragment.style().writing_mode;
BlockFlow { BlockFlow {
base: BaseFlow::new(Some(fragment.style()), writing_mode, match float_kind { base: BaseFlow::new(Some(fragment.style()), writing_mode, match float_kind {
@ -1452,7 +1457,8 @@ impl BlockFlow {
display::T::table_caption | display::T::table_caption |
display::T::table_row_group | display::T::table_row_group |
display::T::table | display::T::table |
display::T::inline_block => { display::T::inline_block |
display::T::flex => {
FormattingContextType::Other FormattingContextType::Other
} }
_ if style.get_box().overflow_x != overflow_x::T::visible || _ if style.get_box().overflow_x != overflow_x::T::visible ||
@ -1579,12 +1585,12 @@ impl BlockFlow {
/// used for calculating shrink-to-fit width. Assumes that intrinsic sizes have already been /// used for calculating shrink-to-fit width. Assumes that intrinsic sizes have already been
/// computed for this flow. /// computed for this flow.
fn content_intrinsic_inline_sizes(&self) -> IntrinsicISizes { fn content_intrinsic_inline_sizes(&self) -> IntrinsicISizes {
let surrounding_inline_size = self.fragment.surrounding_intrinsic_inline_size(); let (border_padding, margin) = self.fragment.surrounding_intrinsic_inline_size();
IntrinsicISizes { IntrinsicISizes {
minimum_inline_size: self.base.intrinsic_inline_sizes.minimum_inline_size - minimum_inline_size: self.base.intrinsic_inline_sizes.minimum_inline_size -
surrounding_inline_size, border_padding - margin,
preferred_inline_size: self.base.intrinsic_inline_sizes.preferred_inline_size - preferred_inline_size: self.base.intrinsic_inline_sizes.preferred_inline_size -
surrounding_inline_size, border_padding - margin,
} }
} }
@ -1906,7 +1912,9 @@ impl Flow for BlockFlow {
self.fragment.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW); self.fragment.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW);
} }
None None
} else if self.is_root() || self.formatting_context_type() != FormattingContextType::None { } else if self.is_root() ||
self.formatting_context_type() != FormattingContextType::None ||
self.base.flags.contains(MARGINS_CANNOT_COLLAPSE) {
// Root element margins should never be collapsed according to CSS § 8.3.1. // Root element margins should never be collapsed according to CSS § 8.3.1.
debug!("assign_block_size: assigning block_size for root flow {:?}", debug!("assign_block_size: assigning block_size for root flow {:?}",
flow::base(self).debug_id()); flow::base(self).debug_id());

View file

@ -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;
@ -348,72 +350,17 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
Fragment::new(node, specific_fragment_info, self.layout_context) Fragment::new(node, specific_fragment_info, self.layout_context)
} }
/// Generates anonymous table objects per CSS 2.1 § 17.2.1.
fn generate_anonymous_table_flows_if_necessary(&mut self,
flow: &mut FlowRef,
child: &mut FlowRef,
child_node: &ConcreteThreadSafeLayoutNode) {
if !flow.is_block_flow() {
return
}
let style_context = self.style_context();
if child.is_table_cell() {
let mut style = child_node.style(style_context);
properties::modify_style_for_anonymous_table_object(&mut style, display::T::table_row);
let fragment = Fragment::from_opaque_node_and_style(child_node.opaque(),
PseudoElementType::Normal,
style,
child_node.selected_style(style_context),
child_node.restyle_damage(),
SpecificFragmentInfo::TableRow);
let mut new_child: FlowRef = Arc::new(TableRowFlow::from_fragment(fragment));
new_child.add_new_child(child.clone());
child.finish();
*child = new_child
}
if child.is_table_row() || child.is_table_rowgroup() {
let mut style = child_node.style(style_context);
properties::modify_style_for_anonymous_table_object(&mut style, display::T::table);
let fragment = Fragment::from_opaque_node_and_style(child_node.opaque(),
PseudoElementType::Normal,
style,
child_node.selected_style(style_context),
child_node.restyle_damage(),
SpecificFragmentInfo::Table);
let mut new_child: FlowRef = Arc::new(TableFlow::from_fragment(fragment));
new_child.add_new_child(child.clone());
child.finish();
*child = new_child
}
if child.is_table() {
let mut style = child_node.style(style_context);
properties::modify_style_for_anonymous_table_object(&mut style, display::T::table);
let fragment =
Fragment::from_opaque_node_and_style(child_node.opaque(),
PseudoElementType::Normal,
style,
child_node.selected_style(style_context),
child_node.restyle_damage(),
SpecificFragmentInfo::TableWrapper);
let mut new_child: FlowRef = Arc::new(TableWrapperFlow::from_fragment(fragment, None));
new_child.add_new_child(child.clone());
child.finish();
*child = new_child
}
}
/// Creates an inline flow from a set of inline fragments, then adds it as a child of the given /// Creates an inline flow from a set of inline fragments, then adds it as a child of the given
/// flow or pushes it onto the given flow list. /// flow or pushes it onto the given flow list.
/// ///
/// `#[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() {
@ -450,8 +397,9 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
let scanned_fragments = let scanned_fragments =
TextRunScanner::new().scan_for_runs(&mut self.layout_context.font_context(), TextRunScanner::new().scan_for_runs(&mut self.layout_context.font_context(),
fragments.fragments); fragments.fragments);
let mut inline_flow_ref: FlowRef = Arc::new( let mut inline_flow_ref: FlowRef =
InlineFlow::from_fragments(scanned_fragments, node.style(self.style_context()).writing_mode)); Arc::new(InlineFlow::from_fragments(scanned_fragments,
node.style(self.style_context()).writing_mode));
// Add all the inline-block fragments as children of the inline flow. // Add all the inline-block fragments as children of the inline flow.
for inline_block_flow in &inline_block_flows { for inline_block_flow in &inline_block_flows {
@ -482,33 +430,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 +457,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,
consecutive_siblings,
abs_descendants, abs_descendants,
legalizer,
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 +488,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;
self.flush_inline_fragments_to_flow(old_inline_fragment_accumulator,
flow, flow,
consecutive_siblings, absolute_descendants,
&mut inline_fragment_accumulator.fragments.absolute_descendants, legalizer,
node); 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 +541,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 +555,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.
@ -693,8 +621,9 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
let mut style = node.style(self.style_context()); let mut style = node.style(self.style_context());
if node_is_input_or_text_area { if node_is_input_or_text_area {
style = self.style_context().stylist. style = self.style_context()
precomputed_values_for_pseudo(&PseudoElement::ServoInputText, Some(&style)).unwrap(); .stylist
.style_for_anonymous_box(&PseudoElement::ServoInputText, &style)
} }
self.create_fragments_for_node_text_content(&mut initial_fragments, node, &style) self.create_fragments_for_node_text_content(&mut initial_fragments, node, &style)
@ -768,8 +697,9 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
return self.build_flow_for_multicol(node, float_kind) return self.build_flow_for_multicol(node, float_kind)
} }
let flow: FlowRef = Arc::new( let fragment = self.build_fragment_for_block(node);
BlockFlow::from_fragment(self.build_fragment_for_block(node), float_kind)); let flow: FlowRef =
Arc::new(BlockFlow::from_fragment_and_float_kind(fragment, float_kind));
self.build_flow_for_block_like(flow, node) self.build_flow_for_block_like(flow, node)
} }
@ -1088,38 +1018,10 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
} }
} }
/// 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<FlowRef>,
flow: &mut FlowRef,
node: &ConcreteThreadSafeLayoutNode) {
let mut anonymous_flow = flow.generate_missing_child_flow(node, self.layout_context);
let mut consecutive_siblings = vec!();
for kid_flow in child_flows {
if anonymous_flow.need_anonymous_flow(&*kid_flow) {
consecutive_siblings.push(kid_flow);
continue;
}
if !consecutive_siblings.is_empty() {
self.generate_anonymous_missing_child(consecutive_siblings,
&mut anonymous_flow,
node);
consecutive_siblings = vec!();
}
anonymous_flow.add_new_child(kid_flow);
}
if !consecutive_siblings.is_empty() {
self.generate_anonymous_missing_child(consecutive_siblings, &mut anonymous_flow, node);
}
// The flow is done.
anonymous_flow.finish();
flow.add_new_child(anonymous_flow);
}
/// Builds a flow for a node with `column-count` or `column-width` non-`auto`. /// Builds a flow for a node with `column-count` or `column-width` non-`auto`.
/// This yields a `MulticolFlow` with a single `MulticolColumnFlow` underneath it. /// This yields a `MulticolFlow` with a single `MulticolColumnFlow` underneath it.
fn build_flow_for_multicol(&mut self, node: &ConcreteThreadSafeLayoutNode, fn build_flow_for_multicol(&mut self,
node: &ConcreteThreadSafeLayoutNode,
float_kind: Option<FloatKind>) float_kind: Option<FloatKind>)
-> ConstructionResult { -> ConstructionResult {
let fragment = Fragment::new(node, SpecificFragmentInfo::Multicol, self.layout_context); let fragment = Fragment::new(node, SpecificFragmentInfo::Multicol, self.layout_context);
@ -1161,11 +1063,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(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));
@ -1183,7 +1100,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);
} }
@ -1193,7 +1110,9 @@ 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 {
// This is the containing block for all the absolute descendants. // This is the containing block for all the absolute descendants.
@ -1261,7 +1180,9 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
/// Builds a flow for a node with `display: list-item`. This yields a `ListItemFlow` with /// Builds a flow for a node with `display: list-item`. This yields a `ListItemFlow` with
/// possibly other `BlockFlow`s or `InlineFlow`s underneath it. /// possibly other `BlockFlow`s or `InlineFlow`s underneath it.
fn build_flow_for_list_item(&mut self, node: &ConcreteThreadSafeLayoutNode, flotation: float::T) fn build_flow_for_list_item(&mut self,
node: &ConcreteThreadSafeLayoutNode,
flotation: float::T)
-> ConstructionResult { -> ConstructionResult {
let flotation = FloatKind::from_property(flotation); let flotation = FloatKind::from_property(flotation);
let marker_fragments = match node.style(self.style_context()).get_list().list_style_image { let marker_fragments = match node.style(self.style_context()).get_list().list_style_image {
@ -1364,7 +1285,9 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
} }
/// Builds a flow for a node with 'display: flex'. /// Builds a flow for a node with 'display: flex'.
fn build_flow_for_flex(&mut self, node: &ConcreteThreadSafeLayoutNode, float_kind: Option<FloatKind>) fn build_flow_for_flex(&mut self,
node: &ConcreteThreadSafeLayoutNode,
float_kind: Option<FloatKind>)
-> ConstructionResult { -> ConstructionResult {
let fragment = self.build_fragment_for_block(node); let fragment = self.build_fragment_for_block(node);
let flow = Arc::new(FlexFlow::from_fragment(fragment, float_kind)); let flow = Arc::new(FlexFlow::from_fragment(fragment, float_kind));
@ -1569,7 +1492,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)
} }
@ -1923,3 +1846,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))
}
}

View file

@ -7,7 +7,7 @@
#![deny(unsafe_code)] #![deny(unsafe_code)]
use app_units::{Au, MAX_AU}; use app_units::{Au, MAX_AU};
use block::BlockFlow; use block::{BlockFlow, MarginsMayCollapseFlag};
use context::{LayoutContext, SharedLayoutContext}; use context::{LayoutContext, SharedLayoutContext};
use display_list_builder::{DisplayListBuildState, FlexFlowDisplayListBuilding}; use display_list_builder::{DisplayListBuildState, FlexFlowDisplayListBuilding};
use euclid::Point2D; use euclid::Point2D;
@ -15,7 +15,6 @@ use floats::FloatKind;
use flow; use flow;
use flow::{Flow, FlowClass, ImmutableFlowUtils, OpaqueFlow}; use flow::{Flow, FlowClass, ImmutableFlowUtils, OpaqueFlow};
use flow::{INLINE_POSITION_IS_STATIC, IS_ABSOLUTELY_POSITIONED}; use flow::{INLINE_POSITION_IS_STATIC, IS_ABSOLUTELY_POSITIONED};
use flow_ref::{self, FlowRef};
use fragment::{Fragment, FragmentBorderBoxIterator, Overflow}; use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
use gfx::display_list::StackingContext; use gfx::display_list::StackingContext;
use layout_debug; use layout_debug;
@ -113,10 +112,8 @@ struct FlexItem {
/// The maximal main size. If this property is not actually set by style /// The maximal main size. If this property is not actually set by style
/// It will be the largest size available for code reuse. /// It will be the largest size available for code reuse.
pub max_size: Au, pub max_size: Au,
/// Reference to the actual flow. /// The index of the actual flow in our child list.
pub flow: FlowRef, pub index: usize,
/// Style of the child flow, stored here to reduce overhead.
pub style: Arc<ServoComputedValues>,
/// The 'flex-grow' property of this item. /// The 'flex-grow' property of this item.
pub flex_grow: f32, pub flex_grow: f32,
/// The 'flex-shrink' property of this item. /// The 'flex-shrink' property of this item.
@ -130,8 +127,8 @@ struct FlexItem {
} }
impl FlexItem { impl FlexItem {
pub fn new(flow: FlowRef) -> FlexItem { pub fn new(index: usize, flow: &Flow) -> FlexItem {
let style = flow.as_block().fragment.style.clone(); let style = &flow.as_block().fragment.style;
let flex_grow = style.get_position().flex_grow; let flex_grow = style.get_position().flex_grow;
let flex_shrink = style.get_position().flex_shrink; let flex_shrink = style.get_position().flex_shrink;
let order = style.get_position().order; let order = style.get_position().order;
@ -142,8 +139,7 @@ impl FlexItem {
base_size: Au(0), base_size: Au(0),
min_size: Au(0), min_size: Au(0),
max_size: MAX_AU, max_size: MAX_AU,
flow: flow, index: index,
style: style,
flex_grow: flex_grow, flex_grow: flex_grow,
flex_shrink: flex_shrink, flex_shrink: flex_shrink,
order: order, order: order,
@ -155,15 +151,15 @@ impl FlexItem {
/// Initialize the used flex base size, minimal main size and maximal main size. /// Initialize the used flex base size, minimal main size and maximal main size.
/// For block mode container this method should be called in assign_block_size() /// For block mode container this method should be called in assign_block_size()
/// pass so that the item has already been layouted. /// pass so that the item has already been layouted.
pub fn init_sizes(&mut self, containing_length: Au, direction: Direction) { pub fn init_sizes(&mut self, flow: &mut Flow, containing_length: Au, direction: Direction) {
let block = flow_ref::deref_mut(&mut self.flow).as_mut_block(); let block = flow.as_mut_block();
match direction { match direction {
// TODO(stshine): the definition of min-{width, height} in style component // TODO(stshine): the definition of min-{width, height} in style component
// should change to LengthOrPercentageOrAuto for automatic implied minimal size. // should change to LengthOrPercentageOrAuto for automatic implied minimal size.
// https://drafts.csswg.org/css-flexbox-1/#min-size-auto // https://drafts.csswg.org/css-flexbox-1/#min-size-auto
Direction::Inline => { Direction::Inline => {
let basis = from_flex_basis(self.style.get_position().flex_basis, let basis = from_flex_basis(block.fragment.style.get_position().flex_basis,
self.style.content_inline_size(), block.fragment.style.content_inline_size(),
Some(containing_length)); Some(containing_length));
// These methods compute auto margins to zero length, which is exactly what we want. // These methods compute auto margins to zero length, which is exactly what we want.
@ -172,33 +168,37 @@ impl FlexItem {
block.fragment.compute_inline_direction_margins(containing_length); block.fragment.compute_inline_direction_margins(containing_length);
block.fragment.compute_block_direction_margins(containing_length); block.fragment.compute_block_direction_margins(containing_length);
let (border_padding, margin) = block.fragment.surrounding_intrinsic_inline_size();
let content_size = block.base.intrinsic_inline_sizes.preferred_inline_size let content_size = block.base.intrinsic_inline_sizes.preferred_inline_size
- block.fragment.surrounding_intrinsic_inline_size() - border_padding
- margin
+ block.fragment.box_sizing_boundary(direction); + block.fragment.box_sizing_boundary(direction);
self.base_size = basis.specified_or_default(content_size); self.base_size = basis.specified_or_default(content_size);
self.max_size = specified_or_none(self.style.max_inline_size(), containing_length) self.max_size = specified_or_none(block.fragment.style.max_inline_size(),
.unwrap_or(MAX_AU); containing_length).unwrap_or(MAX_AU);
self.min_size = specified(self.style.min_inline_size(), containing_length); self.min_size = specified(block.fragment.style.min_inline_size(),
containing_length);
} }
Direction::Block => { Direction::Block => {
let basis = from_flex_basis(self.style.get_position().flex_basis, let basis = from_flex_basis(block.fragment.style.get_position().flex_basis,
self.style.content_block_size(), block.fragment.style.content_block_size(),
Some(containing_length)); Some(containing_length));
let content_size = block.fragment.border_box.size.block let content_size = block.fragment.border_box.size.block
- block.fragment.border_padding.block_start_end() - block.fragment.border_padding.block_start_end()
+ block.fragment.box_sizing_boundary(direction); + block.fragment.box_sizing_boundary(direction);
self.base_size = basis.specified_or_default(content_size); self.base_size = basis.specified_or_default(content_size);
self.max_size = specified_or_none(self.style.max_block_size(), containing_length) self.max_size = specified_or_none(block.fragment.style.max_block_size(),
.unwrap_or(MAX_AU); containing_length).unwrap_or(MAX_AU);
self.min_size = specified(self.style.min_block_size(), containing_length); self.min_size = specified(block.fragment.style.min_block_size(),
containing_length);
} }
} }
} }
/// Returns the outer main size of the item, including paddings and margins, /// Returns the outer main size of the item, including paddings and margins,
/// clamped by max and min size. /// clamped by max and min size.
pub fn outer_main_size(&self, direction: Direction) -> Au { pub fn outer_main_size(&self, flow: &Flow, direction: Direction) -> Au {
let ref fragment = self.flow.as_block().fragment; let ref fragment = flow.as_block().fragment;
let outer_width = match direction { let outer_width = match direction {
Direction::Inline => { Direction::Inline => {
fragment.border_padding.inline_start_end() + fragment.margin.inline_start_end() fragment.border_padding.inline_start_end() + fragment.margin.inline_start_end()
@ -212,8 +212,8 @@ impl FlexItem {
} }
/// Returns the number of auto margins in given direction. /// Returns the number of auto margins in given direction.
pub fn auto_margin_count(&self, direction: Direction) -> i32 { pub fn auto_margin_count(&self, flow: &Flow, direction: Direction) -> i32 {
let margin = self.style.logical_margin(); let margin = flow.as_block().fragment.style.logical_margin();
let mut margin_count = 0; let mut margin_count = 0;
match direction { match direction {
Direction::Inline => { Direction::Inline => {
@ -382,7 +382,7 @@ impl FlexFlow {
} }
FlexFlow { FlexFlow {
block_flow: BlockFlow::from_fragment(fragment, flotation), block_flow: BlockFlow::from_fragment_and_float_kind(fragment, flotation),
main_mode: main_mode, main_mode: main_mode,
available_main_size: AxisSize::Infinite, available_main_size: AxisSize::Infinite,
available_cross_size: AxisSize::Infinite, available_cross_size: AxisSize::Infinite,
@ -411,13 +411,15 @@ impl FlexFlow {
let mut margin_count = 0; let mut margin_count = 0;
let items = &mut self.items[start..]; let items = &mut self.items[start..];
let mut children = self.block_flow.base.children.random_access_mut();
for mut item in items { for mut item in items {
item.init_sizes(container_size, self.main_mode); let kid = children.get(item.index);
let outer_main_size = item.outer_main_size(self.main_mode); item.init_sizes(kid, container_size, self.main_mode);
let outer_main_size = item.outer_main_size(kid, self.main_mode);
if total_line_size + outer_main_size > container_size && end != start && self.is_wrappable { if total_line_size + outer_main_size > container_size && end != start && self.is_wrappable {
break; break;
} }
margin_count += item.auto_margin_count(self.main_mode); margin_count += item.auto_margin_count(kid, self.main_mode);
total_line_size += outer_main_size; total_line_size += outer_main_size;
end += 1; end += 1;
} }
@ -437,8 +439,8 @@ impl FlexFlow {
let mut computation = self.block_flow.fragment.compute_intrinsic_inline_sizes(); let mut computation = self.block_flow.fragment.compute_intrinsic_inline_sizes();
if !fixed_width { if !fixed_width {
for kid in &mut self.items { for kid in self.block_flow.base.children.iter_mut() {
let base = flow::mut_base(flow_ref::deref_mut(&mut kid.flow)); let base = flow::mut_base(kid);
let is_absolutely_positioned = base.flags.contains(IS_ABSOLUTELY_POSITIONED); let is_absolutely_positioned = base.flags.contains(IS_ABSOLUTELY_POSITIONED);
if !is_absolutely_positioned { if !is_absolutely_positioned {
let flex_item_inline_sizes = IntrinsicISizes { let flex_item_inline_sizes = IntrinsicISizes {
@ -463,8 +465,8 @@ impl FlexFlow {
let mut computation = self.block_flow.fragment.compute_intrinsic_inline_sizes(); let mut computation = self.block_flow.fragment.compute_intrinsic_inline_sizes();
if !fixed_width { if !fixed_width {
for kid in &mut self.items { for kid in self.block_flow.base.children.iter_mut() {
let base = flow::mut_base(flow_ref::deref_mut(&mut kid.flow)); let base = flow::mut_base(kid);
let is_absolutely_positioned = base.flags.contains(IS_ABSOLUTELY_POSITIONED); let is_absolutely_positioned = base.flags.contains(IS_ABSOLUTELY_POSITIONED);
if !is_absolutely_positioned { if !is_absolutely_positioned {
computation.content_intrinsic_sizes.minimum_inline_size = computation.content_intrinsic_sizes.minimum_inline_size =
@ -503,13 +505,14 @@ impl FlexFlow {
AxisSize::MinMax(ref constraint) => constraint.clamp(content_inline_size), AxisSize::MinMax(ref constraint) => constraint.clamp(content_inline_size),
AxisSize::Infinite => content_inline_size AxisSize::Infinite => content_inline_size
}; };
let mut children = self.block_flow.base.children.random_access_mut();
for kid in &mut self.items { for kid in &mut self.items {
{ let kid_base = flow::mut_base(children.get(kid.index));
let kid_base = flow::mut_base(flow_ref::deref_mut(&mut kid.flow));
kid_base.block_container_explicit_block_size = container_block_size; kid_base.block_container_explicit_block_size = container_block_size;
if kid_base.flags.contains(INLINE_POSITION_IS_STATIC) { if kid_base.flags.contains(INLINE_POSITION_IS_STATIC) {
// The inline-start margin edge of the child flow is at our inline-start content edge, // The inline-start margin edge of the child flow is at our inline-start content
// and its inline-size is our content inline-size. // edge, and its inline-size is our content inline-size.
kid_base.position.start.i = kid_base.position.start.i =
if kid_base.writing_mode.is_bidi_ltr() == containing_block_mode.is_bidi_ltr() { if kid_base.writing_mode.is_bidi_ltr() == containing_block_mode.is_bidi_ltr() {
inline_start_content_edge inline_start_content_edge
@ -523,7 +526,6 @@ impl FlexFlow {
kid_base.position.start.i = inline_start_content_edge; kid_base.position.start.i = inline_start_content_edge;
} }
} }
}
fn inline_mode_assign_inline_sizes(&mut self, fn inline_mode_assign_inline_sizes(&mut self,
_shared_context: &SharedStyleContext, _shared_context: &SharedStyleContext,
@ -599,8 +601,9 @@ impl FlexFlow {
_ => {} _ => {}
} }
let mut children = self.block_flow.base.children.random_access_mut();
for item in items.iter_mut() { for item in items.iter_mut() {
let mut block = flow_ref::deref_mut(&mut item.flow).as_mut_block(); let mut block = children.get(item.index).as_mut_block();
block.base.block_container_writing_mode = container_mode; block.base.block_container_writing_mode = container_mode;
block.base.block_container_inline_size = inline_size; block.base.block_container_inline_size = inline_size;
@ -645,14 +648,16 @@ impl FlexFlow {
} }
// TODO(zentner): This function should actually flex elements! // TODO(zentner): This function should actually flex elements!
fn block_mode_assign_block_size<'a>(&mut self, layout_context: &'a LayoutContext<'a>) { fn block_mode_assign_block_size(&mut self) {
let mut cur_b = if !self.main_reverse { let mut cur_b = if !self.main_reverse {
self.block_flow.fragment.border_padding.block_start self.block_flow.fragment.border_padding.block_start
} else { } else {
self.block_flow.fragment.border_box.size.block self.block_flow.fragment.border_box.size.block
}; };
for kid in &mut self.items {
let base = flow::mut_base(flow_ref::deref_mut(&mut kid.flow)); let mut children = self.block_flow.base.children.random_access_mut();
for item in &mut self.items {
let mut base = flow::mut_base(children.get(item.index));
if !self.main_reverse { if !self.main_reverse {
base.position.start.b = cur_b; base.position.start.b = cur_b;
cur_b = cur_b + base.position.size.block; cur_b = cur_b + base.position.size.block;
@ -661,7 +666,6 @@ impl FlexFlow {
base.position.start.b = cur_b; base.position.start.b = cur_b;
} }
} }
self.block_flow.assign_block_size(layout_context)
} }
fn inline_mode_assign_block_size<'a>(&mut self, layout_context: &'a LayoutContext<'a>) { fn inline_mode_assign_block_size<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
@ -673,14 +677,18 @@ impl FlexFlow {
let mut total_cross_size = Au(0); let mut total_cross_size = Au(0);
let mut line_interval = Au(0); let mut line_interval = Au(0);
{
let mut children = self.block_flow.base.children.random_access_mut();
for line in self.lines.iter_mut() { for line in self.lines.iter_mut() {
for item in &self.items[line.range.clone()] { for item in &self.items[line.range.clone()] {
let ref fragment = item.flow.as_block().fragment; let fragment = &children.get(item.index).as_block().fragment;
line.cross_size = max(line.cross_size, line.cross_size = max(line.cross_size,
fragment.border_box.size.block + fragment.margin.block_start_end()); fragment.border_box.size.block +
fragment.margin.block_start_end());
} }
total_cross_size += line.cross_size; total_cross_size += line.cross_size;
} }
}
let box_border = self.block_flow.fragment.box_sizing_boundary(Direction::Block); let box_border = self.block_flow.fragment.box_sizing_boundary(Direction::Block);
let parent_container_size = let parent_container_size =
@ -726,10 +734,11 @@ impl FlexFlow {
} }
} }
let mut children = self.block_flow.base.children.random_access_mut();
for line in &self.lines { for line in &self.lines {
for mut item in self.items[line.range.clone()].iter_mut() { for item in self.items[line.range.clone()].iter_mut() {
let auto_margin_count = item.auto_margin_count(Direction::Block); let block = children.get(item.index).as_mut_block();
let mut block = flow_ref::deref_mut(&mut item.flow).as_mut_block(); let auto_margin_count = item.auto_margin_count(block, Direction::Block);
let margin = block.fragment.style().logical_margin(); let margin = block.fragment.style().logical_margin();
let mut margin_block_start = block.fragment.margin.block_start; let mut margin_block_start = block.fragment.margin.block_start;
@ -821,10 +830,17 @@ impl Flow for FlexFlow {
// Flexbox Section 9.1: Re-order flex items according to their order. // Flexbox Section 9.1: Re-order flex items according to their order.
// FIXME(stshine): This should be done during flow construction. // FIXME(stshine): This should be done during flow construction.
let mut items = self.block_flow.base.children.iter_flow_ref_mut() let mut items: Vec<FlexItem> =
.filter(|flow| !flow.as_block().base.flags.contains(IS_ABSOLUTELY_POSITIONED)) self.block_flow
.map(|flow| FlexItem::new(flow.clone())) .base
.collect::<Vec<FlexItem>>(); .children
.iter()
.enumerate()
.filter(|&(_, flow)| {
!flow.as_block().base.flags.contains(IS_ABSOLUTELY_POSITIONED)
})
.map(|(index, flow)| FlexItem::new(index, flow))
.collect();
items.sort_by_key(|item| item.order); items.sort_by_key(|item| item.order);
self.items = items; self.items = items;
@ -910,12 +926,13 @@ impl Flow for FlexFlow {
} }
fn assign_block_size<'a>(&mut self, layout_context: &'a LayoutContext<'a>) { fn assign_block_size<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
self.block_flow.assign_block_size(layout_context); self.block_flow
.assign_block_size_block_base(layout_context,
None,
MarginsMayCollapseFlag::MarginsMayNotCollapse);
match self.main_mode { match self.main_mode {
Direction::Inline => Direction::Inline => self.inline_mode_assign_block_size(layout_context),
self.inline_mode_assign_block_size(layout_context), Direction::Block => self.block_mode_assign_block_size(),
Direction::Block =>
self.block_mode_assign_block_size(layout_context)
} }
} }

View file

@ -33,7 +33,7 @@ use euclid::{Point2D, Size2D};
use floats::{Floats, SpeculatedFloatPlacement}; use floats::{Floats, SpeculatedFloatPlacement};
use flow_list::{FlowList, MutFlowListIterator}; use flow_list::{FlowList, MutFlowListIterator};
use flow_ref::{self, FlowRef, WeakFlowRef}; use flow_ref::{self, FlowRef, WeakFlowRef};
use fragment::{Fragment, FragmentBorderBoxIterator, Overflow, SpecificFragmentInfo}; use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
use gfx::display_list::{ClippingRegion, StackingContext}; use gfx::display_list::{ClippingRegion, StackingContext};
use gfx_traits::StackingContextId; use gfx_traits::StackingContextId;
use gfx_traits::print_tree::PrintTree; use gfx_traits::print_tree::PrintTree;
@ -44,17 +44,16 @@ use parallel::FlowParallelInfo;
use rustc_serialize::{Encodable, Encoder}; use rustc_serialize::{Encodable, Encoder};
use script_layout_interface::restyle_damage::{RECONSTRUCT_FLOW, REFLOW, REFLOW_OUT_OF_FLOW}; use script_layout_interface::restyle_damage::{RECONSTRUCT_FLOW, REFLOW, REFLOW_OUT_OF_FLOW};
use script_layout_interface::restyle_damage::{REPAINT, REPOSITION, RestyleDamage}; use script_layout_interface::restyle_damage::{REPAINT, REPOSITION, RestyleDamage};
use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutNode};
use std::{fmt, mem, raw}; use std::{fmt, mem, raw};
use std::iter::Zip; use std::iter::Zip;
use std::slice::IterMut; use std::slice::IterMut;
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use style::computed_values::{clear, display, empty_cells, float, overflow_x, position, text_align}; use style::computed_values::{clear, float, overflow_x, position, text_align};
use style::context::SharedStyleContext; use style::context::SharedStyleContext;
use style::dom::TRestyleDamage; use style::dom::TRestyleDamage;
use style::logical_geometry::{LogicalRect, LogicalSize, WritingMode}; use style::logical_geometry::{LogicalRect, LogicalSize, WritingMode};
use style::properties::{self, ServoComputedValues}; use style::properties::ServoComputedValues;
use style::values::computed::LengthOrPercentageOrAuto; use style::values::computed::LengthOrPercentageOrAuto;
use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, TableFlow}; use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, TableFlow};
use table_caption::TableCaptionFlow; use table_caption::TableCaptionFlow;
@ -470,12 +469,6 @@ pub trait ImmutableFlowUtils {
/// Returns true if this flow is one of table-related flows. /// Returns true if this flow is one of table-related flows.
fn is_table_kind(self) -> bool; fn is_table_kind(self) -> bool;
/// Returns true if anonymous flow is needed between this flow and child flow.
fn need_anonymous_flow(self, child: &Flow) -> bool;
/// Generates missing child flow of this flow.
fn generate_missing_child_flow<N: ThreadSafeLayoutNode>(self, node: &N, ctx: &LayoutContext) -> FlowRef;
/// Returns true if this flow contains fragments that are roots of an absolute flow tree. /// Returns true if this flow contains fragments that are roots of an absolute flow tree.
fn contains_roots_of_absolute_flow_tree(&self) -> bool; fn contains_roots_of_absolute_flow_tree(&self) -> bool;
@ -665,6 +658,9 @@ bitflags! {
/// Whether this flow contains any text and/or replaced fragments. /// Whether this flow contains any text and/or replaced fragments.
const CONTAINS_TEXT_OR_REPLACED_FRAGMENTS = 0b0001_0000_0000_0000_0000_0000, const CONTAINS_TEXT_OR_REPLACED_FRAGMENTS = 0b0001_0000_0000_0000_0000_0000,
/// Whether margins are prohibited from collapsing with this flow.
const MARGINS_CANNOT_COLLAPSE = 0b0010_0000_0000_0000_0000_0000,
} }
} }
@ -963,7 +959,8 @@ impl fmt::Debug for BaseFlow {
}; };
write!(f, write!(f,
"sc={:?} pos={:?}, {}{} floatspec-in={:?}, floatspec-out={:?}, overflow={:?}{}{}{}", "sc={:?} pos={:?}, {}{} floatspec-in={:?}, floatspec-out={:?}, \
overflow={:?}{}{}{}",
self.stacking_context_id, self.stacking_context_id,
self.position, self.position,
if self.flags.contains(FLOATS_LEFT) { "FL" } else { "" }, if self.flags.contains(FLOATS_LEFT) { "FL" } else { "" },
@ -1233,74 +1230,6 @@ impl<'a> ImmutableFlowUtils for &'a Flow {
} }
} }
/// Returns true if anonymous flow is needed between this flow and child flow.
/// Spec: http://www.w3.org/TR/CSS21/tables.html#anonymous-boxes
fn need_anonymous_flow(self, child: &Flow) -> bool {
match self.class() {
FlowClass::Table => !child.is_proper_table_child(),
FlowClass::TableRowGroup => !child.is_table_row(),
FlowClass::TableRow => !child.is_table_cell(),
// FIXME(zentner): According to spec, anonymous flex items are only needed for text.
FlowClass::Flex => child.is_inline_flow(),
_ => false
}
}
/// Generates missing child flow of this flow.
///
/// FIXME(pcwalton): This duplicates some logic in
/// `generate_anonymous_table_flows_if_necessary()`. We should remove this function eventually,
/// as it's harder to understand.
fn generate_missing_child_flow<N: ThreadSafeLayoutNode>(self, node: &N, ctx: &LayoutContext) -> FlowRef {
let style_context = ctx.style_context();
let mut style = node.style(style_context);
match self.class() {
FlowClass::Table | FlowClass::TableRowGroup => {
properties::modify_style_for_anonymous_table_object(
&mut style,
display::T::table_row);
let fragment = Fragment::from_opaque_node_and_style(
node.opaque(),
PseudoElementType::Normal,
style,
node.selected_style(style_context),
node.restyle_damage(),
SpecificFragmentInfo::TableRow);
Arc::new(TableRowFlow::from_fragment(fragment))
},
FlowClass::TableRow => {
properties::modify_style_for_anonymous_table_object(
&mut style,
display::T::table_cell);
let fragment = Fragment::from_opaque_node_and_style(
node.opaque(),
PseudoElementType::Normal,
style,
node.selected_style(style_context),
node.restyle_damage(),
SpecificFragmentInfo::TableCell);
let hide = node.style(style_context).get_inheritedtable().empty_cells == empty_cells::T::hide;
Arc::new(TableCellFlow::from_node_fragment_and_visibility_flag(node, fragment, !hide))
},
FlowClass::Flex => {
properties::modify_style_for_anonymous_flow(
&mut style,
display::T::block);
let fragment =
Fragment::from_opaque_node_and_style(node.opaque(),
PseudoElementType::Normal,
style,
node.selected_style(style_context),
node.restyle_damage(),
SpecificFragmentInfo::Generic);
Arc::new(BlockFlow::from_fragment(fragment, None))
},
_ => {
panic!("no need to generate a missing child")
}
}
}
/// Returns true if this flow contains fragments that are roots of an absolute flow tree. /// Returns true if this flow contains fragments that are roots of an absolute flow tree.
fn contains_roots_of_absolute_flow_tree(&self) -> bool { fn contains_roots_of_absolute_flow_tree(&self) -> bool {
self.contains_relatively_positioned_fragments() || self.is_root() self.contains_relatively_positioned_fragments() || self.is_root()

View file

@ -6,9 +6,15 @@ use flow::Flow;
use flow_ref::{self, FlowRef}; use flow_ref::{self, FlowRef};
use std::collections::{LinkedList, linked_list}; use std::collections::{LinkedList, linked_list};
// This needs to be reworked now that we have dynamically-sized types in Rust. /// This needs to be reworked now that we have dynamically-sized types in Rust.
// Until then, it's just a wrapper around LinkedList. /// Until then, it's just a wrapper around LinkedList.
///
/// SECURITY-NOTE(pcwalton): It is very important that `FlowRef` values not leak directly to
/// layout. Layout code must only interact with `&Flow` or `&mut Flow` values. Otherwise, layout
/// could stash `FlowRef` values in random places unknown to the system and thereby cause data
/// races. Those data races can lead to memory safety problems, potentially including arbitrary
/// remote code execution! In general, do not add new methods to this file (e.g. new ways of
/// iterating over flows) unless you are *very* sure of what you are doing.
pub struct FlowList { pub struct FlowList {
flows: LinkedList<FlowRef>, flows: LinkedList<FlowRef>,
} }
@ -52,13 +58,19 @@ impl FlowList {
} }
} }
/// Provide a forward iterator /// Provide a forward iterator.
///
/// SECURITY-NOTE(pcwalton): This does not hand out `FlowRef`s by design. Do not add a method
/// to do so! See the comment above in `FlowList`.
#[inline] #[inline]
pub fn iter<'a>(&'a self) -> impl DoubleEndedIterator<Item = &'a Flow> { pub fn iter<'a>(&'a self) -> impl DoubleEndedIterator<Item = &'a Flow> {
self.flows.iter().map(|flow| &**flow) self.flows.iter().map(|flow| &**flow)
} }
/// Provide a forward iterator with mutable references /// Provide a forward iterator with mutable references
///
/// SECURITY-NOTE(pcwalton): This does not hand out `FlowRef`s by design. Do not add a method
/// to do so! See the comment above in `FlowList`.
#[inline] #[inline]
pub fn iter_mut(&mut self) -> MutFlowListIterator { pub fn iter_mut(&mut self) -> MutFlowListIterator {
MutFlowListIterator { MutFlowListIterator {
@ -66,11 +78,18 @@ impl FlowList {
} }
} }
/// Provide a forward iterator with FlowRef items /// Provides a caching random-access iterator that yields mutable references. This is
/// guaranteed to perform no more than O(n) pointer chases.
///
/// SECURITY-NOTE(pcwalton): This does not hand out `FlowRef`s by design. Do not add a method
/// to do so! See the comment above in `FlowList`.
#[inline] #[inline]
pub fn iter_flow_ref_mut<'a>(&'a mut self) pub fn random_access_mut(&mut self) -> FlowListRandomAccessMut {
-> impl DoubleEndedIterator<Item = &'a mut FlowRef> { let length = self.flows.len();
self.flows.iter_mut() FlowListRandomAccessMut {
iterator: self.flows.iter_mut(),
cache: Vec::with_capacity(length),
}
} }
/// O(1) /// O(1)
@ -111,3 +130,22 @@ impl<'a> Iterator for MutFlowListIterator<'a> {
self.it.size_hint() self.it.size_hint()
} }
} }
/// A caching random-access iterator that yields mutable references. This is guaranteed to perform
/// no more than O(n) pointer chases.
pub struct FlowListRandomAccessMut<'a> {
iterator: linked_list::IterMut<'a, FlowRef>,
cache: Vec<FlowRef>,
}
impl<'a> FlowListRandomAccessMut<'a> {
pub fn get<'b>(&'b mut self, index: usize) -> &'b mut Flow {
while index >= self.cache.len() {
match self.iterator.next() {
None => panic!("Flow index out of range!"),
Some(next_flow) => self.cache.push((*next_flow).clone()),
}
}
flow_ref::deref_mut(&mut self.cache[index])
}
}

View file

@ -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)
@ -1051,7 +1075,12 @@ impl Fragment {
SpecificFragmentInfo::Svg(_) => { SpecificFragmentInfo::Svg(_) => {
QuantitiesIncludedInIntrinsicInlineSizes::all() QuantitiesIncludedInIntrinsicInlineSizes::all()
} }
SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell => { SpecificFragmentInfo::Table => {
INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED |
INTRINSIC_INLINE_SIZE_INCLUDES_PADDING |
INTRINSIC_INLINE_SIZE_INCLUDES_BORDER
}
SpecificFragmentInfo::TableCell => {
let base_quantities = INTRINSIC_INLINE_SIZE_INCLUDES_PADDING | let base_quantities = INTRINSIC_INLINE_SIZE_INCLUDES_PADDING |
INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED; INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED;
if self.style.get_inheritedtable().border_collapse == if self.style.get_inheritedtable().border_collapse ==
@ -1091,11 +1120,11 @@ impl Fragment {
} }
} }
/// Returns the portion of the intrinsic inline-size that consists of borders, padding, and/or /// Returns the portion of the intrinsic inline-size that consists of borders/padding and
/// margins. /// margins, respectively.
/// ///
/// FIXME(#2261, pcwalton): This won't work well for inlines: is this OK? /// FIXME(#2261, pcwalton): This won't work well for inlines: is this OK?
pub fn surrounding_intrinsic_inline_size(&self) -> Au { pub fn surrounding_intrinsic_inline_size(&self) -> (Au, Au) {
let flags = self.quantities_included_in_intrinsic_inline_size(); let flags = self.quantities_included_in_intrinsic_inline_size();
let style = self.style(); let style = self.style();
@ -1127,16 +1156,19 @@ impl Fragment {
Au(0) Au(0)
}; };
margin + padding + border (border + padding, margin)
} }
/// Uses the style only to estimate the intrinsic inline-sizes. These may be modified for text /// Uses the style only to estimate the intrinsic inline-sizes. These may be modified for text
/// or replaced elements. /// or replaced elements.
fn style_specified_intrinsic_inline_size(&self) -> IntrinsicISizesContribution { pub fn style_specified_intrinsic_inline_size(&self) -> IntrinsicISizesContribution {
let flags = self.quantities_included_in_intrinsic_inline_size(); let flags = self.quantities_included_in_intrinsic_inline_size();
let style = self.style(); let style = self.style();
let mut specified = Au(0);
// FIXME(#2261, pcwalton): This won't work well for inlines: is this OK?
let (border_padding, margin) = self.surrounding_intrinsic_inline_size();
let mut specified = Au(0);
if flags.contains(INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED) { if flags.contains(INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED) {
specified = MaybeAuto::from_style(style.content_inline_size(), specified = MaybeAuto::from_style(style.content_inline_size(),
Au(0)).specified_or_zero(); Au(0)).specified_or_zero();
@ -1144,17 +1176,18 @@ impl Fragment {
if let Some(max) = model::specified_or_none(style.max_inline_size(), Au(0)) { if let Some(max) = model::specified_or_none(style.max_inline_size(), Au(0)) {
specified = min(specified, max) specified = min(specified, max)
} }
}
// FIXME(#2261, pcwalton): This won't work well for inlines: is this OK? if self.style.get_position().box_sizing == box_sizing::T::border_box {
let surrounding_inline_size = self.surrounding_intrinsic_inline_size(); specified -= border_padding
}
}
IntrinsicISizesContribution { IntrinsicISizesContribution {
content_intrinsic_sizes: IntrinsicISizes { content_intrinsic_sizes: IntrinsicISizes {
minimum_inline_size: specified, minimum_inline_size: specified,
preferred_inline_size: specified, preferred_inline_size: specified,
}, },
surrounding_size: surrounding_inline_size, surrounding_size: border_padding + margin,
} }
} }

View file

@ -42,7 +42,7 @@ impl ListItemFlow {
flotation: Option<FloatKind>) flotation: Option<FloatKind>)
-> ListItemFlow { -> ListItemFlow {
let mut this = ListItemFlow { let mut this = ListItemFlow {
block_flow: BlockFlow::from_fragment(main_fragment, flotation), block_flow: BlockFlow::from_fragment_and_float_kind(main_fragment, flotation),
marker_fragments: marker_fragments, marker_fragments: marker_fragments,
}; };

View file

@ -41,7 +41,7 @@ pub struct MulticolColumnFlow {
impl MulticolFlow { impl MulticolFlow {
pub fn from_fragment(fragment: Fragment, float_kind: Option<FloatKind>) -> MulticolFlow { pub fn from_fragment(fragment: Fragment, float_kind: Option<FloatKind>) -> MulticolFlow {
MulticolFlow { MulticolFlow {
block_flow: BlockFlow::from_fragment(fragment, float_kind), block_flow: BlockFlow::from_fragment_and_float_kind(fragment, float_kind),
column_pitch: Au(0), column_pitch: Au(0),
} }
} }
@ -50,7 +50,7 @@ impl MulticolFlow {
impl MulticolColumnFlow { impl MulticolColumnFlow {
pub fn from_fragment(fragment: Fragment) -> MulticolColumnFlow { pub fn from_fragment(fragment: Fragment) -> MulticolColumnFlow {
MulticolColumnFlow { MulticolColumnFlow {
block_flow: BlockFlow::from_fragment(fragment, None), block_flow: BlockFlow::from_fragment(fragment),
} }
} }
} }

View file

@ -63,7 +63,7 @@ pub struct TableFlow {
impl TableFlow { impl TableFlow {
pub fn from_fragment(fragment: Fragment) -> TableFlow { pub fn from_fragment(fragment: Fragment) -> TableFlow {
let mut block_flow = BlockFlow::from_fragment(fragment, None); let mut block_flow = BlockFlow::from_fragment(fragment);
let table_layout = let table_layout =
if block_flow.fragment().style().get_table().table_layout == table_layout::T::fixed { if block_flow.fragment().style().get_table().table_layout == table_layout::T::fixed {
TableLayout::Fixed TableLayout::Fixed
@ -85,9 +85,13 @@ impl TableFlow {
/// sizes. /// sizes.
fn update_automatic_column_inline_sizes( fn update_automatic_column_inline_sizes(
parent_inline_sizes: &mut Vec<ColumnIntrinsicInlineSize>, parent_inline_sizes: &mut Vec<ColumnIntrinsicInlineSize>,
child_cell_inline_sizes: &[CellIntrinsicInlineSize]) child_cell_inline_sizes: &[CellIntrinsicInlineSize],
surrounding_size: Au)
-> IntrinsicISizes { -> IntrinsicISizes {
let mut total_inline_sizes = IntrinsicISizes::new(); let mut total_inline_sizes = IntrinsicISizes {
minimum_inline_size: surrounding_size,
preferred_inline_size: surrounding_size,
};
let mut column_index = 0; let mut column_index = 0;
for child_cell_inline_size in child_cell_inline_sizes { for child_cell_inline_size in child_cell_inline_sizes {
for _ in 0..child_cell_inline_size.column_span { for _ in 0..child_cell_inline_size.column_span {
@ -120,10 +124,9 @@ impl TableFlow {
} }
} }
total_inline_sizes.minimum_inline_size = total_inline_sizes.minimum_inline_size + total_inline_sizes.minimum_inline_size +=
parent_inline_sizes[column_index].minimum_length; parent_inline_sizes[column_index].minimum_length;
total_inline_sizes.preferred_inline_size = total_inline_sizes.preferred_inline_size +=
total_inline_sizes.preferred_inline_size +
parent_inline_sizes[column_index].preferred; parent_inline_sizes[column_index].preferred;
column_index += 1 column_index += 1
@ -139,7 +142,8 @@ impl TableFlow {
column_inline_sizes: &mut Vec<ColumnIntrinsicInlineSize>, column_inline_sizes: &mut Vec<ColumnIntrinsicInlineSize>,
computation: &mut IntrinsicISizesContribution, computation: &mut IntrinsicISizesContribution,
first_row: bool, first_row: bool,
table_layout: TableLayout) { table_layout: TableLayout,
surrounding_inline_size: Au) {
// Read column inline-sizes from the table-row, and assign inline-size=0 for the columns // Read column inline-sizes from the table-row, and assign inline-size=0 for the columns
// not defined in the column group. // not defined in the column group.
// //
@ -158,7 +162,8 @@ impl TableFlow {
TableLayout::Auto => { TableLayout::Auto => {
computation.union_block(&TableFlow::update_automatic_column_inline_sizes( computation.union_block(&TableFlow::update_automatic_column_inline_sizes(
column_inline_sizes, column_inline_sizes,
&row.cell_intrinsic_inline_sizes)) &row.cell_intrinsic_inline_sizes,
surrounding_inline_size))
} }
} }
} }
@ -227,9 +232,6 @@ impl Flow for TableFlow {
let _scope = layout_debug_scope!("table::bubble_inline_sizes {:x}", let _scope = layout_debug_scope!("table::bubble_inline_sizes {:x}",
self.block_flow.base.debug_id()); self.block_flow.base.debug_id());
// Don't use `compute_intrinsic_inline_sizes` here because that will count padding as
// part of the table, which we don't want to do—it belongs to the table wrapper instead.
// Get column inline sizes from colgroups // Get column inline sizes from colgroups
for kid in self.block_flow.base.child_iter_mut().filter(|kid| kid.is_table_colgroup()) { for kid in self.block_flow.base.child_iter_mut().filter(|kid| kid.is_table_colgroup()) {
for specified_inline_size in &kid.as_mut_table_colgroup().inline_sizes { for specified_inline_size in &kid.as_mut_table_colgroup().inline_sizes {
@ -277,15 +279,18 @@ impl Flow for TableFlow {
&*self.block_flow.fragment.style, &*self.block_flow.fragment.style,
CollapsedBorderProvenance::FromTable)); CollapsedBorderProvenance::FromTable));
let mut first_row = true; let mut first_row = true;
let (border_padding, _) = self.block_flow.fragment.surrounding_intrinsic_inline_size();
{ {
let mut iterator = TableRowIterator::new(&mut self.block_flow.base).peekable(); let mut iterator = TableRowIterator::new(&mut self.block_flow.base).peekable();
while let Some(row) = iterator.next() { while let Some(row) = iterator.next() {
TableFlow::update_column_inline_sizes_for_row(row, TableFlow::update_column_inline_sizes_for_row(
row,
&mut self.column_intrinsic_inline_sizes, &mut self.column_intrinsic_inline_sizes,
&mut computation, &mut computation,
first_row, first_row,
self.table_layout); self.table_layout,
border_padding);
if collapsing_borders { if collapsing_borders {
let next_index_and_sibling = iterator.peek(); let next_index_and_sibling = iterator.peek();
let next_collapsed_borders_in_block_direction = let next_collapsed_borders_in_block_direction =
@ -316,9 +321,16 @@ impl Flow for TableFlow {
}; };
} }
let total_horizontal_spacing = self.total_horizontal_spacing();
computation.surrounding_size = computation.surrounding_size + let mut style_specified_intrinsic_inline_size =
self.total_horizontal_spacing(); self.block_flow
.fragment
.style_specified_intrinsic_inline_size()
.finish();
style_specified_intrinsic_inline_size.minimum_inline_size -= total_horizontal_spacing;
style_specified_intrinsic_inline_size.preferred_inline_size -= total_horizontal_spacing;
computation.union_block(&style_specified_intrinsic_inline_size);
computation.surrounding_size += total_horizontal_spacing;
self.block_flow.base.intrinsic_inline_sizes = computation.finish() self.block_flow.base.intrinsic_inline_sizes = computation.finish()
} }
@ -359,15 +371,16 @@ impl Flow for TableFlow {
let inline_end_content_edge = self.block_flow.fragment.border_padding.inline_end; let inline_end_content_edge = self.block_flow.fragment.border_padding.inline_end;
let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end(); let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();
let spacing_per_cell = self.spacing(); let spacing_per_cell = self.spacing();
let spacing = self.total_horizontal_spacing(); let total_horizontal_spacing = self.total_horizontal_spacing();
let content_inline_size = let content_inline_size = self.block_flow.fragment.border_box.size.inline -
self.block_flow.fragment.border_box.size.inline - padding_and_borders - spacing; padding_and_borders - total_horizontal_spacing;
match self.table_layout { match self.table_layout {
TableLayout::Fixed => { TableLayout::Fixed => {
// In fixed table layout, we distribute extra space among the unspecified columns // In fixed table layout, we distribute extra space among the unspecified columns
// if there are any, or among all the columns if all are specified. // if there are any, or among all the columns if all are specified.
// See: https://drafts.csswg.org/css-tables-3/#distributing-width-to-columns (infobox) // See: https://drafts.csswg.org/css-tables-3/#distributing-width-to-columns
// (infobox)
self.column_computed_inline_sizes.clear(); self.column_computed_inline_sizes.clear();
if num_unspecified_inline_sizes != 0 { if num_unspecified_inline_sizes != 0 {
let extra_column_inline_size = content_inline_size - total_column_inline_size; let extra_column_inline_size = content_inline_size - total_column_inline_size;

View file

@ -29,7 +29,7 @@ pub struct TableCaptionFlow {
impl TableCaptionFlow { impl TableCaptionFlow {
pub fn from_fragment(fragment: Fragment) -> TableCaptionFlow { pub fn from_fragment(fragment: Fragment) -> TableCaptionFlow {
TableCaptionFlow { TableCaptionFlow {
block_flow: BlockFlow::from_fragment(fragment, None) block_flow: BlockFlow::from_fragment(fragment),
} }
} }
} }

View file

@ -46,10 +46,19 @@ pub struct TableCellFlow {
} }
impl TableCellFlow { impl TableCellFlow {
pub fn from_fragment(fragment: Fragment) -> TableCellFlow {
TableCellFlow {
block_flow: BlockFlow::from_fragment(fragment),
collapsed_borders: CollapsedBordersForCell::new(),
column_span: 1,
visible: true,
}
}
pub fn from_node_fragment_and_visibility_flag<N: ThreadSafeLayoutNode>( pub fn from_node_fragment_and_visibility_flag<N: ThreadSafeLayoutNode>(
node: &N, fragment: Fragment, visible: bool) -> TableCellFlow { node: &N, fragment: Fragment, visible: bool) -> TableCellFlow {
TableCellFlow { TableCellFlow {
block_flow: BlockFlow::from_fragment(fragment, None), block_flow: BlockFlow::from_fragment(fragment),
collapsed_borders: CollapsedBordersForCell::new(), collapsed_borders: CollapsedBordersForCell::new(),
column_span: node.get_colspan(), column_span: node.get_colspan(),
visible: visible, visible: visible,

View file

@ -84,7 +84,7 @@ impl TableRowFlow {
pub fn from_fragment(fragment: Fragment) -> TableRowFlow { pub fn from_fragment(fragment: Fragment) -> TableRowFlow {
let writing_mode = fragment.style().writing_mode; let writing_mode = fragment.style().writing_mode;
TableRowFlow { TableRowFlow {
block_flow: BlockFlow::from_fragment(fragment, None), block_flow: BlockFlow::from_fragment(fragment),
cell_intrinsic_inline_sizes: Vec::new(), cell_intrinsic_inline_sizes: Vec::new(),
column_computed_inline_sizes: Vec::new(), column_computed_inline_sizes: Vec::new(),
spacing: border_spacing::T { spacing: border_spacing::T {

View file

@ -64,7 +64,7 @@ impl TableRowGroupFlow {
pub fn from_fragment(fragment: Fragment) -> TableRowGroupFlow { pub fn from_fragment(fragment: Fragment) -> TableRowGroupFlow {
let writing_mode = fragment.style().writing_mode; let writing_mode = fragment.style().writing_mode;
TableRowGroupFlow { TableRowGroupFlow {
block_flow: BlockFlow::from_fragment(fragment, None), block_flow: BlockFlow::from_fragment(fragment),
column_intrinsic_inline_sizes: Vec::new(), column_intrinsic_inline_sizes: Vec::new(),
column_computed_inline_sizes: Vec::new(), column_computed_inline_sizes: Vec::new(),
spacing: border_spacing::T { spacing: border_spacing::T {

View file

@ -29,7 +29,7 @@ use std::cmp::{max, min};
use std::fmt; use std::fmt;
use std::ops::Add; use std::ops::Add;
use std::sync::Arc; use std::sync::Arc;
use style::computed_values::{border_collapse, table_layout}; use style::computed_values::{border_collapse, position, table_layout};
use style::context::SharedStyleContext; use style::context::SharedStyleContext;
use style::logical_geometry::{LogicalRect, LogicalSize}; use style::logical_geometry::{LogicalRect, LogicalSize};
use style::properties::ServoComputedValues; use style::properties::ServoComputedValues;
@ -57,8 +57,13 @@ pub struct TableWrapperFlow {
} }
impl TableWrapperFlow { impl TableWrapperFlow {
pub fn from_fragment(fragment: Fragment, float_kind: Option<FloatKind>) -> TableWrapperFlow { pub fn from_fragment(fragment: Fragment) -> TableWrapperFlow {
let mut block_flow = BlockFlow::from_fragment(fragment, float_kind); TableWrapperFlow::from_fragment_and_float_kind(fragment, None)
}
pub fn from_fragment_and_float_kind(fragment: Fragment, float_kind: Option<FloatKind>)
-> TableWrapperFlow {
let mut block_flow = BlockFlow::from_fragment_and_float_kind(fragment, float_kind);
let table_layout = if block_flow.fragment().style().get_table().table_layout == let table_layout = if block_flow.fragment().style().get_table().table_layout ==
table_layout::T::fixed { table_layout::T::fixed {
TableLayout::Fixed TableLayout::Fixed
@ -71,6 +76,7 @@ impl TableWrapperFlow {
table_layout: table_layout table_layout: table_layout
} }
} }
fn border_padding_and_spacing(&mut self) -> (Au, Au) { fn border_padding_and_spacing(&mut self) -> (Au, Au) {
let (mut table_border_padding, mut spacing) = (Au(0), Au(0)); let (mut table_border_padding, mut spacing) = (Au(0), Au(0));
for kid in self.block_flow.base.child_iter_mut() { for kid in self.block_flow.base.child_iter_mut() {
@ -85,17 +91,15 @@ impl TableWrapperFlow {
(table_border_padding, spacing) (table_border_padding, spacing)
} }
/// Calculates table column sizes for automatic layout per INTRINSIC § 4.3. // Instructs our first child, which is the table itself, to compute its border and padding.
fn calculate_table_column_sizes_for_automatic_layout(
&mut self,
intermediate_column_inline_sizes: &mut [IntermediateColumnInlineSize]) {
// Find the padding and border of our first child, which is the table itself.
// //
// This is a little weird because we're computing border/padding/margins for our child, // This is a little weird because we're computing border/padding/margins for our child,
// when normally the child computes it itself. But it has to be this way because the // when normally the child computes it itself. But it has to be this way because the
// padding will affect where we place the child. This is an odd artifact of the way that // padding will affect where we place the child. This is an odd artifact of the way that
// tables are separated into table flows and table wrapper flows. // tables are separated into table flows and table wrapper flows.
let available_inline_size = self.block_flow.fragment.border_box.size.inline; fn compute_border_and_padding_of_table(&mut self) {
let available_inline_size = self.block_flow.base.block_container_inline_size;
let border_collapse = self.block_flow.fragment.style.get_inheritedtable().border_collapse;
for kid in self.block_flow.base.child_iter_mut() { for kid in self.block_flow.base.child_iter_mut() {
if !kid.is_table() { if !kid.is_table() {
continue continue
@ -103,38 +107,19 @@ impl TableWrapperFlow {
let kid_table = kid.as_mut_table(); let kid_table = kid.as_mut_table();
let kid_block_flow = &mut kid_table.block_flow; let kid_block_flow = &mut kid_table.block_flow;
kid_block_flow.fragment kid_block_flow.fragment.compute_border_and_padding(available_inline_size,
.compute_border_and_padding(available_inline_size, border_collapse);
self.block_flow
.fragment
.style
.get_inheritedtable()
.border_collapse);
kid_block_flow.fragment.compute_block_direction_margins(available_inline_size); kid_block_flow.fragment.compute_block_direction_margins(available_inline_size);
kid_block_flow.fragment.compute_inline_direction_margins(available_inline_size); kid_block_flow.fragment.compute_inline_direction_margins(available_inline_size);
break return
}
} }
let (table_border_padding, spacing) = self.border_padding_and_spacing(); /// Calculates table column sizes for automatic layout per INTRINSIC § 4.3.
fn calculate_table_column_sizes_for_automatic_layout(
// FIXME(pcwalton, spec): INTRINSIC § 8 does not properly define how to compute this, but &mut self,
// says "the basic idea is the same as the shrink-to-fit width that CSS2.1 defines". So we intermediate_column_inline_sizes: &mut [IntermediateColumnInlineSize]) {
// just use the shrink-to-fit inline size. let available_inline_size = self.available_inline_size();
let mut available_inline_size =
match self.block_flow.fragment.style().content_inline_size() {
LengthOrPercentageOrAuto::Auto => {
self.block_flow.get_shrink_to_fit_inline_size(available_inline_size)
}
// FIXME(mttr): This fixes #4421 without breaking our current reftests, but I'm
// not completely sure this is "correct".
//
// That said, `available_inline_size` is, as far as I can tell, equal to the
// table's computed width property (W) and is used from this point forward in a way
// that seems to correspond with CSS 2.1 § 17.5.2.2 under "Column and caption
// widths influence the final table width as follows: …"
_ => available_inline_size,
};
available_inline_size = available_inline_size - spacing;
// Compute all the guesses for the column sizes, and sum them. // Compute all the guesses for the column sizes, and sum them.
let mut total_guess = AutoLayoutCandidateGuess::new(); let mut total_guess = AutoLayoutCandidateGuess::new();
@ -182,8 +167,35 @@ impl TableWrapperFlow {
total_used_inline_size = available_inline_size total_used_inline_size = available_inline_size
} }
self.set_inline_size(total_used_inline_size)
}
fn available_inline_size(&mut self) -> Au {
let available_inline_size = self.block_flow.fragment.border_box.size.inline;
let (table_border_padding, spacing) = self.border_padding_and_spacing();
// FIXME(pcwalton, spec): INTRINSIC § 8 does not properly define how to compute this, but
// says "the basic idea is the same as the shrink-to-fit width that CSS2.1 defines". So we
// just use the shrink-to-fit inline size.
let available_inline_size = match self.block_flow.fragment.style().content_inline_size() {
LengthOrPercentageOrAuto::Auto => {
self.block_flow.get_shrink_to_fit_inline_size(available_inline_size) -
table_border_padding
}
// FIXME(mttr): This fixes #4421 without breaking our current reftests, but I'm not
// completely sure this is "correct".
//
// That said, `available_inline_size` is, as far as I can tell, equal to the table's
// computed width property (W) and is used from this point forward in a way that seems
// to correspond with CSS 2.1 § 17.5.2.2 under "Column and caption widths influence the
// final table width as follows: …"
_ => available_inline_size,
};
available_inline_size - spacing
}
fn set_inline_size(&mut self, total_used_inline_size: Au) {
let (table_border_padding, spacing) = self.border_padding_and_spacing();
self.block_flow.fragment.border_box.size.inline = total_used_inline_size + self.block_flow.fragment.border_box.size.inline = total_used_inline_size +
table_border_padding + spacing; table_border_padding + spacing;
self.block_flow.base.position.size.inline = total_used_inline_size + self.block_flow.base.position.size.inline = total_used_inline_size +
@ -229,6 +241,7 @@ impl TableWrapperFlow {
minimum_width_of_all_columns: minimum_width_of_all_columns, minimum_width_of_all_columns: minimum_width_of_all_columns,
preferred_width_of_all_columns: preferred_width_of_all_columns, preferred_width_of_all_columns: preferred_width_of_all_columns,
border_collapse: border_collapse, border_collapse: border_collapse,
table_border_padding: border_padding,
}; };
let input = let input =
inline_size_computer.compute_inline_size_constraint_inputs(&mut self.block_flow, inline_size_computer.compute_inline_size_constraint_inputs(&mut self.block_flow,
@ -249,6 +262,7 @@ impl TableWrapperFlow {
minimum_width_of_all_columns: minimum_width_of_all_columns, minimum_width_of_all_columns: minimum_width_of_all_columns,
preferred_width_of_all_columns: preferred_width_of_all_columns, preferred_width_of_all_columns: preferred_width_of_all_columns,
border_collapse: border_collapse, border_collapse: border_collapse,
table_border_padding: border_padding,
}; };
let input = let input =
inline_size_computer.compute_inline_size_constraint_inputs(&mut self.block_flow, inline_size_computer.compute_inline_size_constraint_inputs(&mut self.block_flow,
@ -268,6 +282,7 @@ impl TableWrapperFlow {
minimum_width_of_all_columns: minimum_width_of_all_columns, minimum_width_of_all_columns: minimum_width_of_all_columns,
preferred_width_of_all_columns: preferred_width_of_all_columns, preferred_width_of_all_columns: preferred_width_of_all_columns,
border_collapse: border_collapse, border_collapse: border_collapse,
table_border_padding: border_padding,
}; };
let input = let input =
inline_size_computer.compute_inline_size_constraint_inputs(&mut self.block_flow, inline_size_computer.compute_inline_size_constraint_inputs(&mut self.block_flow,
@ -344,14 +359,21 @@ impl Flow for TableWrapperFlow {
containing_block_inline_size; containing_block_inline_size;
} }
// This has to be done before computing our inline size because `compute_used_inline_size`
// internally consults the border and padding of the table.
self.compute_border_and_padding_of_table();
self.compute_used_inline_size(shared_context, self.compute_used_inline_size(shared_context,
containing_block_inline_size, containing_block_inline_size,
&intermediate_column_inline_sizes); &intermediate_column_inline_sizes);
if let TableLayout::Auto = self.table_layout { match self.table_layout {
TableLayout::Auto => {
self.calculate_table_column_sizes_for_automatic_layout( self.calculate_table_column_sizes_for_automatic_layout(
&mut intermediate_column_inline_sizes) &mut intermediate_column_inline_sizes)
} }
TableLayout::Fixed => {}
}
let inline_start_content_edge = self.block_flow.fragment.border_box.start.i; let inline_start_content_edge = self.block_flow.fragment.border_box.start.i;
let content_inline_size = self.block_flow.fragment.border_box.size.inline; let content_inline_size = self.block_flow.fragment.border_box.size.inline;
@ -472,6 +494,10 @@ impl Flow for TableWrapperFlow {
fn print_extra_flow_children(&self, print_tree: &mut PrintTree) { fn print_extra_flow_children(&self, print_tree: &mut PrintTree) {
self.block_flow.print_extra_flow_children(print_tree); self.block_flow.print_extra_flow_children(print_tree);
} }
fn positioning(&self) -> position::T {
self.block_flow.positioning()
}
} }
impl fmt::Debug for TableWrapperFlow { impl fmt::Debug for TableWrapperFlow {
@ -734,10 +760,15 @@ struct IntermediateColumnInlineSize {
percentage: f32, percentage: f32,
} }
/// Returns the computed inline size of the table wrapper represented by `block`.
///
/// `table_border_padding` is the sum of the sizes of all border and padding in the inline
/// direction of the table contained within this table wrapper.
fn initial_computed_inline_size(block: &mut BlockFlow, fn initial_computed_inline_size(block: &mut BlockFlow,
containing_block_inline_size: Au, containing_block_inline_size: Au,
minimum_width_of_all_columns: Au, minimum_width_of_all_columns: Au,
preferred_width_of_all_columns: Au) preferred_width_of_all_columns: Au,
table_border_padding: Au)
-> MaybeAuto { -> MaybeAuto {
let inline_size_from_style = MaybeAuto::from_style(block.fragment.style.content_inline_size(), let inline_size_from_style = MaybeAuto::from_style(block.fragment.style.content_inline_size(),
containing_block_inline_size); containing_block_inline_size);
@ -746,7 +777,8 @@ fn initial_computed_inline_size(block: &mut BlockFlow,
MaybeAuto::Specified(min(containing_block_inline_size, preferred_width_of_all_columns)) MaybeAuto::Specified(min(containing_block_inline_size, preferred_width_of_all_columns))
} }
MaybeAuto::Specified(inline_size_from_style) => { MaybeAuto::Specified(inline_size_from_style) => {
MaybeAuto::Specified(max(inline_size_from_style, minimum_width_of_all_columns)) MaybeAuto::Specified(max(inline_size_from_style - table_border_padding,
minimum_width_of_all_columns))
} }
} }
} }
@ -755,6 +787,7 @@ struct Table {
minimum_width_of_all_columns: Au, minimum_width_of_all_columns: Au,
preferred_width_of_all_columns: Au, preferred_width_of_all_columns: Au,
border_collapse: border_collapse::T, border_collapse: border_collapse::T,
table_border_padding: Au,
} }
impl ISizeAndMarginsComputer for Table { impl ISizeAndMarginsComputer for Table {
@ -769,13 +802,12 @@ impl ISizeAndMarginsComputer for Table {
shared_context: &SharedStyleContext) shared_context: &SharedStyleContext)
-> MaybeAuto { -> MaybeAuto {
let containing_block_inline_size = let containing_block_inline_size =
self.containing_block_inline_size(block, self.containing_block_inline_size(block, parent_flow_inline_size, shared_context);
parent_flow_inline_size,
shared_context);
initial_computed_inline_size(block, initial_computed_inline_size(block,
containing_block_inline_size, containing_block_inline_size,
self.minimum_width_of_all_columns, self.minimum_width_of_all_columns,
self.preferred_width_of_all_columns) self.preferred_width_of_all_columns,
self.table_border_padding)
} }
fn solve_inline_size_constraints(&self, fn solve_inline_size_constraints(&self,
@ -790,6 +822,7 @@ struct FloatedTable {
minimum_width_of_all_columns: Au, minimum_width_of_all_columns: Au,
preferred_width_of_all_columns: Au, preferred_width_of_all_columns: Au,
border_collapse: border_collapse::T, border_collapse: border_collapse::T,
table_border_padding: Au,
} }
impl ISizeAndMarginsComputer for FloatedTable { impl ISizeAndMarginsComputer for FloatedTable {
@ -810,7 +843,8 @@ impl ISizeAndMarginsComputer for FloatedTable {
initial_computed_inline_size(block, initial_computed_inline_size(block,
containing_block_inline_size, containing_block_inline_size,
self.minimum_width_of_all_columns, self.minimum_width_of_all_columns,
self.preferred_width_of_all_columns) self.preferred_width_of_all_columns,
self.table_border_padding)
} }
fn solve_inline_size_constraints(&self, fn solve_inline_size_constraints(&self,
@ -825,6 +859,7 @@ struct AbsoluteTable {
minimum_width_of_all_columns: Au, minimum_width_of_all_columns: Au,
preferred_width_of_all_columns: Au, preferred_width_of_all_columns: Au,
border_collapse: border_collapse::T, border_collapse: border_collapse::T,
table_border_padding: Au,
} }
impl ISizeAndMarginsComputer for AbsoluteTable { impl ISizeAndMarginsComputer for AbsoluteTable {
@ -845,7 +880,8 @@ impl ISizeAndMarginsComputer for AbsoluteTable {
initial_computed_inline_size(block, initial_computed_inline_size(block,
containing_block_inline_size, containing_block_inline_size,
self.minimum_width_of_all_columns, self.minimum_width_of_all_columns,
self.preferred_width_of_all_columns) self.preferred_width_of_all_columns,
self.table_border_padding)
} }
fn containing_block_inline_size(&self, fn containing_block_inline_size(&self,
@ -853,7 +889,9 @@ impl ISizeAndMarginsComputer for AbsoluteTable {
parent_flow_inline_size: Au, parent_flow_inline_size: Au,
shared_context: &SharedStyleContext) shared_context: &SharedStyleContext)
-> Au { -> Au {
AbsoluteNonReplaced.containing_block_inline_size(block, parent_flow_inline_size, shared_context) AbsoluteNonReplaced.containing_block_inline_size(block,
parent_flow_inline_size,
shared_context)
} }
fn solve_inline_size_constraints(&self, fn solve_inline_size_constraints(&self,

View file

@ -265,9 +265,10 @@ pub trait ThreadSafeLayoutNode: Clone + Copy + NodeInfo + PartialEq + Sized {
.current_styles().pseudos.contains_key(&style_pseudo) { .current_styles().pseudos.contains_key(&style_pseudo) {
let mut data = self.get_style_data().unwrap().borrow_mut(); let mut data = self.get_style_data().unwrap().borrow_mut();
let new_style = let new_style =
context.stylist context.stylist.precomputed_values_for_pseudo(
.precomputed_values_for_pseudo(&style_pseudo, &style_pseudo,
Some(&data.current_styles().primary)); Some(&data.current_styles().primary),
false);
data.current_pseudos_mut() data.current_pseudos_mut()
.insert(style_pseudo.clone(), new_style.unwrap()); .insert(style_pseudo.clone(), new_style.unwrap());
} }

View file

@ -9,7 +9,7 @@ use context::SharedStyleContext;
use dom::{OpaqueNode, UnsafeNode}; use dom::{OpaqueNode, UnsafeNode};
use euclid::point::Point2D; use euclid::point::Point2D;
use keyframes::{KeyframesStep, KeyframesStepValue}; use keyframes::{KeyframesStep, KeyframesStepValue};
use properties::{self, ComputedValues, Importance}; use properties::{self, CascadeFlags, ComputedValues, Importance};
use properties::animated_properties::{AnimatedProperty, TransitionProperty}; use properties::animated_properties::{AnimatedProperty, TransitionProperty};
use properties::longhands::animation_direction::computed_value::AnimationDirection; use properties::longhands::animation_direction::computed_value::AnimationDirection;
use properties::longhands::animation_iteration_count::computed_value::AnimationIterationCount; use properties::longhands::animation_iteration_count::computed_value::AnimationIterationCount;
@ -397,11 +397,11 @@ fn compute_style_for_animation_step(context: &SharedStyleContext,
}; };
let (computed, _) = properties::cascade(context.viewport_size, let (computed, _) = properties::cascade(context.viewport_size,
&[declaration_block], &[declaration_block],
false,
Some(previous_style), Some(previous_style),
None, None,
None, None,
context.error_reporter.clone()); context.error_reporter.clone(),
CascadeFlags::empty());
computed computed
} }
} }

View file

@ -13,7 +13,7 @@ use cascade_info::CascadeInfo;
use context::{SharedStyleContext, StyleContext}; use context::{SharedStyleContext, StyleContext};
use data::{NodeStyles, PseudoStyles}; use data::{NodeStyles, PseudoStyles};
use dom::{NodeInfo, TElement, TNode, TRestyleDamage, UnsafeNode}; use dom::{NodeInfo, TElement, TNode, TRestyleDamage, UnsafeNode};
use properties::{ComputedValues, cascade}; use properties::{CascadeFlags, ComputedValues, SHAREABLE, cascade};
use properties::longhands::display::computed_value as display; use properties::longhands::display::computed_value as display;
use selector_impl::{PseudoElement, TheSelectorImpl}; use selector_impl::{PseudoElement, TheSelectorImpl};
use selector_matching::{ApplicableDeclarationBlock, Stylist}; use selector_matching::{ApplicableDeclarationBlock, Stylist};
@ -489,6 +489,8 @@ pub enum StyleSharingResult<ConcreteRestyleDamage: TRestyleDamage> {
// Callers need to pass several boolean flags to cascade_node_pseudo_element. // Callers need to pass several boolean flags to cascade_node_pseudo_element.
// We encapsulate them in this struct to avoid mixing them up. // We encapsulate them in this struct to avoid mixing them up.
//
// FIXME(pcwalton): Unify with `CascadeFlags`, perhaps?
struct CascadeBooleans { struct CascadeBooleans {
shareable: bool, shareable: bool,
cacheable: bool, cacheable: bool,
@ -523,6 +525,11 @@ trait PrivateMatchMethods: TNode {
cacheable = cacheable && !has_style_attribute; cacheable = cacheable && !has_style_attribute;
let mut cascade_info = CascadeInfo::new(); let mut cascade_info = CascadeInfo::new();
let mut cascade_flags = CascadeFlags::empty();
if booleans.shareable {
cascade_flags.insert(SHAREABLE)
}
let (this_style, is_cacheable) = match parent_style { let (this_style, is_cacheable) = match parent_style {
Some(ref parent_style) => { Some(ref parent_style) => {
let cache_entry = applicable_declarations_cache.find(applicable_declarations); let cache_entry = applicable_declarations_cache.find(applicable_declarations);
@ -533,20 +540,20 @@ trait PrivateMatchMethods: TNode {
cascade(shared_context.viewport_size, cascade(shared_context.viewport_size,
applicable_declarations, applicable_declarations,
booleans.shareable,
Some(&***parent_style), Some(&***parent_style),
cached_computed_values, cached_computed_values,
Some(&mut cascade_info), Some(&mut cascade_info),
shared_context.error_reporter.clone()) shared_context.error_reporter.clone(),
cascade_flags)
} }
None => { None => {
cascade(shared_context.viewport_size, cascade(shared_context.viewport_size,
applicable_declarations, applicable_declarations,
booleans.shareable,
None, None,
None, None,
Some(&mut cascade_info), Some(&mut cascade_info),
shared_context.error_reporter.clone()) shared_context.error_reporter.clone(),
cascade_flags)
} }
}; };
cascade_info.finish(self); cascade_info.finish(self);

View file

@ -1524,6 +1524,17 @@ static CASCADE_PROPERTY: [CascadePropertyFn; ${len(data.longhands)}] = [
% endfor % endfor
]; ];
bitflags! {
pub flags CascadeFlags: u8 {
/// Whether the `ComputedValues` structure to be constructed should be considered
/// shareable.
const SHAREABLE = 0x01,
/// Whether to inherit all styles from the parent. If this flag is not present,
/// non-inherited styles are reset to their initial values.
const INHERIT_ALL = 0x02,
}
}
/// Performs the CSS cascade, computing new styles for an element from its parent style and /// Performs the CSS cascade, computing new styles for an element from its parent style and
/// optionally a cached related style. The arguments are: /// optionally a cached related style. The arguments are:
/// ///
@ -1531,9 +1542,6 @@ static CASCADE_PROPERTY: [CascadePropertyFn; ${len(data.longhands)}] = [
/// ///
/// * `applicable_declarations`: The list of CSS rules that matched. /// * `applicable_declarations`: The list of CSS rules that matched.
/// ///
/// * `shareable`: Whether the `ComputedValues` structure to be constructed should be considered
/// shareable.
///
/// * `parent_style`: The parent style, if applicable; if `None`, this is the root node. /// * `parent_style`: The parent style, if applicable; if `None`, this is the root node.
/// ///
/// * `cached_style`: If present, cascading is short-circuited for everything but inherited /// * `cached_style`: If present, cascading is short-circuited for everything but inherited
@ -1541,14 +1549,16 @@ static CASCADE_PROPERTY: [CascadePropertyFn; ${len(data.longhands)}] = [
/// this that it is safe to only provide inherited declarations. If `parent_style` is `None`, /// this that it is safe to only provide inherited declarations. If `parent_style` is `None`,
/// this is ignored. /// this is ignored.
/// ///
/// * `flags`: Various flags.
///
/// Returns the computed values and a boolean indicating whether the result is cacheable. /// Returns the computed values and a boolean indicating whether the result is cacheable.
pub fn cascade(viewport_size: Size2D<Au>, pub fn cascade(viewport_size: Size2D<Au>,
applicable_declarations: &[ApplicableDeclarationBlock], applicable_declarations: &[ApplicableDeclarationBlock],
shareable: bool,
parent_style: Option<<&ComputedValues>, parent_style: Option<<&ComputedValues>,
cached_style: Option<<&ComputedValues>, cached_style: Option<<&ComputedValues>,
mut cascade_info: Option<<&mut CascadeInfo>, mut cascade_info: Option<<&mut CascadeInfo>,
mut error_reporter: StdBox<ParseErrorReporter + Send>) mut error_reporter: StdBox<ParseErrorReporter + Send>,
flags: CascadeFlags)
-> (ComputedValues, bool) { -> (ComputedValues, bool) {
let initial_values = ComputedValues::initial_values(); let initial_values = ComputedValues::initial_values();
let (is_root_element, inherited_style) = match parent_style { let (is_root_element, inherited_style) = match parent_style {
@ -1582,7 +1592,7 @@ pub fn cascade(viewport_size: Size2D<Au>,
if let (Some(cached_style), Some(parent_style)) = (cached_style, parent_style) { if let (Some(cached_style), Some(parent_style)) = (cached_style, parent_style) {
let style = cascade_with_cached_declarations(viewport_size, let style = cascade_with_cached_declarations(viewport_size,
&applicable_declarations, &applicable_declarations,
shareable, flags.contains(SHAREABLE),
parent_style, parent_style,
cached_style, cached_style,
custom_properties, custom_properties,
@ -1591,24 +1601,35 @@ pub fn cascade(viewport_size: Size2D<Au>,
return (style, false) return (style, false)
} }
let mut context = computed::Context { let starting_style = if !flags.contains(INHERIT_ALL) {
is_root_element: is_root_element, ComputedValues::new(custom_properties,
viewport_size: viewport_size, flags.contains(SHAREABLE),
inherited_style: inherited_style,
style: ComputedValues::new(
custom_properties,
shareable,
WritingMode::empty(), WritingMode::empty(),
inherited_style.root_font_size(), inherited_style.root_font_size(),
% for style_struct in data.active_style_structs(): % for style_struct in data.active_style_structs():
% if style_struct.inherited: % if style_struct.inherited:
inherited_style inherited_style.clone_${style_struct.name_lower}(),
% else: % else:
initial_values initial_values.clone_${style_struct.name_lower}(),
% endif % endif
.clone_${style_struct.name_lower}(),
% endfor % endfor
), )
} else {
ComputedValues::new(custom_properties,
flags.contains(SHAREABLE),
WritingMode::empty(),
inherited_style.root_font_size(),
% for style_struct in data.active_style_structs():
inherited_style.clone_${style_struct.name_lower}(),
% endfor
)
};
let mut context = computed::Context {
is_root_element: is_root_element,
viewport_size: viewport_size,
inherited_style: inherited_style,
style: starting_style,
}; };
// Set computed values, overwriting earlier declarations for the same property. // Set computed values, overwriting earlier declarations for the same property.
@ -1906,18 +1927,6 @@ pub fn modify_border_style_for_inline_sides(style: &mut Arc<ComputedValues>,
} }
} }
/// Adjusts the display and position properties as appropriate for an anonymous table object.
#[cfg(feature = "servo")]
#[inline]
pub fn modify_style_for_anonymous_table_object(
style: &mut Arc<ComputedValues>,
new_display_value: longhands::display::computed_value::T) {
let mut style = Arc::make_mut(style);
let box_style = Arc::make_mut(&mut style.box_);
box_style.display = new_display_value;
box_style.position = longhands::position::computed_value::T::static_;
}
/// Adjusts the `position` property as necessary for the outer fragment wrapper of an inline-block. /// Adjusts the `position` property as necessary for the outer fragment wrapper of an inline-block.
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
#[inline] #[inline]

View file

@ -10,7 +10,8 @@ use error_reporting::StdoutErrorReporter;
use keyframes::KeyframesAnimation; use keyframes::KeyframesAnimation;
use media_queries::{Device, MediaType}; use media_queries::{Device, MediaType};
use parking_lot::{RwLock, RwLockReadGuard}; use parking_lot::{RwLock, RwLockReadGuard};
use properties::{self, PropertyDeclaration, PropertyDeclarationBlock, ComputedValues, Importance}; use properties::{self, CascadeFlags, ComputedValues, INHERIT_ALL, Importance};
use properties::{PropertyDeclaration, PropertyDeclarationBlock};
use quickersort::sort_by; use quickersort::sort_by;
use restyle_hints::{RestyleHint, DependencySet}; use restyle_hints::{RestyleHint, DependencySet};
use selector_impl::{ElementExt, TheSelectorImpl, PseudoElement}; use selector_impl::{ElementExt, TheSelectorImpl, PseudoElement};
@ -252,25 +253,63 @@ impl Stylist {
/// Computes the style for a given "precomputed" pseudo-element, taking the /// Computes the style for a given "precomputed" pseudo-element, taking the
/// universal rules and applying them. /// universal rules and applying them.
///
/// If `inherit_all` is true, then all properties are inherited from the parent; otherwise,
/// non-inherited properties are reset to their initial values. The flow constructor uses this
/// flag when constructing anonymous flows.
pub fn precomputed_values_for_pseudo(&self, pub fn precomputed_values_for_pseudo(&self,
pseudo: &PseudoElement, pseudo: &PseudoElement,
parent: Option<&Arc<ComputedValues>>) parent: Option<&Arc<ComputedValues>>,
inherit_all: bool)
-> Option<Arc<ComputedValues>> { -> Option<Arc<ComputedValues>> {
debug_assert!(TheSelectorImpl::pseudo_element_cascade_type(pseudo).is_precomputed()); debug_assert!(TheSelectorImpl::pseudo_element_cascade_type(pseudo).is_precomputed());
if let Some(declarations) = self.precomputed_pseudo_element_decls.get(pseudo) { if let Some(declarations) = self.precomputed_pseudo_element_decls.get(pseudo) {
let mut flags = CascadeFlags::empty();
if inherit_all {
flags.insert(INHERIT_ALL)
}
let (computed, _) = let (computed, _) =
properties::cascade(self.device.au_viewport_size(), properties::cascade(self.device.au_viewport_size(),
&declarations, false, &declarations,
parent.map(|p| &**p), parent.map(|p| &**p),
None, None,
None, None,
Box::new(StdoutErrorReporter)); Box::new(StdoutErrorReporter),
flags);
Some(Arc::new(computed)) Some(Arc::new(computed))
} else { } else {
parent.map(|p| p.clone()) parent.map(|p| p.clone())
} }
} }
/// Returns the style for an anonymous box of the given type.
#[cfg(feature = "servo")]
pub fn style_for_anonymous_box(&self,
pseudo: &PseudoElement,
parent_style: &Arc<ComputedValues>)
-> Arc<ComputedValues> {
// For most (but not all) pseudo-elements, we inherit all values from the parent.
let inherit_all = match *pseudo {
PseudoElement::ServoInputText => false,
PseudoElement::ServoAnonymousBlock |
PseudoElement::ServoAnonymousTable |
PseudoElement::ServoAnonymousTableCell |
PseudoElement::ServoAnonymousTableRow |
PseudoElement::ServoAnonymousTableWrapper |
PseudoElement::ServoTableWrapper => true,
PseudoElement::Before |
PseudoElement::After |
PseudoElement::Selection |
PseudoElement::DetailsSummary |
PseudoElement::DetailsContent => {
unreachable!("That pseudo doesn't represent an anonymous box!")
}
};
self.precomputed_values_for_pseudo(&pseudo, Some(parent_style), inherit_all)
.expect("style_for_anonymous_box(): No precomputed values for that pseudo!")
}
pub fn lazily_compute_pseudo_element_style<E>(&self, pub fn lazily_compute_pseudo_element_style<E>(&self,
element: &E, element: &E,
pseudo: &PseudoElement, pseudo: &PseudoElement,
@ -298,10 +337,12 @@ impl Stylist {
let (computed, _) = let (computed, _) =
properties::cascade(self.device.au_viewport_size(), properties::cascade(self.device.au_viewport_size(),
&declarations, false, &declarations,
Some(&**parent), None, None, Some(&**parent),
Box::new(StdoutErrorReporter)); None,
None,
Box::new(StdoutErrorReporter),
CascadeFlags::empty());
Some(Arc::new(computed)) Some(Arc::new(computed))
} }

View file

@ -13,6 +13,7 @@ use selectors::parser::{AttrSelector, ParserContext, SelectorImpl};
use std::fmt; use std::fmt;
use string_cache::{Atom, Namespace}; use string_cache::{Atom, Namespace};
/// NB: If you add to this list, be sure to update `each_pseudo_element` too.
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum PseudoElement { pub enum PseudoElement {
@ -22,6 +23,12 @@ pub enum PseudoElement {
DetailsSummary, DetailsSummary,
DetailsContent, DetailsContent,
ServoInputText, ServoInputText,
ServoTableWrapper,
ServoAnonymousTableWrapper,
ServoAnonymousTable,
ServoAnonymousTableRow,
ServoAnonymousTableCell,
ServoAnonymousBlock,
} }
impl ToCss for PseudoElement { impl ToCss for PseudoElement {
@ -34,6 +41,12 @@ impl ToCss for PseudoElement {
DetailsSummary => "::-servo-details-summary", DetailsSummary => "::-servo-details-summary",
DetailsContent => "::-servo-details-content", DetailsContent => "::-servo-details-content",
ServoInputText => "::-servo-input-text", ServoInputText => "::-servo-input-text",
ServoTableWrapper => "::-servo-table-wrapper",
ServoAnonymousTableWrapper => "::-servo-anonymous-table-wrapper",
ServoAnonymousTable => "::-servo-anonymous-table",
ServoAnonymousTableRow => "::-servo-anonymous-table-row",
ServoAnonymousTableCell => "::-servo-anonymous-table-cell",
ServoAnonymousBlock => "::-servo-anonymous-block",
}) })
} }
} }
@ -57,7 +70,13 @@ impl PseudoElement {
PseudoElement::Selection => PseudoElementCascadeType::Eager, PseudoElement::Selection => PseudoElementCascadeType::Eager,
PseudoElement::DetailsSummary => PseudoElementCascadeType::Lazy, PseudoElement::DetailsSummary => PseudoElementCascadeType::Lazy,
PseudoElement::DetailsContent | PseudoElement::DetailsContent |
PseudoElement::ServoInputText => PseudoElementCascadeType::Precomputed, PseudoElement::ServoInputText |
PseudoElement::ServoTableWrapper |
PseudoElement::ServoAnonymousTableWrapper |
PseudoElement::ServoAnonymousTable |
PseudoElement::ServoAnonymousTableRow |
PseudoElement::ServoAnonymousTableCell |
PseudoElement::ServoAnonymousBlock => PseudoElementCascadeType::Precomputed,
} }
} }
} }
@ -210,6 +229,42 @@ impl SelectorImpl for ServoSelectorImpl {
} }
ServoInputText ServoInputText
}, },
"-servo-table-wrapper" => {
if !context.in_user_agent_stylesheet {
return Err(())
}
ServoTableWrapper
},
"-servo-anonymous-table-wrapper" => {
if !context.in_user_agent_stylesheet {
return Err(())
}
ServoAnonymousTableWrapper
},
"-servo-anonymous-table" => {
if !context.in_user_agent_stylesheet {
return Err(())
}
ServoAnonymousTable
},
"-servo-anonymous-table-row" => {
if !context.in_user_agent_stylesheet {
return Err(())
}
ServoAnonymousTableRow
},
"-servo-anonymous-table-cell" => {
if !context.in_user_agent_stylesheet {
return Err(())
}
ServoAnonymousTableCell
},
"-servo-anonymous-block" => {
if !context.in_user_agent_stylesheet {
return Err(())
}
ServoAnonymousBlock
},
_ => return Err(()) _ => return Err(())
}; };
@ -232,6 +287,12 @@ impl ServoSelectorImpl {
fun(PseudoElement::DetailsSummary); fun(PseudoElement::DetailsSummary);
fun(PseudoElement::Selection); fun(PseudoElement::Selection);
fun(PseudoElement::ServoInputText); fun(PseudoElement::ServoInputText);
fun(PseudoElement::ServoTableWrapper);
fun(PseudoElement::ServoAnonymousTableWrapper);
fun(PseudoElement::ServoAnonymousTable);
fun(PseudoElement::ServoAnonymousTableRow);
fun(PseudoElement::ServoAnonymousTableCell);
fun(PseudoElement::ServoAnonymousBlock);
} }
#[inline] #[inline]

View file

@ -37,7 +37,7 @@ use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong};
use style::gecko_bindings::sugar::refptr::{GeckoArcPrincipal, GeckoArcURI}; use style::gecko_bindings::sugar::refptr::{GeckoArcPrincipal, GeckoArcURI};
use style::parallel; use style::parallel;
use style::parser::{ParserContext, ParserContextExtraData}; use style::parser::{ParserContext, ParserContextExtraData};
use style::properties::{ComputedValues, Importance, PropertyDeclaration}; use style::properties::{CascadeFlags, ComputedValues, Importance, PropertyDeclaration};
use style::properties::{PropertyDeclarationParseResult, PropertyDeclarationBlock}; use style::properties::{PropertyDeclarationParseResult, PropertyDeclarationBlock};
use style::properties::{cascade, parse_one_declaration}; use style::properties::{cascade, parse_one_declaration};
use style::selector_impl::PseudoElementCascadeType; use style::selector_impl::PseudoElementCascadeType;
@ -143,11 +143,11 @@ pub extern "C" fn Servo_RestyleWithAddedDeclaration(declarations: RawServoDeclar
// FIXME (bug 1303229): Use the actual viewport size here // FIXME (bug 1303229): Use the actual viewport size here
let (computed, _) = cascade(Size2D::new(Au(0), Au(0)), let (computed, _) = cascade(Size2D::new(Au(0), Au(0)),
&[declaration_block], &[declaration_block],
false,
Some(previous_style), Some(previous_style),
None, None,
None, None,
Box::new(StdoutErrorReporter)); Box::new(StdoutErrorReporter),
CascadeFlags::empty());
Arc::new(computed).into_strong() Arc::new(computed).into_strong()
} }
@ -282,7 +282,7 @@ pub extern "C" fn Servo_ComputedValues_GetForAnonymousBox(parent_style_or_null:
let maybe_parent = ComputedValues::arc_from_borrowed(&parent_style_or_null); let maybe_parent = ComputedValues::arc_from_borrowed(&parent_style_or_null);
let new_computed = data.stylist.precomputed_values_for_pseudo(&pseudo, maybe_parent); let new_computed = data.stylist.precomputed_values_for_pseudo(&pseudo, maybe_parent, false);
new_computed.map_or(Strong::null(), |c| c.into_strong()) new_computed.map_or(Strong::null(), |c| c.into_strong())
} }

View file

@ -171,3 +171,47 @@ svg > * {
*|*::-servo-input-text { *|*::-servo-input-text {
margin: 0; margin: 0;
} }
*|*::-servo-table-wrapper {
display: table;
border: none;
}
*|*::-servo-anonymous-table-wrapper {
position: static;
margin: 0;
counter-increment: none;
}
*|*::-servo-anonymous-table {
display: table;
position: static;
border: none;
padding: 0;
counter-increment: none;
}
*|*::-servo-anonymous-table-row {
display: table-row;
position: static;
border: none;
counter-increment: none;
}
*|*::-servo-anonymous-table-cell {
display: table-cell;
position: static;
border: none;
counter-increment: none;
}
*|*::-servo-anonymous-block {
display: block;
position: static;
border: none;
padding: 0;
margin: 0;
width: auto;
height: auto;
}

View file

@ -1,3 +0,0 @@
[flex-flexitem-childmargin.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[flexbox_align-items-stretch-2.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[flexbox_first-line.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[flexbox_generated.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[flexbox_item-bottom-float.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[flexbox_item-top-float.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[flexbox_stf-table-caption.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[multi-line-wrap-reverse-row-reverse.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[multi-line-wrap-with-row-reverse.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[after-content-display-008.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[after-content-display-009.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[after-content-display-010.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[after-content-display-011.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[after-content-display-014.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[background-position-applies-to-001.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[background-position-applies-to-002.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[background-position-applies-to-003.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[background-position-applies-to-004.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[background-position-applies-to-007.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[background-position-applies-to-014.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[before-content-display-008.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[before-content-display-009.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[before-content-display-010.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[before-content-display-011.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[before-content-display-014.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[bidi-009b.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[block-formatting-contexts-001.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[blocks-017.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[border-applies-to-013.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[border-color-applies-to-013.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[border-top-applies-to-013.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[border-top-color-applies-to-013.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[border-top-width-applies-to-013.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[bottom-applies-to-002.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[bottom-applies-to-003.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[bottom-applies-to-004.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[c548-ln-ht-001.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +1,2 @@
[color-applies-to-014.htm] [color-applies-to-014.htm]
type: reftest disabled: https://github.com/servo/servo/pull/13870#issuecomment-255507790
expected: FAIL

View file

@ -1,3 +0,0 @@
[floats-014.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[font-applies-to-007.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[font-style-applies-to-007.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[font-variant-applies-to-007.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[font-weight-applies-to-007.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[height-width-table-001.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[height-width-table-001a.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[height-width-table-001c.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[height-width-table-001d.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[leading-001.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[left-applies-to-002.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[left-applies-to-003.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[left-applies-to-004.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[margin-001.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[margin-002.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[min-width-applies-to-013.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[min-width-applies-to-014.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[min-width-tables-001.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[overflow-applies-to-001.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[overflow-applies-to-002.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[overflow-applies-to-003.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[overflow-applies-to-004.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[overflow-applies-to-005.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[overflow-applies-to-006.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[padding-001.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[padding-002.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[padding-applies-to-001.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[padding-applies-to-002.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[padding-applies-to-003.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[padding-applies-to-004.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[padding-applies-to-005.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[padding-applies-to-006.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[padding-applies-to-013.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[padding-applies-to-013a.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[padding-right-applies-to-013.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[padding-right-applies-to-014.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[position-applies-to-002.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[position-applies-to-003.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[position-applies-to-004.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[right-applies-to-001.htm]
type: reftest
expected: FAIL

Some files were not shown because too many files have changed in this diff Show more