Copyright © 1995-2012 Opera Software AS. All rights reserved. This file is part of the Opera web browser. It may not be distributed under any circumstances.
This is a description of Opera's min/max width calculation and propagation algorithm. Min/max width values values are used to calculate the width of shrink-to-fit containers, tables and flex items.
CSS 2.1 briefly describes the shrink-to-fit algorithm here, which refers to how column width calculation is done in automatic table layout - here.
When a layout box has automatic width (width:auto), and the box is of such a type that it is not to unconditionally take up all available space in its containing block, the shrink-to-fit algorithm is used.
Auto-width regular block boxes will take up all available width in their containing block, so shrink-to-fit is not to be used there, but for floats, absolutely positioned boxes, inline-blocks, tables and BUTTONs, shrink-to-fit will be involved.
The formula itself for calculating the width of a shrink-to-fit container is quite simple:
width = MAX(minimum width, MIN(maximum width, available width))
... where minimum width is the width of the widest piece of unbreakable content, i.e., roughly speaking, how wide the box has to be to avoid horizontal overflow.
... where maximum width is the width that the box would have to be to prevent implicit avoidable wrapping - i.e. implicit line breaks or implicit float clearance.
... where available width is the width the containing block in which the box lives.
The complicated part here is to calculate minimum width and especially maximum width. This is where Opera applies its min/max width calculation and propagation algorithm.
There is also a variable normal minimum width, which is used in ERA mode, where we may allow minimum width to become narrower than strictly allowed in the CSS spec. In such modes, normal minimum width will be set to the minimum width that the content would have had in normal layout mode. For the sake of final shrink-to-fit box width calculation, normal minimum width is generally ignored. Normal minimum width plays a role when we see that we can afford to exceed column minimum widths in automatic table layout, though.
The CSS specification defines two properties min-width and max-width. These have nothing in particular to do with the term min/max width calculation and propagation (although they do affect such calculation and propagation). It is hopefully clear in this documentation which one is meant. The CSS properties are always spelled min-width and max-width. For min/max width calculation and propagation, we use terms like min/max, minimum width and maximum width.
A shrink-to-fit container is a container which establishes a new block formatting context, and it gets its width according to the shrink-to-fit algorithm.
A table with table-layout:auto gets its width according to the shrink-to-fit algorithm. Its minimum width is the sum of the minimum width of all columns in the table, plus any horizontal border-spacing. Its maximum width is the sum of the maximum width of all columns in the table, plus any horizontal border-spacing. This is a simplification; see the table layout documentation for all details.
The flex algorithm uses min/max widths on the items in a flex container to size and/or position the items.
In a horizontal flex container, items' minimum widths are used to resolve 'min-width:auto'. An item's maximum width is used to calculate "flex base size" [1] (the size an item has before stretching / shrinking), if 'width' is 'auto'.
In a vertical flex container, if an item has auto width, its minimum and maximum widths are used to find the hypothetical cross size [2], which in turn is used to align the items horizontally and also calculate the flex line's width.
[1] http://www.w3.org/TR/css3-flexbox/#line-sizing
[2] http://www.w3.org/TR/css3-flexbox/#cross-sizing
Min/max widths are calculated and propagated on the fly and on demand, when a box is being laid out. If an ancestor container has announced that it needs min/max widths, they will be propagated from a descendant box. If this descendant box has not already calculated its min/max widths, or if they have been invalidated somehow (typically by modifying, adding or removing child content), min/max widths must be calculated as well.
A shrink-to-fit container, and table-cell and table-caption, will enable min/max width calculation, on itself, and it will also enable calculation and propagation on all child content. Inline content's min/max widths will be propagated to a virtual line, which will be propagated further to the container. Block content's min/max widths will be propagated to their container. When min/max widths are propagated from content to its container, the min/max values are constrained against any fixed values of the following properties: width, min-width, max-width.
Min/max width calculation and propagation does not require an additional reflow pass in itself, but if the new min/max widths calculated differ from the previous values in such a way that box widths change, an additional reflow pass is required. For a new shrink-to-fit box that has never calculated its min/max widths, two reflow passes are therefore generally necessary, unless we managed to guess the correct width in the first reflow pass. If the min/max widths are unknown at the time of calculating the width of the box (typically in the first reflow pass ever for a box), we guess its width to be equal to the width of its containing block. If this turns out to be true, only one reflow pass is required on this shrink-to-fit box. For tables, we guess that each column will have a width of 1000px, which is almost as good a guess as any. A width of 1000px represents a typical container width, and the assumption is that the layout engine works at optimal efficiency with this width (there will not be "too many" lines, and the lines will not be "too wide").
The CSS spec is explicitly unclear about percent based widths inside a shrink-to-fit box, but different browsers have sort of reached a common way of doing it. Percentual widths, both content width, margin width and padding width are ignored when calculating and propagating min/max widths. Percentual margins and padding are treated as if they were 0. Percentual width is treated as if it were auto. The only exception to this is FlexRoot mode, which has its own way of dealing with percentual widths.
Minimum and maximum widths of a box with fixed width is that box's fixed width. This rule does not apply to flex items (the flex algorithm is allowed to override fixed widths).
Minimum width among adjacent words (text) and other types of inline content is the largest minimum width among all the inline content.
Minimum and maximum width of a word is normally simply the width of that word, but in non-normal layout mode or when the CSS property overflow-wrap is break-word, minimum width will be the width of the widest character in the word.
The maximum width of adjacent words (text) and other types of inline content is the sum of the maximum width of each piece of inline content, including the width of any white-space that separates them.
Since floats, line boxes, replaced content, tables, new block-formatting-context blocks (with overflow:hidden, for instance) are affected by floats, we need to figure out which of the floats previously defined in the block formatting context may make an impact, in order to calculate the correct maximum width. To do this, we need to know the height of every single box; but this is not about the actual height calculated by the layout engine, but rather the height that each box would get if maximum width was satisfied. We call this minimum height, since the height that a box gets if all its lines are stretched to their maximum (and no floats are cleared because of insufficient width) typically will make that box as short as it can possibly get.
The minimum height of a box with fixed height is that box's fixed height.
The minimum height of adjacent words (text) and other types of inline content is the height of the hypothetical line that would be required to contain all of the content.
The minimum height of a table is the sum of the minimum height of all captions, rows and horizontal border-spacing. The minimum height of a table row is the largest minimum height of all cells on that row. Fixed height is honored in the same manner as for regular layout (i.e. it is sort of treated like the min-height property).
Margins affect minimum height, and follow the normal rules for margin collapsing, but percentual margins are ignored (treated as 0).
If the content has percentual width, the minimum width is set to 0, and the maximum width is set to the intrinsic width, regardless of percentage.
If the content has fixed width, minimum and maximum width are set to this value. This rule does not apply to flex items (the flex algorithm is allowed to override fixed widths); instead we behave as if width were auto (in the remaining steps below).
Otherwise, if the content has fixed height and intrinsic ratio, the minimum and maximum widths are resolved from the intrinsic ratio.
Otherwise, if content width is auto, minimum and maximum widths are set to the intrinsic width.
If the content has fixed height, the minimum height is set to this value.
If the content has percentual height but doesn't live inside of a container with fixed height, height becomes auto for the purpose of calculating minimum height. This is very similar to the very rule for calculating the computed height, if specified height is percentual, but is actually even stricter, because of percentual margins and padding.
If the content has auto height, intrinsic height is used. If a fixed width has been specified and the content has an intrinsic ratio, intrinsic height it is scaled against those.
Min/max width propagation is used for table-cells, table-captions, absolutely positioned boxes with auto width, floats with auto width, inline-blocks with auto width, BUTTONs with auto width, LEGENDs with auto width, replaced elements with ::before and/or ::after, internally in MARQUEEs, flex items, and maybe more.
If the width of a float, absolutely positioned box, inline-block, BUTTON, or LEGEND is auto, or if the box is a replaced element with ::before and/or ::after, a ShrinkToFitContainer is created. Such objects will enable min/max width propagation from its descendants, and calculate its width according to the shrink-to-fit algorithm. After one reflow pass (ShrinkToFitContainer::FinishLayout() or UpdateScreen()) (when min/max widths from all descendants have been propagated), it will re-apply the algorithm and see if the width changed. If it changed, another reflow pass is required, and it will mark itself dirty from SignalChange().
A horizontal MARQUEE additionally needs to use the min/max width algorithm internally, to calculate a suitable width of the scrolling content.
A table-cell or table-caption will create a Container and enable min/max propagation from its descendants. Min/max widths from table cells will be propagated further to the table, where an array of columns are kept up-to-date with min/max widths. This is described in greater detail in the table layout documentation. Min/max widths from table captions do not affect the size of the actual table, but may be propagated further to ancestor content, if that is appropriate.
Min/max width calculation is always enabled on flex items. The parent flex container will use these values to size and position the items. They will of course also be propagated further up in the ancestry (beyond the flex container), if some ancestor has enabled min/max width calculation (so that e.g. a float with an auto-width flex container is sized correctly, according to the shrink-to-fit algorithm).