Auto merge of #26755 - servo:flexbox, r=nox

Add Layout 2020 box tree support for Flexbox, behind a pref

CC https://github.com/servo/servo/issues/26639

Layout support will come in future PRs. This one generates a zero-size fragment with no content.
This commit is contained in:
bors-servo 2020-06-04 07:20:24 -04:00 committed by GitHub
commit 3d6fed85ff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 395 additions and 45 deletions

View file

@ -454,6 +454,9 @@ mod gen {
columns: {
enabled: bool,
},
flexbox: {
enabled: bool,
},
#[serde(default = "default_layout_threads")]
threads: i64,
viewport: {

View file

@ -64,14 +64,14 @@ where
&mut self,
node: Node,
text: Cow<'dom, str>,
parent_style: &ServoArc<ComputedValues>,
parent_style: ServoArc<ComputedValues>,
);
/// Or pseudo-element
fn handle_element(
&mut self,
node: Node,
style: &ServoArc<ComputedValues>,
style: ServoArc<ComputedValues>,
display: DisplayGeneratingBox,
contents: Contents,
box_slot: BoxSlot<'dom>,
@ -89,7 +89,7 @@ fn traverse_children_of<'dom, Node>(
for child in iter_child_nodes(parent_element) {
if let Some(contents) = child.as_text() {
handler.handle_text(child, contents, &child.style(context));
handler.handle_text(child, contents, child.style(context));
} else if child.is_element() {
traverse_element(child, context, handler);
}
@ -122,7 +122,7 @@ fn traverse_element<'dom, Node>(
Display::GeneratingBox(display) => {
let contents = replaced.map_or(Contents::OfElement, Contents::Replaced);
let box_slot = element.element_box_slot();
handler.handle_element(element, &style, display, contents, box_slot);
handler.handle_element(element, style, display, contents, box_slot);
},
}
}
@ -148,7 +148,7 @@ fn traverse_pseudo_element<'dom, Node>(
let items = generate_pseudo_element_content(&style, element, context);
let box_slot = element.pseudo_element_box_slot(which);
let contents = Contents::OfPseudoElement(items);
handler.handle_element(element, &style, display, contents, box_slot);
handler.handle_element(element, style, display, contents, box_slot);
},
}
} else {
@ -169,7 +169,7 @@ fn traverse_pseudo_element_contents<'dom, Node>(
for item in items {
match item {
PseudoElementContentItem::Text(text) => {
handler.handle_text(node, text.into(), pseudo_element_style)
handler.handle_text(node, text.into(), pseudo_element_style.clone())
},
PseudoElementContentItem::Replaced(contents) => {
let item_style = anonymous_style.get_or_insert_with(|| {
@ -193,7 +193,7 @@ fn traverse_pseudo_element_contents<'dom, Node>(
);
handler.handle_element(
node,
item_style,
item_style.clone(),
display_inline,
Contents::Replaced(contents),
// We dont keep pointers to boxes generated by contents of pseudo-elements

View file

@ -3,6 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::cell::ArcRefCell;
use crate::flexbox::FlexLevelBox;
use crate::flow::inline::InlineLevelBox;
use crate::flow::BlockLevelBox;
@ -17,4 +18,5 @@ pub(super) enum LayoutBox {
DisplayContents,
BlockLevel(ArcRefCell<BlockLevelBox>),
InlineLevel(ArcRefCell<InlineLevelBox>),
FlexLevel(ArcRefCell<FlexLevelBox>),
}

View file

@ -0,0 +1,261 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
use crate::dom_traversal::{BoxSlot, Contents, NodeExt, NonReplacedContents, TraversalHandler};
use crate::element_data::LayoutBox;
use crate::formatting_contexts::{IndependentFormattingContext, IndependentLayout};
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
use crate::sizing::{BoxContentSizes, ContentSizes, ContentSizesRequest};
use crate::style_ext::DisplayGeneratingBox;
use crate::ContainingBlock;
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use servo_arc::Arc;
use std::borrow::Cow;
use style::properties::ComputedValues;
use style::values::computed::Length;
use style::values::specified::text::TextDecorationLine;
use style::Zero;
// FIXME: `min-width: auto` is not zero: https://drafts.csswg.org/css-flexbox/#min-size-auto
#[derive(Debug, Serialize)]
pub(crate) struct FlexContainer {
children: Vec<ArcRefCell<FlexLevelBox>>,
}
#[derive(Debug, Serialize)]
pub(crate) enum FlexLevelBox {
FlexItem(IndependentFormattingContext),
OutOfFlowAbsolutelyPositionedBox(Arc<AbsolutelyPositionedBox>),
}
impl FlexContainer {
pub fn construct<'dom>(
context: &LayoutContext,
node: impl NodeExt<'dom>,
style: &Arc<ComputedValues>,
contents: NonReplacedContents,
content_sizes: ContentSizesRequest,
propagated_text_decoration_line: TextDecorationLine,
) -> (Self, BoxContentSizes) {
let text_decoration_line =
propagated_text_decoration_line | style.clone_text_decoration_line();
let mut builder = FlexContainerBuilder {
context,
node,
style,
text_decoration_line,
contiguous_text_runs: Vec::new(),
jobs: Vec::new(),
has_text_runs: false,
};
contents.traverse(context, node, style, &mut builder);
let content_sizes = content_sizes.compute(|| {
// FIXME
ContentSizes::zero()
});
(builder.finish(), content_sizes)
}
}
/// https://drafts.csswg.org/css-flexbox/#flex-items
struct FlexContainerBuilder<'a, 'dom, Node> {
context: &'a LayoutContext<'a>,
node: Node,
style: &'a Arc<ComputedValues>,
text_decoration_line: TextDecorationLine,
contiguous_text_runs: Vec<TextRun<'dom, Node>>,
/// To be run in parallel with rayon in `finish`
jobs: Vec<FlexLevelJob<'dom, Node>>,
has_text_runs: bool,
}
enum FlexLevelJob<'dom, Node> {
/// Or pseudo-element
Element {
node: Node,
style: Arc<ComputedValues>,
display: DisplayGeneratingBox,
contents: Contents,
box_slot: BoxSlot<'dom>,
},
TextRuns(Vec<TextRun<'dom, Node>>),
}
struct TextRun<'dom, Node> {
node: Node,
text: Cow<'dom, str>,
parent_style: Arc<ComputedValues>,
}
impl<'a, 'dom, Node: 'dom> TraversalHandler<'dom, Node> for FlexContainerBuilder<'a, 'dom, Node>
where
Node: NodeExt<'dom>,
{
fn handle_text(&mut self, node: Node, text: Cow<'dom, str>, parent_style: Arc<ComputedValues>) {
self.contiguous_text_runs.push(TextRun {
node,
text,
parent_style,
})
}
/// Or pseudo-element
fn handle_element(
&mut self,
node: Node,
style: Arc<ComputedValues>,
display: DisplayGeneratingBox,
contents: Contents,
box_slot: BoxSlot<'dom>,
) {
// FIXME: are text runs considered "contiguous" if they are only separated
// by an out-of-flow abspos element?
// (That is, are they wrapped in the same anonymous flex item, or each its own?)
self.wrap_any_text_in_anonymous_block_container();
self.jobs.push(FlexLevelJob::Element {
node,
style,
display,
contents,
box_slot,
})
}
}
/// https://drafts.csswg.org/css-text/#white-space
fn is_only_document_white_space<Node>(run: &TextRun<'_, Node>) -> bool {
// FIXME: is this the right definition? See
// https://github.com/w3c/csswg-drafts/issues/5146
// https://github.com/w3c/csswg-drafts/issues/5147
run.text
.bytes()
.all(|byte| matches!(byte, b' ' | b'\n' | b'\t'))
}
impl<'a, 'dom, Node: 'dom> FlexContainerBuilder<'a, 'dom, Node>
where
Node: NodeExt<'dom>,
{
fn wrap_any_text_in_anonymous_block_container(&mut self) {
let runs = std::mem::take(&mut self.contiguous_text_runs);
if runs.iter().all(is_only_document_white_space) {
// There is no text run, or they all only contain document white space characters
} else {
self.jobs.push(FlexLevelJob::TextRuns(runs));
self.has_text_runs = true;
}
}
fn finish(mut self) -> FlexContainer {
self.wrap_any_text_in_anonymous_block_container();
let anonymous_style = if self.has_text_runs {
Some(
self.context
.shared_context()
.stylist
.style_for_anonymous::<Node::ConcreteElement>(
&self.context.shared_context().guards,
&style::selector_parser::PseudoElement::ServoText,
self.style,
),
)
} else {
None
};
let mut children = std::mem::take(&mut self.jobs)
.into_par_iter()
.map(|job| match job {
FlexLevelJob::TextRuns(runs) => ArcRefCell::new(FlexLevelBox::FlexItem(
IndependentFormattingContext::construct_for_text_runs(
self.context,
self.node,
anonymous_style.clone().unwrap(),
runs.into_iter().map(|run| crate::flow::inline::TextRun {
tag: run.node.as_opaque(),
text: run.text.into(),
parent_style: run.parent_style,
}),
ContentSizesRequest::None, // FIXME: request sizes when we start using them
self.text_decoration_line,
),
)),
FlexLevelJob::Element {
node,
style,
display,
contents,
box_slot,
} => {
let display_inside = match display {
DisplayGeneratingBox::OutsideInside { inside, .. } => inside,
};
let box_ = if style.get_box().position.is_absolutely_positioned() {
// https://drafts.csswg.org/css-flexbox/#abspos-items
ArcRefCell::new(FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(Arc::new(
AbsolutelyPositionedBox::construct(
self.context,
node,
style.clone(),
display_inside,
contents,
),
)))
} else {
ArcRefCell::new(FlexLevelBox::FlexItem(
IndependentFormattingContext::construct(
self.context,
node,
style.clone(),
display_inside,
contents,
ContentSizesRequest::None, // FIXME: request sizes when we start using them
self.text_decoration_line,
),
))
};
box_slot.set(LayoutBox::FlexLevel(box_.clone()));
box_
},
})
.collect::<Vec<_>>();
// https://drafts.csswg.org/css-flexbox/#order-modified-document-order
children.sort_by_key(|child| match &*child.borrow() {
FlexLevelBox::FlexItem(item) => item.style.clone_order(),
// “Absolutely-positioned children of a flex container are treated
// as having order: 0 for the purpose of determining their painting order
// relative to flex items.”
FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => 0,
});
FlexContainer { children }
}
}
impl FlexContainer {
pub(crate) fn layout(
&self,
layout_context: &LayoutContext,
positioning_context: &mut PositioningContext,
containing_block: &ContainingBlock,
tree_rank: usize,
) -> IndependentLayout {
// FIXME
let _ = layout_context;
let _ = positioning_context;
let _ = containing_block;
let _ = tree_rank;
IndependentLayout {
fragments: Vec::new(),
content_block_size: Length::zero(),
}
}
}

View file

@ -47,6 +47,30 @@ impl BlockFormattingContext {
};
(bfc, inline_content_sizes)
}
pub fn construct_for_text_runs<'dom>(
context: &LayoutContext,
runs: impl Iterator<Item = TextRun>,
content_sizes: ContentSizesRequest,
text_decoration_line: TextDecorationLine,
) -> (Self, BoxContentSizes) {
// FIXME: do white space collapsing
let inline_level_boxes = runs
.map(|run| ArcRefCell::new(InlineLevelBox::TextRun(run)))
.collect();
let ifc = InlineFormattingContext {
inline_level_boxes,
text_decoration_line,
};
let content_sizes = content_sizes.compute(|| ifc.inline_content_sizes(context));
let contents = BlockContainer::InlineFormattingContext(ifc);
let bfc = Self {
contents,
contains_floats: false,
};
(bfc, content_sizes)
}
}
struct BlockLevelJob<'dom, Node> {
@ -249,7 +273,7 @@ where
fn handle_element(
&mut self,
node: Node,
style: &Arc<ComputedValues>,
style: Arc<ComputedValues>,
display: DisplayGeneratingBox,
contents: Contents,
box_slot: BoxSlot<'dom>,
@ -265,22 +289,12 @@ where
// https://drafts.csswg.org/css2/visuren.html#dis-pos-flo
if box_style.position.is_absolutely_positioned() {
self.handle_absolutely_positioned_element(
node,
style.clone(),
inside,
contents,
box_slot,
node, style, inside, contents, box_slot,
)
} else if box_style.float.is_floating() {
self.handle_float_element(node, style.clone(), inside, contents, box_slot)
self.handle_float_element(node, style, inside, contents, box_slot)
} else {
self.handle_block_level_element(
node,
style.clone(),
inside,
contents,
box_slot,
)
self.handle_block_level_element(node, style, inside, contents, box_slot)
}
},
},
@ -291,7 +305,7 @@ where
&mut self,
node: Node,
input: Cow<'dom, str>,
parent_style: &Arc<ComputedValues>,
parent_style: Arc<ComputedValues>,
) {
let (leading_whitespace, mut input) = self.handle_leading_whitespace(&input);
if leading_whitespace || !input.is_empty() {
@ -341,7 +355,6 @@ where
}
if let Some(text) = new_text_run_contents {
let parent_style = parent_style.clone();
inlines.push(ArcRefCell::new(InlineLevelBox::TextRun(TextRun {
tag: node.as_opaque(),
parent_style,
@ -418,7 +431,7 @@ where
fn handle_inline_level_element(
&mut self,
node: Node,
style: &Arc<ComputedValues>,
style: Arc<ComputedValues>,
display_inside: DisplayInside,
contents: Contents,
) -> ArcRefCell<InlineLevelBox> {
@ -449,14 +462,15 @@ where
inline_box.last_fragment = true;
ArcRefCell::new(InlineLevelBox::InlineBox(inline_box))
} else {
let content_sizes = ContentSizesRequest::inline_if(!style.inline_size_is_length());
ArcRefCell::new(InlineLevelBox::Atomic(
IndependentFormattingContext::construct(
self.context,
node,
style.clone(),
style,
display_inside,
contents,
ContentSizesRequest::inline_if(!style.inline_size_is_length()),
content_sizes,
// Text decorations are not propagated to atomic inline-level descendants.
TextDecorationLine::NONE,
),

View file

@ -10,6 +10,7 @@ use crate::display_list::stacking_context::{
};
use crate::dom_traversal::{iter_child_nodes, Contents, NodeExt};
use crate::element_data::LayoutBox;
use crate::flexbox::FlexLevelBox;
use crate::flow::construct::ContainsFloats;
use crate::flow::float::FloatBox;
use crate::flow::inline::InlineLevelBox;
@ -119,6 +120,7 @@ impl BoxTree {
enum UpdatePoint {
AbsolutelyPositionedBlockLevelBox(ArcRefCell<BlockLevelBox>),
AbsolutelyPositionedInlineLevelBox(ArcRefCell<InlineLevelBox>),
AbsolutelyPositionedFlexLevelBox(ArcRefCell<FlexLevelBox>),
}
fn update_point<'dom, Node>(
@ -188,6 +190,14 @@ impl BoxTree {
},
_ => return None,
},
LayoutBox::FlexLevel(flex_level_box) => match &*flex_level_box.borrow() {
FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(_)
if box_style.position.is_absolutely_positioned() =>
{
UpdatePoint::AbsolutelyPositionedFlexLevelBox(flex_level_box.clone())
},
_ => return None,
},
};
Some((primary_style.clone(), display_inside, update_point))
}
@ -217,6 +227,12 @@ impl BoxTree {
out_of_flow_absolutely_positioned_box,
);
},
UpdatePoint::AbsolutelyPositionedFlexLevelBox(flex_level_box) => {
*flex_level_box.borrow_mut() =
FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(
out_of_flow_absolutely_positioned_box,
);
},
}
return true;
}

View file

@ -4,6 +4,7 @@
use crate::context::LayoutContext;
use crate::dom_traversal::{Contents, NodeExt};
use crate::flexbox::FlexContainer;
use crate::flow::BlockFormattingContext;
use crate::fragments::Fragment;
use crate::positioned::PositioningContext;
@ -43,6 +44,7 @@ pub(crate) struct IndependentLayout {
#[derive(Debug, Serialize)]
enum IndependentFormattingContextContents {
Flow(BlockFormattingContext),
Flex(FlexContainer),
// Not called FC in specs, but behaves close enough
Replaced(ReplacedContent),
@ -53,6 +55,7 @@ pub(crate) struct NonReplacedIFC<'a>(NonReplacedIFCKind<'a>);
enum NonReplacedIFCKind<'a> {
Flow(&'a BlockFormattingContext),
Flex(&'a FlexContainer),
}
impl IndependentFormattingContext {
@ -83,6 +86,22 @@ impl IndependentFormattingContext {
contents: IndependentFormattingContextContents::Flow(bfc),
}
},
DisplayInside::Flex => {
let (fc, content_sizes) = FlexContainer::construct(
context,
node,
&style,
non_replaced,
content_sizes,
propagated_text_decoration_line,
);
Self {
tag: node.as_opaque(),
style,
content_sizes,
contents: IndependentFormattingContextContents::Flex(fc),
}
},
},
Err(replaced) => {
let content_sizes = content_sizes.compute(|| replaced.inline_content_sizes(&style));
@ -96,6 +115,28 @@ impl IndependentFormattingContext {
}
}
pub fn construct_for_text_runs<'dom>(
context: &LayoutContext,
node: impl NodeExt<'dom>,
style: Arc<ComputedValues>,
runs: impl Iterator<Item = crate::flow::inline::TextRun>,
content_sizes: ContentSizesRequest,
propagated_text_decoration_line: TextDecorationLine,
) -> Self {
let (bfc, content_sizes) = BlockFormattingContext::construct_for_text_runs(
context,
runs,
content_sizes,
propagated_text_decoration_line,
);
Self {
tag: node.as_opaque(),
style,
content_sizes,
contents: IndependentFormattingContextContents::Flow(bfc),
}
}
pub fn as_replaced(&self) -> Result<&ReplacedContent, NonReplacedIFC> {
use self::IndependentFormattingContextContents as Contents;
use self::NonReplacedIFC as NR;
@ -103,6 +144,7 @@ impl IndependentFormattingContext {
match &self.contents {
Contents::Replaced(r) => Ok(r),
Contents::Flow(f) => Err(NR(Kind::Flow(f))),
Contents::Flex(f) => Err(NR(Kind::Flex(f))),
}
}
}
@ -122,6 +164,12 @@ impl NonReplacedIFC<'_> {
containing_block,
tree_rank,
),
NonReplacedIFCKind::Flex(fc) => fc.layout(
layout_context,
positioning_context,
containing_block,
tree_rank,
),
}
}
}

