mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
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:
commit
6b40f97289
139 changed files with 932 additions and 896 deletions
|
@ -36,7 +36,7 @@ use floats::{ClearType, FloatKind, Floats, PlacementInfo};
|
|||
use flow::{self, BaseFlow, EarlyAbsolutePositionInfo, Flow, FlowClass, ForceNonfloatedFlag};
|
||||
use flow::{BLOCK_POSITION_IS_STATIC, CLEARS_LEFT, CLEARS_RIGHT};
|
||||
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::IS_ABSOLUTELY_POSITIONED;
|
||||
use flow_list::FlowList;
|
||||
|
@ -533,7 +533,12 @@ impl Encodable for BlockFlowFlags {
|
|||
}
|
||||
|
||||
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;
|
||||
BlockFlow {
|
||||
base: BaseFlow::new(Some(fragment.style()), writing_mode, match float_kind {
|
||||
|
@ -1452,7 +1457,8 @@ impl BlockFlow {
|
|||
display::T::table_caption |
|
||||
display::T::table_row_group |
|
||||
display::T::table |
|
||||
display::T::inline_block => {
|
||||
display::T::inline_block |
|
||||
display::T::flex => {
|
||||
FormattingContextType::Other
|
||||
}
|
||||
_ 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
|
||||
/// computed for this flow.
|
||||
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 {
|
||||
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 -
|
||||
surrounding_inline_size,
|
||||
border_padding - margin,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1906,7 +1912,9 @@ impl Flow for BlockFlow {
|
|||
self.fragment.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW);
|
||||
}
|
||||
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.
|
||||
debug!("assign_block_size: assigning block_size for root flow {:?}",
|
||||
flow::base(self).debug_id());
|
||||
|
|
|
@ -19,8 +19,9 @@ use context::LayoutContext;
|
|||
use data::{HAS_NEWLY_CONSTRUCTED_FLOW, PersistentLayoutData};
|
||||
use flex::FlexFlow;
|
||||
use floats::FloatKind;
|
||||
use flow::{self, AbsoluteDescendants, IS_ABSOLUTELY_POSITIONED, ImmutableFlowUtils};
|
||||
use flow::{CAN_BE_FRAGMENTED, MutableFlowUtils, MutableOwnedFlowUtils};
|
||||
use flow::{self, AbsoluteDescendants, Flow, FlowClass, ImmutableFlowUtils};
|
||||
use flow::{CAN_BE_FRAGMENTED, IS_ABSOLUTELY_POSITIONED, MARGINS_CANNOT_COLLAPSE};
|
||||
use flow::{MutableFlowUtils, MutableOwnedFlowUtils};
|
||||
use flow_ref::{self, FlowRef};
|
||||
use fragment::{CanvasFragmentInfo, ImageFragmentInfo, InlineAbsoluteFragmentInfo, SvgFragmentInfo};
|
||||
use fragment::{Fragment, GeneratedContentInfo, IframeFragmentInfo};
|
||||
|
@ -48,6 +49,7 @@ use style::computed_values::content::ContentItem;
|
|||
use style::computed_values::position;
|
||||
use style::context::SharedStyleContext;
|
||||
use style::properties::{self, ServoComputedValues};
|
||||
use style::selector_matching::Stylist;
|
||||
use style::servo_selector_impl::PseudoElement;
|
||||
use table::TableFlow;
|
||||
use table_caption::TableCaptionFlow;
|
||||
|
@ -348,73 +350,18 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
|||
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
|
||||
/// flow or pushes it onto the given flow list.
|
||||
///
|
||||
/// `#[inline(always)]` because this is performance critical and LLVM will not inline it
|
||||
/// otherwise.
|
||||
#[inline(always)]
|
||||
fn flush_inline_fragments_to_flow_or_list(&mut self,
|
||||
fragment_accumulator: InlineFragmentsAccumulator,
|
||||
flow: &mut FlowRef,
|
||||
flow_list: &mut Vec<FlowRef>,
|
||||
absolute_descendants: &mut AbsoluteDescendants,
|
||||
node: &ConcreteThreadSafeLayoutNode) {
|
||||
fn flush_inline_fragments_to_flow(&mut self,
|
||||
fragment_accumulator: InlineFragmentsAccumulator,
|
||||
flow: &mut FlowRef,
|
||||
absolute_descendants: &mut AbsoluteDescendants,
|
||||
legalizer: &mut Legalizer,
|
||||
node: &ConcreteThreadSafeLayoutNode) {
|
||||
let mut fragments = fragment_accumulator.to_intermediate_inline_fragments();
|
||||
if fragments.is_empty() {
|
||||
return
|
||||
|
@ -450,8 +397,9 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
|||
let scanned_fragments =
|
||||
TextRunScanner::new().scan_for_runs(&mut self.layout_context.font_context(),
|
||||
fragments.fragments);
|
||||
let mut inline_flow_ref: FlowRef = Arc::new(
|
||||
InlineFlow::from_fragments(scanned_fragments, node.style(self.style_context()).writing_mode));
|
||||
let mut inline_flow_ref: FlowRef =
|
||||
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.
|
||||
for inline_block_flow in &inline_block_flows {
|
||||
|
@ -482,33 +430,26 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
|||
}
|
||||
|
||||
inline_flow_ref.finish();
|
||||
|
||||
if flow.need_anonymous_flow(&*inline_flow_ref) {
|
||||
flow_list.push(inline_flow_ref)
|
||||
} else {
|
||||
flow.add_new_child(inline_flow_ref)
|
||||
}
|
||||
legalizer.add_child(&self.style_context().stylist, flow, inline_flow_ref)
|
||||
}
|
||||
|
||||
fn build_block_flow_using_construction_result_of_child(
|
||||
&mut self,
|
||||
flow: &mut FlowRef,
|
||||
consecutive_siblings: &mut Vec<FlowRef>,
|
||||
node: &ConcreteThreadSafeLayoutNode,
|
||||
kid: ConcreteThreadSafeLayoutNode,
|
||||
inline_fragment_accumulator: &mut InlineFragmentsAccumulator,
|
||||
abs_descendants: &mut AbsoluteDescendants) {
|
||||
abs_descendants: &mut AbsoluteDescendants,
|
||||
legalizer: &mut Legalizer) {
|
||||
match kid.swap_out_construction_result() {
|
||||
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
|
||||
// TableWrapperFlow.
|
||||
if flow.is_table() && kid_flow.is_table_caption() {
|
||||
self.set_flow_construction_result(&kid,
|
||||
ConstructionResult::Flow(kid_flow,
|
||||
AbsoluteDescendants::new()))
|
||||
} else if flow.need_anonymous_flow(&*kid_flow) {
|
||||
consecutive_siblings.push(kid_flow)
|
||||
let construction_result =
|
||||
ConstructionResult::Flow(kid_flow, AbsoluteDescendants::new());
|
||||
self.set_flow_construction_result(&kid, construction_result)
|
||||
} else {
|
||||
if !flow::base(&*kid_flow).flags.contains(IS_ABSOLUTELY_POSITIONED) {
|
||||
// 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 =
|
||||
mem::replace(inline_fragment_accumulator,
|
||||
InlineFragmentsAccumulator::new());
|
||||
self.flush_inline_fragments_to_flow_or_list(
|
||||
old_inline_fragment_accumulator,
|
||||
flow,
|
||||
consecutive_siblings,
|
||||
abs_descendants,
|
||||
node);
|
||||
self.flush_inline_fragments_to_flow(old_inline_fragment_accumulator,
|
||||
flow,
|
||||
abs_descendants,
|
||||
legalizer,
|
||||
node);
|
||||
}
|
||||
|
||||
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);
|
||||
legalizer.add_child(&self.style_context().stylist, flow, kid_flow)
|
||||
}
|
||||
abs_descendants.push_descendants(kid_abs_descendants);
|
||||
}
|
||||
|
@ -554,20 +488,16 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
|||
let old_inline_fragment_accumulator =
|
||||
mem::replace(inline_fragment_accumulator,
|
||||
InlineFragmentsAccumulator::new());
|
||||
self.flush_inline_fragments_to_flow_or_list(
|
||||
old_inline_fragment_accumulator,
|
||||
flow,
|
||||
consecutive_siblings,
|
||||
&mut inline_fragment_accumulator.fragments.absolute_descendants,
|
||||
node);
|
||||
let absolute_descendants =
|
||||
&mut inline_fragment_accumulator.fragments.absolute_descendants;
|
||||
self.flush_inline_fragments_to_flow(old_inline_fragment_accumulator,
|
||||
flow,
|
||||
absolute_descendants,
|
||||
legalizer,
|
||||
node);
|
||||
|
||||
// Push the flow generated by the {ib} split onto our list of
|
||||
// flows.
|
||||
if flow.need_anonymous_flow(&*kid_flow) {
|
||||
consecutive_siblings.push(kid_flow)
|
||||
} else {
|
||||
flow.add_new_child(kid_flow)
|
||||
}
|
||||
// Push the flow generated by the {ib} split onto our list of flows.
|
||||
legalizer.add_child(&self.style_context().stylist, flow, kid_flow)
|
||||
}
|
||||
|
||||
// Add the fragments to the list we're maintaining.
|
||||
|
@ -611,12 +541,12 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
|||
-> ConstructionResult {
|
||||
// Gather up fragments for the inline flows we might need to create.
|
||||
let mut inline_fragment_accumulator = InlineFragmentsAccumulator::new();
|
||||
let mut consecutive_siblings = vec!();
|
||||
|
||||
inline_fragment_accumulator.fragments.push_all(initial_fragments);
|
||||
|
||||
// List of absolute descendants, in tree order.
|
||||
let mut abs_descendants = AbsoluteDescendants::new();
|
||||
let mut legalizer = Legalizer::new();
|
||||
if !node.is_replaced_content() {
|
||||
for kid in node.children() {
|
||||
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(
|
||||
&mut flow,
|
||||
&mut consecutive_siblings,
|
||||
node,
|
||||
kid,
|
||||
&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}
|
||||
// splits, after stripping ignorable whitespace.
|
||||
self.flush_inline_fragments_to_flow_or_list(inline_fragment_accumulator,
|
||||
&mut flow,
|
||||
&mut consecutive_siblings,
|
||||
&mut abs_descendants,
|
||||
node);
|
||||
if !consecutive_siblings.is_empty() {
|
||||
self.generate_anonymous_missing_child(consecutive_siblings, &mut flow, node);
|
||||
}
|
||||
self.flush_inline_fragments_to_flow(inline_fragment_accumulator,
|
||||
&mut flow,
|
||||
&mut abs_descendants,
|
||||
&mut legalizer,
|
||||
node);
|
||||
|
||||
// The flow is done.
|
||||
legalizer.finish(&mut flow);
|
||||
flow.finish();
|
||||
|
||||
// Set up the absolute descendants.
|
||||
|
@ -693,8 +621,9 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
|||
|
||||
let mut style = node.style(self.style_context());
|
||||
if node_is_input_or_text_area {
|
||||
style = self.style_context().stylist.
|
||||
precomputed_values_for_pseudo(&PseudoElement::ServoInputText, Some(&style)).unwrap();
|
||||
style = self.style_context()
|
||||
.stylist
|
||||
.style_for_anonymous_box(&PseudoElement::ServoInputText, &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)
|
||||
}
|
||||
|
||||
let flow: FlowRef = Arc::new(
|
||||
BlockFlow::from_fragment(self.build_fragment_for_block(node), float_kind));
|
||||
let fragment = self.build_fragment_for_block(node);
|
||||
let flow: FlowRef =
|
||||
Arc::new(BlockFlow::from_fragment_and_float_kind(fragment, float_kind));
|
||||
self.build_flow_for_block_like(flow, node)
|
||||
}
|
||||
|
||||
|
@ -1088,40 +1018,12 @@ 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`.
|
||||
/// This yields a `MulticolFlow` with a single `MulticolColumnFlow` underneath it.
|
||||
fn build_flow_for_multicol(&mut self, node: &ConcreteThreadSafeLayoutNode,
|
||||
float_kind: Option<FloatKind>)
|
||||
-> ConstructionResult {
|
||||
fn build_flow_for_multicol(&mut self,
|
||||
node: &ConcreteThreadSafeLayoutNode,
|
||||
float_kind: Option<FloatKind>)
|
||||
-> ConstructionResult {
|
||||
let fragment = Fragment::new(node, SpecificFragmentInfo::Multicol, self.layout_context);
|
||||
let mut flow: FlowRef = Arc::new(MulticolFlow::from_fragment(fragment, float_kind));
|
||||
|
||||
|
@ -1161,11 +1063,26 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
|||
|
||||
/// Builds a flow for a node with `display: table`. This yields a `TableWrapperFlow` with
|
||||
/// possibly other `TableCaptionFlow`s or `TableFlow`s underneath it.
|
||||
fn build_flow_for_table_wrapper(&mut self, node: &ConcreteThreadSafeLayoutNode, float_value: float::T)
|
||||
-> ConstructionResult {
|
||||
let fragment = Fragment::new(node, SpecificFragmentInfo::TableWrapper, self.layout_context);
|
||||
let mut wrapper_flow: FlowRef = Arc::new(
|
||||
TableWrapperFlow::from_fragment(fragment, FloatKind::from_property(float_value)));
|
||||
fn build_flow_for_table(&mut self, node: &ConcreteThreadSafeLayoutNode, float_value: float::T)
|
||||
-> ConstructionResult {
|
||||
let mut legalizer = Legalizer::new();
|
||||
|
||||
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_flow = Arc::new(TableFlow::from_fragment(table_fragment));
|
||||
|
@ -1183,7 +1100,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
|||
caption_side::T::top);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1193,7 +1110,9 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
|||
caption_side::T::bottom);
|
||||
|
||||
// The flow is done.
|
||||
legalizer.finish(&mut wrapper_flow);
|
||||
wrapper_flow.finish();
|
||||
|
||||
let contains_positioned_fragments = wrapper_flow.contains_positioned_fragments();
|
||||
if contains_positioned_fragments {
|
||||
// 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
|
||||
/// 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 {
|
||||
let flotation = FloatKind::from_property(flotation);
|
||||
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'.
|
||||
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 {
|
||||
let fragment = self.build_fragment_for_block(node);
|
||||
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.
|
||||
(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)
|
||||
}
|
||||
|
||||
|
@ -1923,3 +1846,199 @@ impl ComputedValueUtils for ServoComputedValues {
|
|||
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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#![deny(unsafe_code)]
|
||||
|
||||
use app_units::{Au, MAX_AU};
|
||||
use block::BlockFlow;
|
||||
use block::{BlockFlow, MarginsMayCollapseFlag};
|
||||
use context::{LayoutContext, SharedLayoutContext};
|
||||
use display_list_builder::{DisplayListBuildState, FlexFlowDisplayListBuilding};
|
||||
use euclid::Point2D;
|
||||
|
@ -15,7 +15,6 @@ use floats::FloatKind;
|
|||
use flow;
|
||||
use flow::{Flow, FlowClass, ImmutableFlowUtils, OpaqueFlow};
|
||||
use flow::{INLINE_POSITION_IS_STATIC, IS_ABSOLUTELY_POSITIONED};
|
||||
use flow_ref::{self, FlowRef};
|
||||
use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
|
||||
use gfx::display_list::StackingContext;
|
||||
use layout_debug;
|
||||
|
@ -113,10 +112,8 @@ struct FlexItem {
|
|||
/// The maximal main size. If this property is not actually set by style
|
||||
/// It will be the largest size available for code reuse.
|
||||
pub max_size: Au,
|
||||
/// Reference to the actual flow.
|
||||
pub flow: FlowRef,
|
||||
/// Style of the child flow, stored here to reduce overhead.
|
||||
pub style: Arc<ServoComputedValues>,
|
||||
/// The index of the actual flow in our child list.
|
||||
pub index: usize,
|
||||
/// The 'flex-grow' property of this item.
|
||||
pub flex_grow: f32,
|
||||
/// The 'flex-shrink' property of this item.
|
||||
|
@ -130,8 +127,8 @@ struct FlexItem {
|
|||
}
|
||||
|
||||
impl FlexItem {
|
||||
pub fn new(flow: FlowRef) -> FlexItem {
|
||||
let style = flow.as_block().fragment.style.clone();
|
||||
pub fn new(index: usize, flow: &Flow) -> FlexItem {
|
||||
let style = &flow.as_block().fragment.style;
|
||||
let flex_grow = style.get_position().flex_grow;
|
||||
let flex_shrink = style.get_position().flex_shrink;
|
||||
let order = style.get_position().order;
|
||||
|
@ -142,8 +139,7 @@ impl FlexItem {
|
|||
base_size: Au(0),
|
||||
min_size: Au(0),
|
||||
max_size: MAX_AU,
|
||||
flow: flow,
|
||||
style: style,
|
||||
index: index,
|
||||
flex_grow: flex_grow,
|
||||
flex_shrink: flex_shrink,
|
||||
order: order,
|
||||
|
@ -155,15 +151,15 @@ impl FlexItem {
|
|||
/// 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()
|
||||
/// pass so that the item has already been layouted.
|
||||
pub fn init_sizes(&mut self, containing_length: Au, direction: Direction) {
|
||||
let block = flow_ref::deref_mut(&mut self.flow).as_mut_block();
|
||||
pub fn init_sizes(&mut self, flow: &mut Flow, containing_length: Au, direction: Direction) {
|
||||
let block = flow.as_mut_block();
|
||||
match direction {
|
||||
// TODO(stshine): the definition of min-{width, height} in style component
|
||||
// should change to LengthOrPercentageOrAuto for automatic implied minimal size.
|
||||
// https://drafts.csswg.org/css-flexbox-1/#min-size-auto
|
||||
Direction::Inline => {
|
||||
let basis = from_flex_basis(self.style.get_position().flex_basis,
|
||||
self.style.content_inline_size(),
|
||||
let basis = from_flex_basis(block.fragment.style.get_position().flex_basis,
|
||||
block.fragment.style.content_inline_size(),
|
||||
Some(containing_length));
|
||||
|
||||
// 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_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
|
||||
- block.fragment.surrounding_intrinsic_inline_size()
|
||||
- border_padding
|
||||
- margin
|
||||
+ block.fragment.box_sizing_boundary(direction);
|
||||
self.base_size = basis.specified_or_default(content_size);
|
||||
self.max_size = specified_or_none(self.style.max_inline_size(), containing_length)
|
||||
.unwrap_or(MAX_AU);
|
||||
self.min_size = specified(self.style.min_inline_size(), containing_length);
|
||||
self.max_size = specified_or_none(block.fragment.style.max_inline_size(),
|
||||
containing_length).unwrap_or(MAX_AU);
|
||||
self.min_size = specified(block.fragment.style.min_inline_size(),
|
||||
containing_length);
|
||||
}
|
||||
Direction::Block => {
|
||||
let basis = from_flex_basis(self.style.get_position().flex_basis,
|
||||
self.style.content_block_size(),
|
||||
let basis = from_flex_basis(block.fragment.style.get_position().flex_basis,
|
||||
block.fragment.style.content_block_size(),
|
||||
Some(containing_length));
|
||||
let content_size = block.fragment.border_box.size.block
|
||||
- block.fragment.border_padding.block_start_end()
|
||||
+ block.fragment.box_sizing_boundary(direction);
|
||||
self.base_size = basis.specified_or_default(content_size);
|
||||
self.max_size = specified_or_none(self.style.max_block_size(), containing_length)
|
||||
.unwrap_or(MAX_AU);
|
||||
self.min_size = specified(self.style.min_block_size(), containing_length);
|
||||
self.max_size = specified_or_none(block.fragment.style.max_block_size(),
|
||||
containing_length).unwrap_or(MAX_AU);
|
||||
self.min_size = specified(block.fragment.style.min_block_size(),
|
||||
containing_length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the outer main size of the item, including paddings and margins,
|
||||
/// clamped by max and min size.
|
||||
pub fn outer_main_size(&self, direction: Direction) -> Au {
|
||||
let ref fragment = self.flow.as_block().fragment;
|
||||
pub fn outer_main_size(&self, flow: &Flow, direction: Direction) -> Au {
|
||||
let ref fragment = flow.as_block().fragment;
|
||||
let outer_width = match direction {
|
||||
Direction::Inline => {
|
||||
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.
|
||||
pub fn auto_margin_count(&self, direction: Direction) -> i32 {
|
||||
let margin = self.style.logical_margin();
|
||||
pub fn auto_margin_count(&self, flow: &Flow, direction: Direction) -> i32 {
|
||||
let margin = flow.as_block().fragment.style.logical_margin();
|
||||
let mut margin_count = 0;
|
||||
match direction {
|
||||
Direction::Inline => {
|
||||
|
@ -382,7 +382,7 @@ impl FlexFlow {
|
|||
}
|
||||
|
||||
FlexFlow {
|
||||
block_flow: BlockFlow::from_fragment(fragment, flotation),
|
||||
block_flow: BlockFlow::from_fragment_and_float_kind(fragment, flotation),
|
||||
main_mode: main_mode,
|
||||
available_main_size: AxisSize::Infinite,
|
||||
available_cross_size: AxisSize::Infinite,
|
||||
|
@ -411,13 +411,15 @@ impl FlexFlow {
|
|||
let mut margin_count = 0;
|
||||
|
||||
let items = &mut self.items[start..];
|
||||
let mut children = self.block_flow.base.children.random_access_mut();
|
||||
for mut item in items {
|
||||
item.init_sizes(container_size, self.main_mode);
|
||||
let outer_main_size = item.outer_main_size(self.main_mode);
|
||||
let kid = children.get(item.index);
|
||||
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 {
|
||||
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;
|
||||
end += 1;
|
||||
}
|
||||
|
@ -437,8 +439,8 @@ impl FlexFlow {
|
|||
|
||||
let mut computation = self.block_flow.fragment.compute_intrinsic_inline_sizes();
|
||||
if !fixed_width {
|
||||
for kid in &mut self.items {
|
||||
let base = flow::mut_base(flow_ref::deref_mut(&mut kid.flow));
|
||||
for kid in self.block_flow.base.children.iter_mut() {
|
||||
let base = flow::mut_base(kid);
|
||||
let is_absolutely_positioned = base.flags.contains(IS_ABSOLUTELY_POSITIONED);
|
||||
if !is_absolutely_positioned {
|
||||
let flex_item_inline_sizes = IntrinsicISizes {
|
||||
|
@ -463,8 +465,8 @@ impl FlexFlow {
|
|||
|
||||
let mut computation = self.block_flow.fragment.compute_intrinsic_inline_sizes();
|
||||
if !fixed_width {
|
||||
for kid in &mut self.items {
|
||||
let base = flow::mut_base(flow_ref::deref_mut(&mut kid.flow));
|
||||
for kid in self.block_flow.base.children.iter_mut() {
|
||||
let base = flow::mut_base(kid);
|
||||
let is_absolutely_positioned = base.flags.contains(IS_ABSOLUTELY_POSITIONED);
|
||||
if !is_absolutely_positioned {
|
||||
computation.content_intrinsic_sizes.minimum_inline_size =
|
||||
|
@ -503,25 +505,25 @@ impl FlexFlow {
|
|||
AxisSize::MinMax(ref constraint) => constraint.clamp(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 {
|
||||
{
|
||||
let kid_base = flow::mut_base(flow_ref::deref_mut(&mut kid.flow));
|
||||
kid_base.block_container_explicit_block_size = container_block_size;
|
||||
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,
|
||||
// and its inline-size is our content inline-size.
|
||||
kid_base.position.start.i =
|
||||
if kid_base.writing_mode.is_bidi_ltr() == containing_block_mode.is_bidi_ltr() {
|
||||
inline_start_content_edge
|
||||
} else {
|
||||
// The kid's inline 'start' is at the parent's 'end'
|
||||
inline_end_content_edge
|
||||
};
|
||||
}
|
||||
kid_base.block_container_inline_size = container_inline_size;
|
||||
kid_base.block_container_writing_mode = containing_block_mode;
|
||||
kid_base.position.start.i = inline_start_content_edge;
|
||||
let kid_base = flow::mut_base(children.get(kid.index));
|
||||
kid_base.block_container_explicit_block_size = container_block_size;
|
||||
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, and its inline-size is our content inline-size.
|
||||
kid_base.position.start.i =
|
||||
if kid_base.writing_mode.is_bidi_ltr() == containing_block_mode.is_bidi_ltr() {
|
||||
inline_start_content_edge
|
||||
} else {
|
||||
// The kid's inline 'start' is at the parent's 'end'
|
||||
inline_end_content_edge
|
||||
};
|
||||
}
|
||||
kid_base.block_container_inline_size = container_inline_size;
|
||||
kid_base.block_container_writing_mode = containing_block_mode;
|
||||
kid_base.position.start.i = inline_start_content_edge;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -599,8 +601,9 @@ impl FlexFlow {
|
|||
_ => {}
|
||||
}
|
||||
|
||||
let mut children = self.block_flow.base.children.random_access_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_inline_size = inline_size;
|
||||
|
@ -645,14 +648,16 @@ impl FlexFlow {
|
|||
}
|
||||
|
||||
// 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 {
|
||||
self.block_flow.fragment.border_padding.block_start
|
||||
} else {
|
||||
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 {
|
||||
base.position.start.b = cur_b;
|
||||
cur_b = cur_b + base.position.size.block;
|
||||
|
@ -661,7 +666,6 @@ impl FlexFlow {
|
|||
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>) {
|
||||
|
@ -673,13 +677,17 @@ impl FlexFlow {
|
|||
let mut total_cross_size = Au(0);
|
||||
let mut line_interval = Au(0);
|
||||
|
||||
for line in self.lines.iter_mut() {
|
||||
for item in &self.items[line.range.clone()] {
|
||||
let ref fragment = item.flow.as_block().fragment;
|
||||
line.cross_size = max(line.cross_size,
|
||||
fragment.border_box.size.block + fragment.margin.block_start_end());
|
||||
{
|
||||
let mut children = self.block_flow.base.children.random_access_mut();
|
||||
for line in self.lines.iter_mut() {
|
||||
for item in &self.items[line.range.clone()] {
|
||||
let fragment = &children.get(item.index).as_block().fragment;
|
||||
line.cross_size = max(line.cross_size,
|
||||
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);
|
||||
|
@ -726,10 +734,11 @@ impl FlexFlow {
|
|||
}
|
||||
}
|
||||
|
||||
let mut children = self.block_flow.base.children.random_access_mut();
|
||||
for line in &self.lines {
|
||||
for mut item in self.items[line.range.clone()].iter_mut() {
|
||||
let auto_margin_count = item.auto_margin_count(Direction::Block);
|
||||
let mut block = flow_ref::deref_mut(&mut item.flow).as_mut_block();
|
||||
for item in self.items[line.range.clone()].iter_mut() {
|
||||
let block = children.get(item.index).as_mut_block();
|
||||
let auto_margin_count = item.auto_margin_count(block, Direction::Block);
|
||||
let margin = block.fragment.style().logical_margin();
|
||||
|
||||
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.
|
||||
// FIXME(stshine): This should be done during flow construction.
|
||||
let mut items = self.block_flow.base.children.iter_flow_ref_mut()
|
||||
.filter(|flow| !flow.as_block().base.flags.contains(IS_ABSOLUTELY_POSITIONED))
|
||||
.map(|flow| FlexItem::new(flow.clone()))
|
||||
.collect::<Vec<FlexItem>>();
|
||||
let mut items: Vec<FlexItem> =
|
||||
self.block_flow
|
||||
.base
|
||||
.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);
|
||||
self.items = items;
|
||||
|
@ -910,12 +926,13 @@ impl Flow for FlexFlow {
|
|||
}
|
||||
|
||||
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 {
|
||||
Direction::Inline =>
|
||||
self.inline_mode_assign_block_size(layout_context),
|
||||
Direction::Block =>
|
||||
self.block_mode_assign_block_size(layout_context)
|
||||
Direction::Inline => self.inline_mode_assign_block_size(layout_context),
|
||||
Direction::Block => self.block_mode_assign_block_size(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ use euclid::{Point2D, Size2D};
|
|||
use floats::{Floats, SpeculatedFloatPlacement};
|
||||
use flow_list::{FlowList, MutFlowListIterator};
|
||||
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_traits::StackingContextId;
|
||||
use gfx_traits::print_tree::PrintTree;
|
||||
|
@ -44,17 +44,16 @@ use parallel::FlowParallelInfo;
|
|||
use rustc_serialize::{Encodable, Encoder};
|
||||
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::wrapper_traits::{PseudoElementType, ThreadSafeLayoutNode};
|
||||
use std::{fmt, mem, raw};
|
||||
use std::iter::Zip;
|
||||
use std::slice::IterMut;
|
||||
use std::sync::Arc;
|
||||
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::dom::TRestyleDamage;
|
||||
use style::logical_geometry::{LogicalRect, LogicalSize, WritingMode};
|
||||
use style::properties::{self, ServoComputedValues};
|
||||
use style::properties::ServoComputedValues;
|
||||
use style::values::computed::LengthOrPercentageOrAuto;
|
||||
use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, TableFlow};
|
||||
use table_caption::TableCaptionFlow;
|
||||
|
@ -470,12 +469,6 @@ pub trait ImmutableFlowUtils {
|
|||
/// Returns true if this flow is one of table-related flows.
|
||||
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.
|
||||
fn contains_roots_of_absolute_flow_tree(&self) -> bool;
|
||||
|
||||
|
@ -665,6 +658,9 @@ bitflags! {
|
|||
|
||||
/// Whether this flow contains any text and/or replaced fragments.
|
||||
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,
|
||||
"sc={:?} pos={:?}, {}{} floatspec-in={:?}, floatspec-out={:?}, overflow={:?}{}{}{}",
|
||||
"sc={:?} pos={:?}, {}{} floatspec-in={:?}, floatspec-out={:?}, \
|
||||
overflow={:?}{}{}{}",
|
||||
self.stacking_context_id,
|
||||
self.position,
|
||||
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.
|
||||
fn contains_roots_of_absolute_flow_tree(&self) -> bool {
|
||||
self.contains_relatively_positioned_fragments() || self.is_root()
|
||||
|
|
|
@ -6,9 +6,15 @@ use flow::Flow;
|
|||
use flow_ref::{self, FlowRef};
|
||||
use std::collections::{LinkedList, linked_list};
|
||||
|
||||
// This needs to be reworked now that we have dynamically-sized types in Rust.
|
||||
// Until then, it's just a wrapper around LinkedList.
|
||||
|
||||
/// This needs to be reworked now that we have dynamically-sized types in Rust.
|
||||
/// 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 {
|
||||
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]
|
||||
pub fn iter<'a>(&'a self) -> impl DoubleEndedIterator<Item = &'a Flow> {
|
||||
self.flows.iter().map(|flow| &**flow)
|
||||
}
|
||||
|
||||
/// 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]
|
||||
pub fn iter_mut(&mut self) -> 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]
|
||||
pub fn iter_flow_ref_mut<'a>(&'a mut self)
|
||||
-> impl DoubleEndedIterator<Item = &'a mut FlowRef> {
|
||||
self.flows.iter_mut()
|
||||
pub fn random_access_mut(&mut self) -> FlowListRandomAccessMut {
|
||||
let length = self.flows.len();
|
||||
FlowListRandomAccessMut {
|
||||
iterator: self.flows.iter_mut(),
|
||||
cache: Vec::with_capacity(length),
|
||||
}
|
||||
}
|
||||
|
||||
/// O(1)
|
||||
|
@ -111,3 +130,22 @@ impl<'a> Iterator for MutFlowListIterator<'a> {
|
|||
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])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
/// preserving all the other data.
|
||||
pub fn transform(&self, size: LogicalSize<Au>, info: SpecificFragmentInfo)
|
||||
|
@ -1051,7 +1075,12 @@ impl Fragment {
|
|||
SpecificFragmentInfo::Svg(_) => {
|
||||
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 |
|
||||
INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED;
|
||||
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
|
||||
/// margins.
|
||||
/// Returns the portion of the intrinsic inline-size that consists of borders/padding and
|
||||
/// margins, respectively.
|
||||
///
|
||||
/// 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 style = self.style();
|
||||
|
||||
|
@ -1127,16 +1156,19 @@ impl Fragment {
|
|||
Au(0)
|
||||
};
|
||||
|
||||
margin + padding + border
|
||||
(border + padding, margin)
|
||||
}
|
||||
|
||||
/// Uses the style only to estimate the intrinsic inline-sizes. These may be modified for text
|
||||
/// 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 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) {
|
||||
specified = MaybeAuto::from_style(style.content_inline_size(),
|
||||
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)) {
|
||||
specified = min(specified, max)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(#2261, pcwalton): This won't work well for inlines: is this OK?
|
||||
let surrounding_inline_size = self.surrounding_intrinsic_inline_size();
|
||||
if self.style.get_position().box_sizing == box_sizing::T::border_box {
|
||||
specified -= border_padding
|
||||
}
|
||||
}
|
||||
|
||||
IntrinsicISizesContribution {
|
||||
content_intrinsic_sizes: IntrinsicISizes {
|
||||
minimum_inline_size: specified,
|
||||
preferred_inline_size: specified,
|
||||
},
|
||||
surrounding_size: surrounding_inline_size,
|
||||
surrounding_size: border_padding + margin,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ impl ListItemFlow {
|
|||
flotation: Option<FloatKind>)
|
||||
-> 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,
|
||||
};
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ pub struct MulticolColumnFlow {
|
|||
impl MulticolFlow {
|
||||
pub fn from_fragment(fragment: Fragment, float_kind: Option<FloatKind>) -> 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),
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ impl MulticolFlow {
|
|||
impl MulticolColumnFlow {
|
||||
pub fn from_fragment(fragment: Fragment) -> MulticolColumnFlow {
|
||||
MulticolColumnFlow {
|
||||
block_flow: BlockFlow::from_fragment(fragment, None),
|
||||
block_flow: BlockFlow::from_fragment(fragment),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ pub struct TableFlow {
|
|||
|
||||
impl 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 =
|
||||
if block_flow.fragment().style().get_table().table_layout == table_layout::T::fixed {
|
||||
TableLayout::Fixed
|
||||
|
@ -85,9 +85,13 @@ impl TableFlow {
|
|||
/// sizes.
|
||||
fn update_automatic_column_inline_sizes(
|
||||
parent_inline_sizes: &mut Vec<ColumnIntrinsicInlineSize>,
|
||||
child_cell_inline_sizes: &[CellIntrinsicInlineSize])
|
||||
child_cell_inline_sizes: &[CellIntrinsicInlineSize],
|
||||
surrounding_size: Au)
|
||||
-> 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;
|
||||
for child_cell_inline_size in child_cell_inline_sizes {
|
||||
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;
|
||||
total_inline_sizes.preferred_inline_size =
|
||||
total_inline_sizes.preferred_inline_size +
|
||||
total_inline_sizes.preferred_inline_size +=
|
||||
parent_inline_sizes[column_index].preferred;
|
||||
|
||||
column_index += 1
|
||||
|
@ -139,7 +142,8 @@ impl TableFlow {
|
|||
column_inline_sizes: &mut Vec<ColumnIntrinsicInlineSize>,
|
||||
computation: &mut IntrinsicISizesContribution,
|
||||
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
|
||||
// not defined in the column group.
|
||||
//
|
||||
|
@ -158,7 +162,8 @@ impl TableFlow {
|
|||
TableLayout::Auto => {
|
||||
computation.union_block(&TableFlow::update_automatic_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}",
|
||||
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
|
||||
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 {
|
||||
|
@ -277,15 +279,18 @@ impl Flow for TableFlow {
|
|||
&*self.block_flow.fragment.style,
|
||||
CollapsedBorderProvenance::FromTable));
|
||||
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();
|
||||
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 computation,
|
||||
first_row,
|
||||
self.table_layout);
|
||||
self.table_layout,
|
||||
border_padding);
|
||||
if collapsing_borders {
|
||||
let next_index_and_sibling = iterator.peek();
|
||||
let next_collapsed_borders_in_block_direction =
|
||||
|
@ -316,9 +321,16 @@ impl Flow for TableFlow {
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
computation.surrounding_size = computation.surrounding_size +
|
||||
self.total_horizontal_spacing();
|
||||
let total_horizontal_spacing = self.total_horizontal_spacing();
|
||||
let mut style_specified_intrinsic_inline_size =
|
||||
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()
|
||||
}
|
||||
|
@ -359,15 +371,16 @@ impl Flow for TableFlow {
|
|||
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 spacing_per_cell = self.spacing();
|
||||
let spacing = self.total_horizontal_spacing();
|
||||
let content_inline_size =
|
||||
self.block_flow.fragment.border_box.size.inline - padding_and_borders - spacing;
|
||||
let total_horizontal_spacing = self.total_horizontal_spacing();
|
||||
let content_inline_size = self.block_flow.fragment.border_box.size.inline -
|
||||
padding_and_borders - total_horizontal_spacing;
|
||||
|
||||
match self.table_layout {
|
||||
TableLayout::Fixed => {
|
||||
// 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.
|
||||
// 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();
|
||||
if num_unspecified_inline_sizes != 0 {
|
||||
let extra_column_inline_size = content_inline_size - total_column_inline_size;
|
||||
|
|
|
@ -29,7 +29,7 @@ pub struct TableCaptionFlow {
|
|||
impl TableCaptionFlow {
|
||||
pub fn from_fragment(fragment: Fragment) -> TableCaptionFlow {
|
||||
TableCaptionFlow {
|
||||
block_flow: BlockFlow::from_fragment(fragment, None)
|
||||
block_flow: BlockFlow::from_fragment(fragment),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,10 +46,19 @@ pub struct 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>(
|
||||
node: &N, fragment: Fragment, visible: bool) -> TableCellFlow {
|
||||
TableCellFlow {
|
||||
block_flow: BlockFlow::from_fragment(fragment, None),
|
||||
block_flow: BlockFlow::from_fragment(fragment),
|
||||
collapsed_borders: CollapsedBordersForCell::new(),
|
||||
column_span: node.get_colspan(),
|
||||
visible: visible,
|
||||
|
|
|
@ -84,7 +84,7 @@ impl TableRowFlow {
|
|||
pub fn from_fragment(fragment: Fragment) -> TableRowFlow {
|
||||
let writing_mode = fragment.style().writing_mode;
|
||||
TableRowFlow {
|
||||
block_flow: BlockFlow::from_fragment(fragment, None),
|
||||
block_flow: BlockFlow::from_fragment(fragment),
|
||||
cell_intrinsic_inline_sizes: Vec::new(),
|
||||
column_computed_inline_sizes: Vec::new(),
|
||||
spacing: border_spacing::T {
|
||||
|
|
|
@ -64,7 +64,7 @@ impl TableRowGroupFlow {
|
|||
pub fn from_fragment(fragment: Fragment) -> TableRowGroupFlow {
|
||||
let writing_mode = fragment.style().writing_mode;
|
||||
TableRowGroupFlow {
|
||||
block_flow: BlockFlow::from_fragment(fragment, None),
|
||||
block_flow: BlockFlow::from_fragment(fragment),
|
||||
column_intrinsic_inline_sizes: Vec::new(),
|
||||
column_computed_inline_sizes: Vec::new(),
|
||||
spacing: border_spacing::T {
|
||||
|
|
|
@ -29,7 +29,7 @@ use std::cmp::{max, min};
|
|||
use std::fmt;
|
||||
use std::ops::Add;
|
||||
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::logical_geometry::{LogicalRect, LogicalSize};
|
||||
use style::properties::ServoComputedValues;
|
||||
|
@ -57,8 +57,13 @@ pub struct TableWrapperFlow {
|
|||
}
|
||||
|
||||
impl TableWrapperFlow {
|
||||
pub fn from_fragment(fragment: Fragment, float_kind: Option<FloatKind>) -> TableWrapperFlow {
|
||||
let mut block_flow = BlockFlow::from_fragment(fragment, float_kind);
|
||||
pub fn from_fragment(fragment: Fragment) -> TableWrapperFlow {
|
||||
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 ==
|
||||
table_layout::T::fixed {
|
||||
TableLayout::Fixed
|
||||
|
@ -71,6 +76,7 @@ impl TableWrapperFlow {
|
|||
table_layout: table_layout
|
||||
}
|
||||
}
|
||||
|
||||
fn border_padding_and_spacing(&mut self) -> (Au, Au) {
|
||||
let (mut table_border_padding, mut spacing) = (Au(0), Au(0));
|
||||
for kid in self.block_flow.base.child_iter_mut() {
|
||||
|
@ -85,17 +91,15 @@ impl TableWrapperFlow {
|
|||
(table_border_padding, spacing)
|
||||
}
|
||||
|
||||
/// Calculates table column sizes for automatic layout per INTRINSIC § 4.3.
|
||||
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,
|
||||
// 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
|
||||
// tables are separated into table flows and table wrapper flows.
|
||||
let available_inline_size = self.block_flow.fragment.border_box.size.inline;
|
||||
// Instructs our first child, which is the table itself, to compute its border and padding.
|
||||
//
|
||||
// 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
|
||||
// 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.
|
||||
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() {
|
||||
if !kid.is_table() {
|
||||
continue
|
||||
|
@ -103,38 +107,19 @@ impl TableWrapperFlow {
|
|||
|
||||
let kid_table = kid.as_mut_table();
|
||||
let kid_block_flow = &mut kid_table.block_flow;
|
||||
kid_block_flow.fragment
|
||||
.compute_border_and_padding(available_inline_size,
|
||||
self.block_flow
|
||||
.fragment
|
||||
.style
|
||||
.get_inheritedtable()
|
||||
.border_collapse);
|
||||
kid_block_flow.fragment.compute_border_and_padding(available_inline_size,
|
||||
border_collapse);
|
||||
kid_block_flow.fragment.compute_block_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();
|
||||
|
||||
// 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 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;
|
||||
/// Calculates table column sizes for automatic layout per INTRINSIC § 4.3.
|
||||
fn calculate_table_column_sizes_for_automatic_layout(
|
||||
&mut self,
|
||||
intermediate_column_inline_sizes: &mut [IntermediateColumnInlineSize]) {
|
||||
let available_inline_size = self.available_inline_size();
|
||||
|
||||
// Compute all the guesses for the column sizes, and sum them.
|
||||
let mut total_guess = AutoLayoutCandidateGuess::new();
|
||||
|
@ -182,8 +167,35 @@ impl TableWrapperFlow {
|
|||
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 +
|
||||
table_border_padding + spacing;
|
||||
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,
|
||||
preferred_width_of_all_columns: preferred_width_of_all_columns,
|
||||
border_collapse: border_collapse,
|
||||
table_border_padding: border_padding,
|
||||
};
|
||||
let input =
|
||||
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,
|
||||
preferred_width_of_all_columns: preferred_width_of_all_columns,
|
||||
border_collapse: border_collapse,
|
||||
table_border_padding: border_padding,
|
||||
};
|
||||
let input =
|
||||
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,
|
||||
preferred_width_of_all_columns: preferred_width_of_all_columns,
|
||||
border_collapse: border_collapse,
|
||||
table_border_padding: border_padding,
|
||||
};
|
||||
let input =
|
||||
inline_size_computer.compute_inline_size_constraint_inputs(&mut self.block_flow,
|
||||
|
@ -344,13 +359,20 @@ impl Flow for TableWrapperFlow {
|
|||
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,
|
||||
containing_block_inline_size,
|
||||
&intermediate_column_inline_sizes);
|
||||
|
||||
if let TableLayout::Auto = self.table_layout {
|
||||
self.calculate_table_column_sizes_for_automatic_layout(
|
||||
&mut intermediate_column_inline_sizes)
|
||||
match self.table_layout {
|
||||
TableLayout::Auto => {
|
||||
self.calculate_table_column_sizes_for_automatic_layout(
|
||||
&mut intermediate_column_inline_sizes)
|
||||
}
|
||||
TableLayout::Fixed => {}
|
||||
}
|
||||
|
||||
let inline_start_content_edge = self.block_flow.fragment.border_box.start.i;
|
||||
|
@ -472,6 +494,10 @@ impl Flow for TableWrapperFlow {
|
|||
fn print_extra_flow_children(&self, print_tree: &mut PrintTree) {
|
||||
self.block_flow.print_extra_flow_children(print_tree);
|
||||
}
|
||||
|
||||
fn positioning(&self) -> position::T {
|
||||
self.block_flow.positioning()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for TableWrapperFlow {
|
||||
|
@ -734,10 +760,15 @@ struct IntermediateColumnInlineSize {
|
|||
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,
|
||||
containing_block_inline_size: Au,
|
||||
minimum_width_of_all_columns: Au,
|
||||
preferred_width_of_all_columns: Au)
|
||||
preferred_width_of_all_columns: Au,
|
||||
table_border_padding: Au)
|
||||
-> MaybeAuto {
|
||||
let inline_size_from_style = MaybeAuto::from_style(block.fragment.style.content_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(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,
|
||||
preferred_width_of_all_columns: Au,
|
||||
border_collapse: border_collapse::T,
|
||||
table_border_padding: Au,
|
||||
}
|
||||
|
||||
impl ISizeAndMarginsComputer for Table {
|
||||
|
@ -769,13 +802,12 @@ impl ISizeAndMarginsComputer for Table {
|
|||
shared_context: &SharedStyleContext)
|
||||
-> MaybeAuto {
|
||||
let containing_block_inline_size =
|
||||
self.containing_block_inline_size(block,
|
||||
parent_flow_inline_size,
|
||||
shared_context);
|
||||
self.containing_block_inline_size(block, parent_flow_inline_size, shared_context);
|
||||
initial_computed_inline_size(block,
|
||||
containing_block_inline_size,
|
||||
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,
|
||||
|
@ -790,6 +822,7 @@ struct FloatedTable {
|
|||
minimum_width_of_all_columns: Au,
|
||||
preferred_width_of_all_columns: Au,
|
||||
border_collapse: border_collapse::T,
|
||||
table_border_padding: Au,
|
||||
}
|
||||
|
||||
impl ISizeAndMarginsComputer for FloatedTable {
|
||||
|
@ -810,7 +843,8 @@ impl ISizeAndMarginsComputer for FloatedTable {
|
|||
initial_computed_inline_size(block,
|
||||
containing_block_inline_size,
|
||||
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,
|
||||
|
@ -825,6 +859,7 @@ struct AbsoluteTable {
|
|||
minimum_width_of_all_columns: Au,
|
||||
preferred_width_of_all_columns: Au,
|
||||
border_collapse: border_collapse::T,
|
||||
table_border_padding: Au,
|
||||
}
|
||||
|
||||
impl ISizeAndMarginsComputer for AbsoluteTable {
|
||||
|
@ -845,7 +880,8 @@ impl ISizeAndMarginsComputer for AbsoluteTable {
|
|||
initial_computed_inline_size(block,
|
||||
containing_block_inline_size,
|
||||
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,
|
||||
|
@ -853,7 +889,9 @@ impl ISizeAndMarginsComputer for AbsoluteTable {
|
|||
parent_flow_inline_size: Au,
|
||||
shared_context: &SharedStyleContext)
|
||||
-> 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,
|
||||
|
|
|
@ -265,9 +265,10 @@ pub trait ThreadSafeLayoutNode: Clone + Copy + NodeInfo + PartialEq + Sized {
|
|||
.current_styles().pseudos.contains_key(&style_pseudo) {
|
||||
let mut data = self.get_style_data().unwrap().borrow_mut();
|
||||
let new_style =
|
||||
context.stylist
|
||||
.precomputed_values_for_pseudo(&style_pseudo,
|
||||
Some(&data.current_styles().primary));
|
||||
context.stylist.precomputed_values_for_pseudo(
|
||||
&style_pseudo,
|
||||
Some(&data.current_styles().primary),
|
||||
false);
|
||||
data.current_pseudos_mut()
|
||||
.insert(style_pseudo.clone(), new_style.unwrap());
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use context::SharedStyleContext;
|
|||
use dom::{OpaqueNode, UnsafeNode};
|
||||
use euclid::point::Point2D;
|
||||
use keyframes::{KeyframesStep, KeyframesStepValue};
|
||||
use properties::{self, ComputedValues, Importance};
|
||||
use properties::{self, CascadeFlags, ComputedValues, Importance};
|
||||
use properties::animated_properties::{AnimatedProperty, TransitionProperty};
|
||||
use properties::longhands::animation_direction::computed_value::AnimationDirection;
|
||||
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,
|
||||
&[declaration_block],
|
||||
false,
|
||||
Some(previous_style),
|
||||
None,
|
||||
None,
|
||||
context.error_reporter.clone());
|
||||
context.error_reporter.clone(),
|
||||
CascadeFlags::empty());
|
||||
computed
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use cascade_info::CascadeInfo;
|
|||
use context::{SharedStyleContext, StyleContext};
|
||||
use data::{NodeStyles, PseudoStyles};
|
||||
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 selector_impl::{PseudoElement, TheSelectorImpl};
|
||||
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.
|
||||
// We encapsulate them in this struct to avoid mixing them up.
|
||||
//
|
||||
// FIXME(pcwalton): Unify with `CascadeFlags`, perhaps?
|
||||
struct CascadeBooleans {
|
||||
shareable: bool,
|
||||
cacheable: bool,
|
||||
|
@ -523,6 +525,11 @@ trait PrivateMatchMethods: TNode {
|
|||
cacheable = cacheable && !has_style_attribute;
|
||||
|
||||
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 {
|
||||
Some(ref parent_style) => {
|
||||
let cache_entry = applicable_declarations_cache.find(applicable_declarations);
|
||||
|
@ -533,20 +540,20 @@ trait PrivateMatchMethods: TNode {
|
|||
|
||||
cascade(shared_context.viewport_size,
|
||||
applicable_declarations,
|
||||
booleans.shareable,
|
||||
Some(&***parent_style),
|
||||
cached_computed_values,
|
||||
Some(&mut cascade_info),
|
||||
shared_context.error_reporter.clone())
|
||||
shared_context.error_reporter.clone(),
|
||||
cascade_flags)
|
||||
}
|
||||
None => {
|
||||
cascade(shared_context.viewport_size,
|
||||
applicable_declarations,
|
||||
booleans.shareable,
|
||||
None,
|
||||
None,
|
||||
Some(&mut cascade_info),
|
||||
shared_context.error_reporter.clone())
|
||||
shared_context.error_reporter.clone(),
|
||||
cascade_flags)
|
||||
}
|
||||
};
|
||||
cascade_info.finish(self);
|
||||
|
|
|
@ -1524,6 +1524,17 @@ static CASCADE_PROPERTY: [CascadePropertyFn; ${len(data.longhands)}] = [
|
|||
% 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
|
||||
/// 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.
|
||||
///
|
||||
/// * `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.
|
||||
///
|
||||
/// * `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 is ignored.
|
||||
///
|
||||
/// * `flags`: Various flags.
|
||||
///
|
||||
/// Returns the computed values and a boolean indicating whether the result is cacheable.
|
||||
pub fn cascade(viewport_size: Size2D<Au>,
|
||||
applicable_declarations: &[ApplicableDeclarationBlock],
|
||||
shareable: bool,
|
||||
parent_style: Option<<&ComputedValues>,
|
||||
cached_style: Option<<&ComputedValues>,
|
||||
mut cascade_info: Option<<&mut CascadeInfo>,
|
||||
mut error_reporter: StdBox<ParseErrorReporter + Send>)
|
||||
mut error_reporter: StdBox<ParseErrorReporter + Send>,
|
||||
flags: CascadeFlags)
|
||||
-> (ComputedValues, bool) {
|
||||
let initial_values = ComputedValues::initial_values();
|
||||
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) {
|
||||
let style = cascade_with_cached_declarations(viewport_size,
|
||||
&applicable_declarations,
|
||||
shareable,
|
||||
flags.contains(SHAREABLE),
|
||||
parent_style,
|
||||
cached_style,
|
||||
custom_properties,
|
||||
|
@ -1591,24 +1601,35 @@ pub fn cascade(viewport_size: Size2D<Au>,
|
|||
return (style, false)
|
||||
}
|
||||
|
||||
let starting_style = if !flags.contains(INHERIT_ALL) {
|
||||
ComputedValues::new(custom_properties,
|
||||
flags.contains(SHAREABLE),
|
||||
WritingMode::empty(),
|
||||
inherited_style.root_font_size(),
|
||||
% for style_struct in data.active_style_structs():
|
||||
% if style_struct.inherited:
|
||||
inherited_style.clone_${style_struct.name_lower}(),
|
||||
% else:
|
||||
initial_values.clone_${style_struct.name_lower}(),
|
||||
% endif
|
||||
% 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: ComputedValues::new(
|
||||
custom_properties,
|
||||
shareable,
|
||||
WritingMode::empty(),
|
||||
inherited_style.root_font_size(),
|
||||
% for style_struct in data.active_style_structs():
|
||||
% if style_struct.inherited:
|
||||
inherited_style
|
||||
% else:
|
||||
initial_values
|
||||
% endif
|
||||
.clone_${style_struct.name_lower}(),
|
||||
% endfor
|
||||
),
|
||||
style: starting_style,
|
||||
};
|
||||
|
||||
// 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.
|
||||
#[cfg(feature = "servo")]
|
||||
#[inline]
|
||||
|
|
|
@ -10,7 +10,8 @@ use error_reporting::StdoutErrorReporter;
|
|||
use keyframes::KeyframesAnimation;
|
||||
use media_queries::{Device, MediaType};
|
||||
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 restyle_hints::{RestyleHint, DependencySet};
|
||||
use selector_impl::{ElementExt, TheSelectorImpl, PseudoElement};
|
||||
|
@ -252,25 +253,63 @@ impl Stylist {
|
|||
|
||||
/// Computes the style for a given "precomputed" pseudo-element, taking the
|
||||
/// 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,
|
||||
pseudo: &PseudoElement,
|
||||
parent: Option<&Arc<ComputedValues>>)
|
||||
parent: Option<&Arc<ComputedValues>>,
|
||||
inherit_all: bool)
|
||||
-> Option<Arc<ComputedValues>> {
|
||||
debug_assert!(TheSelectorImpl::pseudo_element_cascade_type(pseudo).is_precomputed());
|
||||
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, _) =
|
||||
properties::cascade(self.device.au_viewport_size(),
|
||||
&declarations, false,
|
||||
&declarations,
|
||||
parent.map(|p| &**p),
|
||||
None,
|
||||
None,
|
||||
Box::new(StdoutErrorReporter));
|
||||
Box::new(StdoutErrorReporter),
|
||||
flags);
|
||||
Some(Arc::new(computed))
|
||||
} else {
|
||||
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,
|
||||
element: &E,
|
||||
pseudo: &PseudoElement,
|
||||
|
@ -298,10 +337,12 @@ impl Stylist {
|
|||
|
||||
let (computed, _) =
|
||||
properties::cascade(self.device.au_viewport_size(),
|
||||
&declarations, false,
|
||||
Some(&**parent), None, None,
|
||||
Box::new(StdoutErrorReporter));
|
||||
|
||||
&declarations,
|
||||
Some(&**parent),
|
||||
None,
|
||||
None,
|
||||
Box::new(StdoutErrorReporter),
|
||||
CascadeFlags::empty());
|
||||
|
||||
Some(Arc::new(computed))
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ use selectors::parser::{AttrSelector, ParserContext, SelectorImpl};
|
|||
use std::fmt;
|
||||
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)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub enum PseudoElement {
|
||||
|
@ -22,6 +23,12 @@ pub enum PseudoElement {
|
|||
DetailsSummary,
|
||||
DetailsContent,
|
||||
ServoInputText,
|
||||
ServoTableWrapper,
|
||||
ServoAnonymousTableWrapper,
|
||||
ServoAnonymousTable,
|
||||
ServoAnonymousTableRow,
|
||||
ServoAnonymousTableCell,
|
||||
ServoAnonymousBlock,
|
||||
}
|
||||
|
||||
impl ToCss for PseudoElement {
|
||||
|
@ -34,6 +41,12 @@ impl ToCss for PseudoElement {
|
|||
DetailsSummary => "::-servo-details-summary",
|
||||
DetailsContent => "::-servo-details-content",
|
||||
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::DetailsSummary => PseudoElementCascadeType::Lazy,
|
||||
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
|
||||
},
|
||||
"-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(())
|
||||
};
|
||||
|
||||
|
@ -232,6 +287,12 @@ impl ServoSelectorImpl {
|
|||
fun(PseudoElement::DetailsSummary);
|
||||
fun(PseudoElement::Selection);
|
||||
fun(PseudoElement::ServoInputText);
|
||||
fun(PseudoElement::ServoTableWrapper);
|
||||
fun(PseudoElement::ServoAnonymousTableWrapper);
|
||||
fun(PseudoElement::ServoAnonymousTable);
|
||||
fun(PseudoElement::ServoAnonymousTableRow);
|
||||
fun(PseudoElement::ServoAnonymousTableCell);
|
||||
fun(PseudoElement::ServoAnonymousBlock);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -37,7 +37,7 @@ use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong};
|
|||
use style::gecko_bindings::sugar::refptr::{GeckoArcPrincipal, GeckoArcURI};
|
||||
use style::parallel;
|
||||
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::{cascade, parse_one_declaration};
|
||||
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
|
||||
let (computed, _) = cascade(Size2D::new(Au(0), Au(0)),
|
||||
&[declaration_block],
|
||||
false,
|
||||
Some(previous_style),
|
||||
None,
|
||||
None,
|
||||
Box::new(StdoutErrorReporter));
|
||||
Box::new(StdoutErrorReporter),
|
||||
CascadeFlags::empty());
|
||||
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 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())
|
||||
}
|
||||
|
||||
|
|
|
@ -171,3 +171,47 @@ svg > * {
|
|||
*|*::-servo-input-text {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[flex-flexitem-childmargin.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[flexbox_align-items-stretch-2.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[flexbox_first-line.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[flexbox_generated.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[flexbox_item-bottom-float.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[flexbox_item-top-float.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[flexbox_stf-table-caption.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[multi-line-wrap-reverse-row-reverse.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[multi-line-wrap-with-row-reverse.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[after-content-display-008.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[after-content-display-009.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[after-content-display-010.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[after-content-display-011.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[after-content-display-014.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[background-position-applies-to-001.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[background-position-applies-to-002.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[background-position-applies-to-003.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[background-position-applies-to-004.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[background-position-applies-to-007.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[background-position-applies-to-014.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[before-content-display-008.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[before-content-display-009.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[before-content-display-010.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[before-content-display-011.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[before-content-display-014.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[bidi-009b.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[block-formatting-contexts-001.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[blocks-017.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[border-applies-to-013.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[border-color-applies-to-013.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[border-top-applies-to-013.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[border-top-color-applies-to-013.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[border-top-width-applies-to-013.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[bottom-applies-to-002.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[bottom-applies-to-003.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[bottom-applies-to-004.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[c548-ln-ht-001.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +1,2 @@
|
|||
[color-applies-to-014.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
||||
disabled: https://github.com/servo/servo/pull/13870#issuecomment-255507790
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[floats-014.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[font-applies-to-007.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[font-style-applies-to-007.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[font-variant-applies-to-007.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[font-weight-applies-to-007.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[height-width-table-001.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[height-width-table-001a.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[height-width-table-001c.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[height-width-table-001d.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[leading-001.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[left-applies-to-002.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[left-applies-to-003.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[left-applies-to-004.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[margin-001.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[margin-002.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[min-width-applies-to-013.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[min-width-applies-to-014.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[min-width-tables-001.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[overflow-applies-to-001.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[overflow-applies-to-002.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[overflow-applies-to-003.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[overflow-applies-to-004.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[overflow-applies-to-005.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[overflow-applies-to-006.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[padding-001.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[padding-002.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[padding-applies-to-001.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[padding-applies-to-002.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[padding-applies-to-003.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[padding-applies-to-004.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[padding-applies-to-005.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[padding-applies-to-006.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[padding-applies-to-013.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[padding-applies-to-013a.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[padding-right-applies-to-013.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[padding-right-applies-to-014.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[position-applies-to-002.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[position-applies-to-003.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[position-applies-to-004.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue