Skip to content

Layout 2020

Martin Robinson edited this page Jul 7, 2023 · 27 revisions

A new implementation of CSS layout for Servo. For more information see the Servo Layout Engines Report.

Motivation

Servo’s original implementation of CSS layout (retronymed layout 2013) achieves fine-grained parallelism through a rigid separation of multi-thread tree traversal from the operations that run on each tree nodes. Some parts of CSS (particularly floats) do not map well to this model, making web-compatibility hard or impossible.

The high level design ideas and goals in no particular order:

  • Don’t gratuitously make code deviate from CSS specs in terms of structure or terminology.

  • Mostly “classic” imperative code style with recursive tree traversals.

  • Prefer Rust enums over inheritance or dynamic dispatch. Try to model in the Rust type system what tree structures are possible or impossible in CSS. (For example: direct children of an inline box are all inline-level boxes.)

  • Don’t mandate parallelism at every level of the tree(s), opportunistically parallelize some loops with rayon. This is hopefully enough to usually saturate available cores and maintain speedups. When floats are involved, disable parallelism for that block formatting context and pass around an Option<&mut FloatContext>. Parallelism can be resumed within inner formatting contexts, if any.

  • Separate the box tree from the fragment tree (as defined in CSS specs). Make their nodes atomically-reference-counted, and mostly immutable. (AtomicRefCell or similar is still acceptable, to consider on a case-by-case basis.) Make them persistent: incremental updates (e.g. after the DOM changed) are done by making new nodes are re-using unchanged sub-trees. Consider im’s Vector over Vec for the (direct) children of a given node, to reduce the cost of replacing just one in case there are many.

  • Write new tests for WPT while writing new code, if we find that that code isn’t well covered already.

Enabling CSS properties

The style crate is used by three different layout “engines”: gecko (Firefox), servo-2013, and servo-2020. It has a single rough mechanism that causes a property not to be parsed which involves putting a property behind a run-time preference. A pref can be useful to land a partial implementation of a feature, or one whose specification is not stable enough yet.

Layout 2020 started out by disabling (almost) every property, and now is progressively enabling them as corresponding layout support is added. So when working on a new feature, before tests start doing anything interesting you will likely need to find some property declaration(s) in .mako.rs files under components/style/ and remove a servo_pref="layout.legacy_layout line.

Status

Text

CSS features

  • Constructing boxes from the DOM
    • display: none
    • display: contents
    • Abstracting ::before and ::after pseudo-elements v.s. DOM elements
  • Basic box model: margins, borders, padding
  • min/max-width/height
  • Intrinsic sizing
  • Writing modes
  • Fragmentation
    • Simple(r) cases where a break is at a single point of the tree
    • Parallel flows
  • Paint ordering, z-index, and stacking contexts
  • Flow layout (a.k.a. blocks and inlines)
    • Construct anonymous block boxes as needed
    • Block-in-inline splits
    • Basic flow and line breaking
    • Better handling of line break opportunities
    • (Control for) white-space collapsing
    • Baselines and vertical-align
    • Inline-level independent formatting contexts such as inline-block
    • Margin collapsing
    • [-] Floats and clearing
  • position: absolute
  • position: fixed
  • Generated content (::before, ::after, display: list-item, …)
    • Counters and ordered lists
    • Quotes
  • Replaced elements/boxes (such as <img>)
    • Layout support (sizing is different)
    • Construct replaced boxes for <img>
    • For other HTML element types as appropriate
  • Tables
  • Multicol
  • [-] Flexbox
  • Grid
  • Transforms
  • Transitions and animations

Basic HTML

Element Layout 2013 Layout 2020
<a> Y Y
<audio> N N
<button> Y Y
<br> Y partial
<canvas> Y Y
<details> Y N
<fieldset> partial partial
<iframe> Y N
<input> partial partial
<hr> Y Y
<img> Y Y
<legend> N N
<marquee> N N
<meter> N N
<ol> Y N
<progress> N N
<select> partial partial
<summary> Y N
<textarea> partial partial
<ul> Y Y
<video> N N

CSS2 Properties

Property Layout 2013 Layout 2020
azimuth N/A N/A
background-attachment N N
background-color Y Y
background-image Y Y
background-position Y Y
background-repeat Y Y
background Y Y
border-collapse Y N
border-color Y Y
border-spacing Y N
border-style Y Y
border-top Y Y
border-top-color Y Y
border-top-style Y Y
border-top-width Y Y
border-width Y Y
border Y Y
bottom Y Y
caption-side Y N
clear Y N
clip
Partial Incorrect for borders and background.
Y
color Y Y
content
Partial<string>: Y
<uri>: N
<counter>: Y
attr(): N
quotes: Y
Partial<string>: Y
<uri>: Y
<counter>: N
attr(): Y
quotes: N
counter-increment Y N
counter-reset Y N
cue-after N/A N/A
cue-before N/A N/A
cue N/A N/A
cursor Y Y
direction Y N
display ? ?
elevation N/A N/A
empty-cells Y N
float Y N
font-family Y Y
font-size Y Y
font-style Y Y
font-variant Y N
font-weight Y Y
font Y Y
height Y Y
left Y Y
letter-spacing Y Y
line-height Y Y
list-style-image
PartialWorks, but should probably affect line height when outside positioned
PartialWorks, but wrong baseline alignment
list-style-position Y
NOnly supports inside position
list-style-type
Partialdisc: Y
circle: Y
square: Y
decimal: Y
decimal-leading-zero: N
lower-roman: N
upper-roman: N
lower-greek: Y
lower-latin: N
upper-latin: N
armenian: N
georgian: N
lower-alpha: Y
upper-alpha: Y
none: Y
Partialdisc: Y
circle: Y
square: Y
decimal: N
decimal-leading-zero: N
lower-roman: N
upper-roman: N
lower-greek: N
lower-latin: N
upper-latin: N
armenian: N
georgian: N
lower-alpha: N
upper-alpha: N
none: Y
list-style See longhands See longhands
margin-right Y Y
margin-top Y Y
margin Y Y
max-height Y Y
max-width Y Y
min-height Y Y
min-width Y Y
orphans N/A N/A
outline-color Y N
outline-style Y N
outline-width Y N
outline Y N
overflow
Partialauto and scroll behave as hidden
Partialauto and scroll behave as hidden, and they don't establish a BFC
padding-top Y Y
padding Y Y
page-break-after N/A N/A
page-break-before N/A N/A
page-break-inside N/A N/A
pause-after N/A N/A
pause-before N/A N/A
pause N/A N/A
pitch-range N/A N/A
pitch N/A N/A
play-during N/A N/A
position Y Y
quotes Y N/A
richness N/A N/A
right Y Y
speak-header N/A N/A
speak-numeral N/A N/A
speak-punctuation N/A N/A
speak N/A N/A
speech-rate N/A N/A
stress N/A N/A
table-layout Y N
text-align Y Y
text-decoration N N
text-indent Y N
text-transform Y N
top Y Y
unicode-bidi N N
vertical-align Y N
visibility Y Y
voice-family N/A N/A
volume N/A N/A
white-space Y Y
widows N/A N/A
width Y Y
word-spacing Y Y
z-index Y Y
Clone this wiki locally