View file

@ -15,6 +15,7 @@ pub mod data;
pub mod display_list;
mod dom_traversal;
pub mod element_data;
mod flexbox;
mod flow;
mod formatting_contexts;
mod fragments;

View file

@ -48,6 +48,7 @@ pub(crate) enum DisplayOutside {
pub(crate) enum DisplayInside {
Flow,
FlowRoot,
Flex,
}
/// Percentages resolved but not `auto` margins
@ -394,6 +395,7 @@ impl From<stylo::Display> for Display {
let inside = match packed.inside() {
stylo::DisplayInside::Flow => DisplayInside::Flow,
stylo::DisplayInside::FlowRoot => DisplayInside::FlowRoot,
stylo::DisplayInside::Flex => DisplayInside::Flex,
// These should not be values of DisplayInside, but oh well
stylo::DisplayInside::None => return Display::None,

View file

@ -266,7 +266,8 @@ ${helpers.predefined_type(
"order",
"Integer",
"0",
engines="gecko servo-2013",
engines="gecko servo-2013 servo-2020",
servo_2020_pref="layout.flexbox.enabled",
extra_prefixes="webkit",
animation_value_type="ComputedValue",
spec="https://drafts.csswg.org/css-flexbox/#order-property",

View file

@ -34,6 +34,17 @@ fn moz_box_display_values_enabled(context: &ParserContext) -> bool {
static_prefs::pref!("layout.css.xul-box-display-values.content.enabled")
}
fn flexbox_enabled() -> bool {
if cfg!(feature = "servo-layout-2020") {
servo_config::prefs::pref_map()
.get("layout.flexbox.enabled")
.as_bool()
.unwrap_or(false)
} else {
true
}
}
/// Defines an elements display type, which consists of
/// the two basic qualities of how an element generates boxes
/// <https://drafts.csswg.org/css-display/#propdef-display>
@ -63,7 +74,6 @@ pub enum DisplayInside {
Contents,
Flow,
FlowRoot,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
Flex,
#[cfg(feature = "gecko")]
Grid,
@ -146,9 +156,7 @@ impl Display {
pub const Block: Self = Self::new(DisplayOutside::Block, DisplayInside::Flow);
#[cfg(feature = "gecko")]
pub const FlowRoot: Self = Self::new(DisplayOutside::Block, DisplayInside::FlowRoot);
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const Flex: Self = Self::new(DisplayOutside::Block, DisplayInside::Flex);
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const InlineFlex: Self = Self::new(DisplayOutside::Inline, DisplayInside::Flex);
#[cfg(feature = "gecko")]
pub const Grid: Self = Self::new(DisplayOutside::Block, DisplayInside::Grid);
@ -317,9 +325,9 @@ impl Display {
#[inline]
pub fn is_atomic_inline_level(&self) -> bool {
match *self {
Display::InlineBlock => true,
Display::InlineBlock | Display::InlineFlex => true,
#[cfg(any(feature = "servo-layout-2013"))]
Display::InlineFlex | Display::InlineTable => true,
Display::InlineTable => true,
_ => false,
}
}
@ -330,7 +338,6 @@ impl Display {
/// This is used to implement various style fixups.
pub fn is_item_container(&self) -> bool {
match self.inside() {
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
DisplayInside::Flex => true,
#[cfg(feature = "gecko")]
DisplayInside::Grid => true,
@ -432,12 +439,9 @@ impl ToCss for Display {
_ => match (outside, inside) {
#[cfg(feature = "gecko")]
(DisplayOutside::Inline, DisplayInside::Grid) => dest.write_str("inline-grid"),
(DisplayOutside::Inline, DisplayInside::Flex) => dest.write_str("inline-flex"),
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
(DisplayOutside::Inline, DisplayInside::Flex) |
(DisplayOutside::Inline, DisplayInside::Table) => {
dest.write_str("inline-")?;
inside.to_css(dest)
},
(DisplayOutside::Inline, DisplayInside::Table) => dest.write_str("inline-table"),
#[cfg(feature = "gecko")]
(DisplayOutside::Block, DisplayInside::Ruby) => dest.write_str("block ruby"),
(_, inside) => {
@ -467,12 +471,11 @@ fn parse_display_inside<'i, 't>(
) -> Result<DisplayInside, ParseError<'i>> {
Ok(try_match_ident_ignore_ascii_case! { input,
"flow" => DisplayInside::Flow,
"flex" if flexbox_enabled() => DisplayInside::Flex,
#[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
"flow-root" => DisplayInside::FlowRoot,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"table" => DisplayInside::Table,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"flex" => DisplayInside::Flex,
#[cfg(feature = "gecko")]
"grid" => DisplayInside::Grid,
#[cfg(feature = "gecko")]
@ -575,10 +578,8 @@ impl Parse for Display {
"inline-block" => Display::InlineBlock,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"inline-table" => Display::InlineTable,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"-webkit-flex" => Display::Flex,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"inline-flex" | "-webkit-inline-flex" => Display::InlineFlex,
"-webkit-flex" if flexbox_enabled() => Display::Flex,
"inline-flex" | "-webkit-inline-flex" if flexbox_enabled() => Display::InlineFlex,
#[cfg(feature = "gecko")]
"inline-grid" => Display::InlineGrid,
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]

View file

@ -88,6 +88,7 @@
"js.werror.enabled": false,
"layout.animations.test.enabled": false,
"layout.columns.enabled": false,
"layout.flexbox.enabled": false,
"layout.threads": 3,
"layout.viewport.enabled": false,
"layout.writing-mode.enabled": false,

View file

@ -1 +1 @@
prefs: ["layout.columns.enabled:true"]
prefs: ["layout.columns.enabled:true", "layout.flexbox.enabled:true"]