diff --git a/components/layout/construct.rs b/components/layout/construct.rs index f9d11e89915..797f7cf0703 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -16,6 +16,7 @@ use block::BlockFlow; use context::LayoutContext; use data::{HAS_NEWLY_CONSTRUCTED_FLOW, LayoutDataWrapper}; +use flex::FlexFlow; use floats::FloatKind; use flow::{MutableFlowUtils, MutableOwnedFlowUtils}; use flow::{self, AbsoluteDescendants, Flow, ImmutableFlowUtils, IS_ABSOLUTELY_POSITIONED}; @@ -1277,6 +1278,14 @@ impl<'a> FlowConstructor<'a> { ConstructionResult::Flow(flow, AbsoluteDescendants::new()) } + /// Builds a flow for a node with 'display: flex'. + fn build_flow_for_flex(&mut self, node: &ThreadSafeLayoutNode, float_kind: Option) + -> ConstructionResult { + let fragment = self.build_fragment_for_block(node); + let flow = Arc::new(FlexFlow::from_fragment(fragment, float_kind)); + self.build_flow_for_block_like(flow, node) + } + /// Attempts to perform incremental repair to account for recent changes to this node. This /// can fail and return false, indicating that flows will need to be reconstructed. /// @@ -1518,6 +1527,13 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> { self.set_flow_construction_result(node, construction_result) } + // Flex items contribute flex flow construction results. + (display::T::flex, float_value, _) => { + let float_kind = FloatKind::from_property(float_value); + let construction_result = self.build_flow_for_flex(node, float_kind); + self.set_flow_construction_result(node, construction_result) + } + // Block flows that are not floated contribute block flow construction results. // // TODO(pcwalton): Make this only trigger for blocks and handle the other `display` diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 9cc43559bed..e49b306169f 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -13,6 +13,7 @@ use azure::azure_hl::Color; use block::BlockFlow; use context::LayoutContext; +use flex::FlexFlow; use flow::{self, BaseFlow, Flow, IS_ABSOLUTELY_POSITIONED, NEEDS_LAYER}; use flow_ref; use fragment::{CoordinateSystem, Fragment, IframeFragmentInfo, ImageFragmentInfo}; @@ -1886,6 +1887,23 @@ impl ListItemFlowDisplayListBuilding for ListItemFlow { } } +pub trait FlexFlowDisplayListBuilding { + fn build_display_list_for_flex(&mut self, + display_list: Box, + layout_context: &LayoutContext); +} + +impl FlexFlowDisplayListBuilding for FlexFlow { + fn build_display_list_for_flex(&mut self, + display_list: Box, + layout_context: &LayoutContext) { + // Draw the rest of the block. + self.as_mut_block().build_display_list_for_block(display_list, + layout_context, + BorderPaintingMode::Separate) + } +} + trait BaseFlowDisplayListBuilding { fn build_display_items_for_debugging_tint(&self, display_list: &mut DisplayList, diff --git a/components/layout/flex.rs b/components/layout/flex.rs new file mode 100644 index 00000000000..6861d6a62df --- /dev/null +++ b/components/layout/flex.rs @@ -0,0 +1,446 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Layout for elements with a CSS `display` property of `flex`. + +#![deny(unsafe_code)] + +use block::BlockFlow; +use context::LayoutContext; +use display_list_builder::FlexFlowDisplayListBuilding; +use floats::FloatKind; +use flow; +use flow::INLINE_POSITION_IS_STATIC; +use flow::IS_ABSOLUTELY_POSITIONED; +use flow::ImmutableFlowUtils; +use flow::mut_base; +use flow::{Flow, FlowClass, OpaqueFlow}; +use flow::{HAS_LEFT_FLOATED_DESCENDANTS, HAS_RIGHT_FLOATED_DESCENDANTS}; +use fragment::{Fragment, FragmentBorderBoxIterator}; +use incremental::{REFLOW, REFLOW_OUT_OF_FLOW}; +use layout_debug; +use model::{IntrinsicISizes}; +use style::computed_values::{flex_direction, float}; +use style::properties::style_structs; +use style::values::computed::LengthOrPercentageOrAuto; + +use euclid::{Point2D, Rect}; +use gfx::display_list::DisplayList; +use model::MaybeAuto; +use std::cmp::max; +use std::sync::Arc; +use style::properties::ComputedValues; +use util::geometry::Au; +use util::logical_geometry::LogicalSize; +use util::opts; + +// A mode describes which logical axis a flex axis is parallel with. +// The logical axises are inline and block, the flex axises are main and cross. +// When the flex container has flex-direction: column or flex-direction: column-reverse, the main axis +// should be block. Otherwise, it should be inline. +#[derive(Debug)] +enum Mode { + Inline, + Block +} + +/// A block with the CSS `display` property equal to `flex`. +#[derive(Debug)] +pub struct FlexFlow { + /// Data common to all block flows. + block_flow: BlockFlow, + /// The logical axis which the main axis will be parallel with. + /// The cross axis will be parallel with the opposite logical axis. + main_mode: Mode, +} + +fn flex_style(fragment: &Fragment) -> &style_structs::Flex { + fragment.style.get_flex() +} + +// TODO(zentner): This function should use flex-basis. +fn flex_item_inline_sizes(flow: &mut Flow) -> IntrinsicISizes { + let _scope = layout_debug_scope!("flex::flex_item_inline_sizes"); + debug!("flex_item_inline_sizes"); + let base = flow::mut_base(flow); + + debug!("FlexItem intrinsic inline sizes: {:?}, {:?}", + base.intrinsic_inline_sizes.minimum_inline_size, + base.intrinsic_inline_sizes.preferred_inline_size); + + IntrinsicISizes { + minimum_inline_size: base.intrinsic_inline_sizes.minimum_inline_size, + preferred_inline_size: base.intrinsic_inline_sizes.preferred_inline_size, + } +} + +impl FlexFlow { + pub fn from_fragment(fragment: Fragment, + flotation: Option) + -> FlexFlow { + + let main_mode = match flex_style(&fragment).flex_direction { + flex_direction::T::row_reverse => Mode::Inline, + flex_direction::T::row => Mode::Inline, + flex_direction::T::column_reverse => Mode::Block, + flex_direction::T::column => Mode::Block + }; + + let this = FlexFlow { + block_flow: BlockFlow::from_fragment(fragment, flotation), + main_mode: main_mode + }; + + this + } + + // TODO(zentner): This function should use flex-basis. + // Currently, this is the core of BlockFlow::bubble_inline_sizes() with all float logic + // stripped out, and max replaced with union_nonbreaking_inline. + fn inline_mode_bubble_inline_sizes(&mut self) { + let fixed_width = match self.block_flow.fragment.style().get_box().width { + LengthOrPercentageOrAuto::Length(_) => true, + _ => false, + }; + + let mut computation = self.block_flow.fragment.compute_intrinsic_inline_sizes(); + if !fixed_width { + for kid in self.block_flow.base.child_iter() { + let is_absolutely_positioned = + flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED); + if !is_absolutely_positioned { + computation.union_nonbreaking_inline(&flex_item_inline_sizes(kid)); + } + } + } + self.block_flow.base.intrinsic_inline_sizes = computation.finish(); + } + + // TODO(zentner): This function should use flex-basis. + // Currently, this is the core of BlockFlow::bubble_inline_sizes() with all float logic + // stripped out. + fn block_mode_bubble_inline_sizes(&mut self) { + let fixed_width = match self.block_flow.fragment.style().get_box().width { + LengthOrPercentageOrAuto::Length(_) => true, + _ => false, + }; + + let mut computation = self.block_flow.fragment.compute_intrinsic_inline_sizes(); + if !fixed_width { + for kid in self.block_flow.base.child_iter() { + let is_absolutely_positioned = + flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED); + let child_base = flow::mut_base(kid); + if !is_absolutely_positioned { + computation.content_intrinsic_sizes.minimum_inline_size = + max(computation.content_intrinsic_sizes.minimum_inline_size, + child_base.intrinsic_inline_sizes.minimum_inline_size); + + computation.content_intrinsic_sizes.preferred_inline_size = + max(computation.content_intrinsic_sizes.preferred_inline_size, + child_base.intrinsic_inline_sizes.preferred_inline_size); + } + } + } + self.block_flow.base.intrinsic_inline_sizes = computation.finish(); + } + + // TODO(zentner): This function needs to be radically different for multi-line flexbox. + // Currently, this is the core of BlockFlow::propagate_assigned_inline_size_to_children() with + // all float and table logic stripped out. + fn block_mode_assign_inline_sizes(&mut self, + _layout_context: &LayoutContext, + inline_start_content_edge: Au, + inline_end_content_edge: Au, + content_inline_size: Au) { + let _scope = layout_debug_scope!("flex::block_mode_assign_inline_sizes"); + debug!("block_mode_assign_inline_sizes"); + + // Calculate non-auto block size to pass to children. + let content_block_size = self.block_flow.fragment.style().content_block_size(); + + let explicit_content_size = + match (content_block_size, self.block_flow.base.block_container_explicit_block_size) { + (LengthOrPercentageOrAuto::Percentage(percent), Some(container_size)) => { + Some(container_size.scale_by(percent)) + } + (LengthOrPercentageOrAuto::Percentage(_), None) | + (LengthOrPercentageOrAuto::Auto, _) => None, + (LengthOrPercentageOrAuto::Length(length), _) => Some(length), + }; + + // FIXME (mbrubeck): Get correct mode for absolute containing block + let containing_block_mode = self.block_flow.base.writing_mode; + + let mut iterator = self.block_flow.base.child_iter().enumerate().peekable(); + while let Some((_, kid)) = iterator.next() { + { + let kid_base = flow::mut_base(kid); + kid_base.block_container_explicit_block_size = explicit_content_size; + } + + // 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. + let kid_mode = flow::base(kid).writing_mode; + { + let kid_base = flow::mut_base(kid); + if kid_base.flags.contains(INLINE_POSITION_IS_STATIC) { + kid_base.position.start.i = + if kid_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 = content_inline_size; + kid_base.block_container_writing_mode = containing_block_mode; + } + } + } + + // TODO(zentner): This function should actually flex elements! + // Currently, this is the core of InlineFlow::propagate_assigned_inline_size_to_children() with + // fragment logic stripped out. + fn inline_mode_assign_inline_sizes(&mut self, + _layout_context: &LayoutContext, + inline_start_content_edge: Au, + _inline_end_content_edge: Au, + content_inline_size: Au) { + let _scope = layout_debug_scope!("flex::inline_mode_assign_inline_sizes"); + debug!("inline_mode_assign_inline_sizes"); + + debug!("content_inline_size = {:?}", content_inline_size); + debug!("child_count = {:?}", ImmutableFlowUtils::child_count(self as &Flow) as i32); + let even_content_inline_size = content_inline_size / ImmutableFlowUtils::child_count(self as &Flow) as i32; + + let inline_size = self.block_flow.base.block_container_inline_size; + let container_mode = self.block_flow.base.block_container_writing_mode; + self.block_flow.base.position.size.inline = inline_size; + + let block_container_explicit_block_size = self.block_flow.base.block_container_explicit_block_size; + let mut inline_child_start = inline_start_content_edge; + for kid in self.block_flow.base.child_iter() { + let kid_base = flow::mut_base(kid); + + kid_base.block_container_inline_size = even_content_inline_size; + kid_base.block_container_writing_mode = container_mode; + kid_base.block_container_explicit_block_size = block_container_explicit_block_size; + kid_base.position.start.i = inline_child_start; + inline_child_start = inline_child_start + even_content_inline_size; + } + } + + // TODO(zentner): This function should actually flex elements! + fn block_mode_assign_block_size<'a>(&mut self, layout_context: &'a LayoutContext<'a>) { + self.block_flow.assign_block_size(layout_context) + } + + // TODO(zentner): This function should actually flex elements! + // Currently, this is the core of TableRowFlow::assign_block_size() with + // float related logic stripped out. + fn inline_mode_assign_block_size<'a>(&mut self, layout_context: &'a LayoutContext<'a>) { + let _scope = layout_debug_scope!("flex::inline_mode_assign_block_size"); + + let mut max_block_size = Au(0); + let thread_id = self.block_flow.base.thread_id; + for kid in self.block_flow.base.child_iter() { + kid.assign_block_size_for_inorder_child_if_necessary(layout_context, thread_id); + + { + let child_fragment = &mut kid.as_mut_block().fragment; + // TODO: Percentage block-size + let child_specified_block_size = + MaybeAuto::from_style(child_fragment.style().content_block_size(), + Au(0)).specified_or_zero(); + max_block_size = + max(max_block_size, + child_specified_block_size + + child_fragment.border_padding.block_start_end()); + } + let child_node = flow::mut_base(kid); + child_node.position.start.b = Au(0); + max_block_size = max(max_block_size, child_node.position.size.block); + } + + let mut block_size = max_block_size; + // TODO: Percentage block-size + + block_size = match MaybeAuto::from_style(self.block_flow + .fragment + .style() + .content_block_size(), + Au(0)) { + MaybeAuto::Auto => block_size, + MaybeAuto::Specified(value) => max(value, block_size), + }; + + // Assign the block-size of own fragment + let mut position = self.block_flow.fragment.border_box; + position.size.block = block_size; + self.block_flow.fragment.border_box = position; + self.block_flow.base.position.size.block = block_size; + + // Assign the block-size of kid fragments, which is the same value as own block-size. + for kid in self.block_flow.base.child_iter() { + { + let kid_fragment = &mut kid.as_mut_block().fragment; + let mut position = kid_fragment.border_box; + position.size.block = block_size; + kid_fragment.border_box = position; + } + + // Assign the child's block size. + flow::mut_base(kid).position.size.block = block_size + } + } +} + +impl Flow for FlexFlow { + fn class(&self) -> FlowClass { + FlowClass::Flex + } + + fn as_mut_block<'a>(&'a mut self) -> &'a mut BlockFlow { + &mut self.block_flow + } + + fn bubble_inline_sizes(&mut self) { + let _scope = layout_debug_scope!("flex::bubble_inline_sizes {:x}", + self.block_flow.base.debug_id()); + + // Flexbox Section 9.0: Generate anonymous flex items: + // This part was handled in the flow constructor. + + // Flexbox Section 9.1: Re-order the flex items (and any absolutely positioned flex + // container children) according to their order. + // TODO(zentner): We need to re-order the items at some point. However, all the operations + // here ignore order, so we can afford to do it later, if necessary. + + // `flex item`s (our children) cannot be floated. Furthermore, they all establish BFC's. + // Therefore, we do not have to handle any floats here. + + let mut flags = self.block_flow.base.flags; + flags.remove(HAS_LEFT_FLOATED_DESCENDANTS); + flags.remove(HAS_RIGHT_FLOATED_DESCENDANTS); + + match self.main_mode { + Mode::Inline => self.inline_mode_bubble_inline_sizes(), + Mode::Block => self.block_mode_bubble_inline_sizes() + } + + // Although our children can't be floated, we can. + match self.block_flow.fragment.style().get_box().float { + float::T::none => {} + float::T::left => flags.insert(HAS_LEFT_FLOATED_DESCENDANTS), + float::T::right => flags.insert(HAS_RIGHT_FLOATED_DESCENDANTS), + } + self.block_flow.base.flags = flags + } + + fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) { + let _scope = layout_debug_scope!("flex::assign_inline_sizes {:x}", self.block_flow.base.debug_id()); + debug!("assign_inline_sizes"); + + if !self.block_flow.base.restyle_damage.intersects(REFLOW_OUT_OF_FLOW | REFLOW) { + return + } + + // Our inline-size was set to the inline-size of the containing block by the flow's parent. + // Now compute the real value. + let containing_block_inline_size = self.block_flow.base.block_container_inline_size; + self.block_flow.compute_used_inline_size(layout_context, containing_block_inline_size); + if self.block_flow.base.flags.is_float() { + self.block_flow.float.as_mut().unwrap().containing_inline_size = containing_block_inline_size + } + + // Move in from the inline-start border edge. + let inline_start_content_edge = self.block_flow.fragment.border_box.start.i + + self.block_flow.fragment.border_padding.inline_start; + + debug!("inline_start_content_edge = {:?}", inline_start_content_edge); + + let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end(); + + // Distance from the inline-end margin edge to the inline-end content edge. + let inline_end_content_edge = + self.block_flow.fragment.margin.inline_end + + self.block_flow.fragment.border_padding.inline_end; + + debug!("padding_and_borders = {:?}", padding_and_borders); + debug!("self.block_flow.fragment.border_box.size.inline = {:?}", + self.block_flow.fragment.border_box.size.inline); + let content_inline_size = self.block_flow.fragment.border_box.size.inline - padding_and_borders; + + match self.main_mode { + Mode::Inline => + self.inline_mode_assign_inline_sizes(layout_context, + inline_start_content_edge, + inline_end_content_edge, + content_inline_size), + Mode::Block => + self.block_mode_assign_inline_sizes(layout_context, + inline_start_content_edge, + inline_end_content_edge, + content_inline_size) + } + } + + fn assign_block_size<'a>(&mut self, layout_context: &'a LayoutContext<'a>) { + self.block_flow.assign_block_size(layout_context); + match self.main_mode { + Mode::Inline => + self.inline_mode_assign_block_size(layout_context), + Mode::Block => + self.block_mode_assign_block_size(layout_context) + } + } + + fn compute_absolute_position(&mut self, layout_context: &LayoutContext) { + self.block_flow.compute_absolute_position(layout_context) + } + + fn place_float_if_applicable<'a>(&mut self, layout_context: &'a LayoutContext<'a>) { + self.block_flow.place_float_if_applicable(layout_context) + } + + fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) { + self.block_flow.update_late_computed_inline_position_if_necessary(inline_position) + } + + fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) { + self.block_flow.update_late_computed_block_position_if_necessary(block_position) + } + + fn build_display_list(&mut self, layout_context: &LayoutContext) { + self.build_display_list_for_flex(Box::new(DisplayList::new()), layout_context); + + if opts::get().validate_display_list_geometry { + self.block_flow.base.validate_display_list_geometry(); + } + } + + fn repair_style(&mut self, new_style: &Arc) { + self.block_flow.repair_style(new_style) + } + + fn compute_overflow(&self) -> Rect { + self.block_flow.compute_overflow() + } + + fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize { + self.block_flow.generated_containing_block_size(flow) + } + + fn iterate_through_fragment_border_boxes(&self, + iterator: &mut FragmentBorderBoxIterator, + level: i32, + stacking_context_position: &Point2D) { + self.block_flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position); + } + + fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) { + self.block_flow.mutate_fragments(mutator); + } +} diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 66ee466d450..6d319bdd991 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -517,6 +517,7 @@ pub enum FlowClass { TableCaption, TableCell, Multicol, + Flex, } /// A top-down traversal. @@ -1173,6 +1174,8 @@ impl<'a> ImmutableFlowUtils for &'a Flow { 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 } } @@ -1210,6 +1213,15 @@ impl<'a> ImmutableFlowUtils for &'a Flow { let hide = node.style().get_inheritedtable().empty_cells == empty_cells::T::hide; Arc::new(TableCellFlow::from_node_fragment_and_visibility_flag(node, fragment, !hide)) }, + FlowClass::Flex => { + let fragment = + Fragment::from_opaque_node_and_style(node.opaque(), + PseudoElementType::Normal, + style, + node.restyle_damage(), + SpecificFragmentInfo::Generic); + Arc::new(BlockFlow::from_fragment(fragment, None)) + }, _ => { panic!("no need to generate a missing child") } diff --git a/components/layout/lib.rs b/components/layout/lib.rs index 438ac1edd87..a524236fac0 100644 --- a/components/layout/lib.rs +++ b/components/layout/lib.rs @@ -72,6 +72,7 @@ pub mod construct; pub mod context; pub mod data; pub mod display_list_builder; +pub mod flex; pub mod floats; pub mod flow; pub mod flow_list; diff --git a/components/script/dom/webidls/CSSStyleDeclaration.webidl b/components/script/dom/webidls/CSSStyleDeclaration.webidl index 3444a460441..014e12740bf 100644 --- a/components/script/dom/webidls/CSSStyleDeclaration.webidl +++ b/components/script/dom/webidls/CSSStyleDeclaration.webidl @@ -204,4 +204,6 @@ partial interface CSSStyleDeclaration { [SetterThrows, TreatNullAs=EmptyString] attribute DOMString transitionTimingFunction; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString transitionProperty; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString transitionDelay; + + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString flexDirection; }; diff --git a/components/style/properties.mako.rs b/components/style/properties.mako.rs index ea6ab4a3378..33508b4220a 100644 --- a/components/style/properties.mako.rs +++ b/components/style/properties.mako.rs @@ -407,15 +407,66 @@ pub mod longhands { ${new_style_struct("Box", is_inherited=False)} // TODO(SimonSapin): don't parse `inline-table`, since we don't support it - <%self:single_keyword_computed name="display" - custom_cascade="True" - values="inline block inline-block - table inline-table table-row-group table-header-group table-footer-group - table-row table-column-group table-column table-cell table-caption - list-item - none"> + <%self:longhand name="display" custom_cascade="True"> + <% + values = """inline block inline-block + table inline-table table-row-group table-header-group table-footer-group + table-row table-column-group table-column table-cell table-caption + list-item flex + none + """.split() + experimental_values = set("flex".split()) + %> + pub use self::computed_value::T as SpecifiedValue; use values::computed::Context; + pub mod computed_value { + #[allow(non_camel_case_types)] + #[derive(Clone, Eq, PartialEq, Copy, Hash, RustcEncodable, Debug, HeapSizeOf)] + #[derive(Deserialize, Serialize)] + pub enum T { + % for value in values: + ${to_rust_ident(value)}, + % endfor + } + + impl ::cssparser::ToCss for T { + fn to_css(&self, dest: &mut W) -> ::std::fmt::Result + where W: ::std::fmt::Write { + match self { + % for value in values: + &T::${to_rust_ident(value)} => dest.write_str("${value}"), + % endfor + } + } + } + } + #[inline] pub fn get_initial_value() -> computed_value::T { + computed_value::T::${to_rust_ident(values[0])} + } + pub fn parse(_context: &ParserContext, input: &mut Parser) + -> Result { + match_ignore_ascii_case! { try!(input.expect_ident()), + % for value in values[:-1]: + "${value}" => { + % if value in experimental_values: + if !::util::opts::experimental_enabled() { return Err(()) } + % endif + Ok(computed_value::T::${to_rust_ident(value)}) + }, + % endfor + % for value in values[-1:]: + "${value}" => { + % if value in experimental_values: + if !::util::opts::experimental_enabled() { return Err(()) } + % endif + Ok(computed_value::T::${to_rust_ident(value)}) + } + % endfor + _ => Err(()) + } + } + impl ToComputedValue for SpecifiedValue { type ComputedValue = computed_value::T; @@ -457,7 +508,7 @@ pub mod longhands { longhands::_servo_text_decorations_in_effect::derive_from_display(*computed_value, &context); } - + ${single_keyword("position", "static absolute relative fixed")} ${single_keyword("float", "none left right")} @@ -4746,6 +4797,14 @@ pub mod longhands { pub use properties::longhands::transition_duration::{get_initial_single_value}; pub use properties::longhands::transition_duration::{get_initial_value, parse, parse_one}; + + // CSS Flexible Box Layout Module Level 1 + // http://www.w3.org/TR/css3-flexbox/ + + ${new_style_struct("Flex", is_inherited=False)} + + // Flex container properties + ${single_keyword("flex-direction", "row row-reverse column column-reverse", experimental=True)} } diff --git a/tests/ref/basic.list b/tests/ref/basic.list index 14d9ae7ab9d..40c2e3bd3af 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -98,6 +98,8 @@ flaky_cpu == append_style_a.html append_style_b.html == first_child_pseudo_a.html first_child_pseudo_b.html == first_of_type_pseudo_a.html first_of_type_pseudo_b.html == fixed_width_overrides_child_intrinsic_width_a.html fixed_width_overrides_child_intrinsic_width_ref.html +experimental == flex_column_direction.html flex_column_direction_ref.html +experimental == flex_row_direction.html flex_row_direction_ref.html == float_clearance_a.html float_clearance_ref.html == float_clearance_intrinsic_width_a.html float_clearance_intrinsic_width_ref.html == float_intrinsic_height.html float_intrinsic_height_ref.html diff --git a/tests/ref/flex_column_direction.html b/tests/ref/flex_column_direction.html new file mode 100644 index 00000000000..c63bc6b1ee7 --- /dev/null +++ b/tests/ref/flex_column_direction.html @@ -0,0 +1,37 @@ + + + + + CSS Test: flex container should layout flex items horizontally + + + + + +
+
+
+
+ + diff --git a/tests/ref/flex_column_direction_ref.html b/tests/ref/flex_column_direction_ref.html new file mode 100644 index 00000000000..3a4c2e29307 --- /dev/null +++ b/tests/ref/flex_column_direction_ref.html @@ -0,0 +1,32 @@ + + + + + CSS Reftest Reference + + + + +
+
+
+
+ + diff --git a/tests/ref/flex_row_direction.html b/tests/ref/flex_row_direction.html new file mode 100644 index 00000000000..68689e5eca9 --- /dev/null +++ b/tests/ref/flex_row_direction.html @@ -0,0 +1,34 @@ + + + + + CSS Test: flex container should layout flex items horizontally + + + + + +
+
+
+
+ + diff --git a/tests/ref/flex_row_direction_ref.html b/tests/ref/flex_row_direction_ref.html new file mode 100644 index 00000000000..dfb59df1136 --- /dev/null +++ b/tests/ref/flex_row_direction_ref.html @@ -0,0 +1,34 @@ + + + + + CSS Reftest Reference + + + + +
+
+
+
+ +