mirror of
https://github.com/servo/servo.git
synced 2025-08-06 22:15:33 +01:00
Auto merge of #7312 - servo:flex-experimental, r=SimonSapin
Initial (very rudimentary) flexbox implementation. This is #7154 with two additional commits (that I did rather than ask @zentner-kyle to do it because it was a bit tricky.) r? @pcwalton for the last two commit r=me+pcwalton in #7154 for earlier commits. <!-- Reviewable:start --> [<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/7312) <!-- Reviewable:end -->
This commit is contained in:
commit
5e83a3f0a3
12 changed files with 701 additions and 8 deletions
|
@ -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<FloatKind>)
|
||||
-> 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`
|
||||
|
|
|
@ -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<DisplayList>,
|
||||
layout_context: &LayoutContext);
|
||||
}
|
||||
|
||||
impl FlexFlowDisplayListBuilding for FlexFlow {
|
||||
fn build_display_list_for_flex(&mut self,
|
||||
display_list: Box<DisplayList>,
|
||||
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,
|
||||
|
|
446
components/layout/flex.rs
Normal file
446
components/layout/flex.rs
Normal file
|
@ -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<FloatKind>)
|
||||
-> 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<ComputedValues>) {
|
||||
self.block_flow.repair_style(new_style)
|
||||
}
|
||||
|
||||
fn compute_overflow(&self) -> Rect<Au> {
|
||||
self.block_flow.compute_overflow()
|
||||
}
|
||||
|
||||
fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> {
|
||||
self.block_flow.generated_containing_block_size(flow)
|
||||
}
|
||||
|
||||
fn iterate_through_fragment_border_boxes(&self,
|
||||
iterator: &mut FragmentBorderBoxIterator,
|
||||
level: i32,
|
||||
stacking_context_position: &Point2D<Au>) {
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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<W>(&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<SpecifiedValue, ()> {
|
||||
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);
|
||||
}
|
||||
</%self:single_keyword_computed>
|
||||
</%self:longhand>
|
||||
|
||||
${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};
|
||||
</%self:longhand>
|
||||
|
||||
// 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)}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
37
tests/ref/flex_column_direction.html
Normal file
37
tests/ref/flex_column_direction.html
Normal file
|
@ -0,0 +1,37 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>CSS Test: flex container should layout flex items horizontally</title>
|
||||
<link rel="author" title="Kyle Zentner" href="mailto:zentner.kyle@gmail.com">
|
||||
<link rel="help" href="https://drafts.csswg.org/css-flexbox/">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
.flexbox {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 300px;
|
||||
}
|
||||
.first-item {
|
||||
background: orange;
|
||||
height: 150px;
|
||||
width: 100px;
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
.second-item {
|
||||
background: blue;
|
||||
height: 150px;
|
||||
width: 100px;
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="flexbox">
|
||||
<div class="first-item"></div>
|
||||
<div class="second-item"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
32
tests/ref/flex_column_direction_ref.html
Normal file
32
tests/ref/flex_column_direction_ref.html
Normal file
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>CSS Reftest Reference</title>
|
||||
<link rel="author" title="Kyle Zentner" href="mailto:zentner.kyle@gmail.com">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
.flexbox {
|
||||
height: 300px;
|
||||
}
|
||||
.first-item {
|
||||
background: orange;
|
||||
width: 100px;
|
||||
height: 150px;
|
||||
}
|
||||
.second-item {
|
||||
background: blue;
|
||||
width: 100px;
|
||||
height: 150px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="flexbox">
|
||||
<div class="first-item"></div>
|
||||
<div class="second-item"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
34
tests/ref/flex_row_direction.html
Normal file
34
tests/ref/flex_row_direction.html
Normal file
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>CSS Test: flex container should layout flex items horizontally</title>
|
||||
<link rel="author" title="Kyle Zentner" href="mailto:zentner.kyle@gmail.com">
|
||||
<link rel="help" href="https://drafts.csswg.org/css-flexbox/">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
.flexbox {
|
||||
display: flex;
|
||||
width: 300px;
|
||||
}
|
||||
.first-item {
|
||||
background: orange;
|
||||
height: 100px;
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
.second-item {
|
||||
background: blue;
|
||||
height: 100px;
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="flexbox">
|
||||
<div class="first-item"></div>
|
||||
<div class="second-item"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
34
tests/ref/flex_row_direction_ref.html
Normal file
34
tests/ref/flex_row_direction_ref.html
Normal file
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>CSS Reftest Reference</title>
|
||||
<link rel="author" title="Kyle Zentner" href="mailto:zentner.kyle@gmail.com">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
.flexbox {
|
||||
width: 300px;
|
||||
}
|
||||
.first-item {
|
||||
background: orange;
|
||||
height: 100px;
|
||||
width: 150px;
|
||||
float: left;
|
||||
}
|
||||
.second-item {
|
||||
background: blue;
|
||||
height: 100px;
|
||||
width: 150px;
|
||||
float: left;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="flexbox">
|
||||
<div class="first-item"></div>
|
||||
<div class="second-item"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Add a link
Reference in a new issue