FlexRoot

Copyright © 1995-2010 Opera Software AS. All rights reserved. This file is part of the Opera web browser. It may not be distributed under any circumstances.

About this document

This is a specification. We try not to deal with implementation details or bugs in Opera more than necessary here.

Introduction

According to the CSS spec, the initial containing block width - in Opera also called "layout viewport width" - is the same as the window width (if the document is the root frame). FlexRoot changes this. FlexRoot is a rendering mode where the initial containing block width is allowed to grow beyond the width of the browser window (and even get narrower as well). It is suitable in environments where the screen width is narrower than what a web developer typically assumes. Its goal is to make web pages appear as similar as possible to what they look like on a desktop system, without making usage and navigation difficult. It is basically an improvement of the old "Virtual Screen Width" feature, where an initial containing block width was hardcoded (to e.g. 640px). With FlexRoot the layout viewport width depends on page content. This means that a page should never be wider than "necessary". The layout viewport will grow wider only if wide content demands it, typically when large absolute widths or horizontal paddings/margins/borders are specified, or when widths are auto on replaced content with large intrinsic widths.

In most cases it is logical and desirable to combine FlexRoot with the "Limit paragraph width" (aka. ERA text-wrap) feature. Combining FlexRoot with adaptive zoom to find a suitable initial zoom level is also useful.

FlexRoot minimum width and maximum width

Two parameters are used to control FlexRoot behavior; minimum width and maximum width. Minimum width is by default the same as the window width (if the document is the root frame), while maximum width should be set to the typical width of a "wide page", to prevent FlexRoot to grow beyond this limit. Some content would cause a very large layout viewport width otherwise. For example, <div style="width:10000px;"></div> would cause the layout viewport to become 10000px, which would mean 10000px wide lines, right-aligned floats placed way to the right. This is why we constrain the layout viewport width like this.

The following is always true: minimum width <= layout viewport width <= maximum width

Maximum width must be set for FlexRoot to be enabled, and the other way around: FlexRoot is enabled by setting maximum width. Minimum width is optional, and pretty much obsolete with the introduction of the 3 viewports (visual, layout and rendering viewports) in core 2.3 (and 2.2.1).

There are two preference settings in the "User Prefs" section to define these values:

"FlexRoot Max Width"

Default value: 0 (disable FlexRoot)
Suggested value: 850
If the value is different from 0, FlexRoot is enabled, and the value will define the maximum number of pixels to use as the initial containing block width.

"FlexRoot Min Width"

Default value: 0 (derive from window width)
Suggested value: 0
If FlexRoot is enabled, and the value is different from 0, it will define the minimum number of pixels to use as the initial containing block width. If FlexRoot is enabled, and the value is 0, the window width will be used as minimum width of the layout viewport of the root frame.

How it works

The general behavior of FlexRoot is easy to describe using CSS: FlexRoot uses a pseudo element that is the parent of the root HTML element in a document. This element will be laid out with the shrink-to-fit algorithm, very similar to an element with e.g. "position:absolute; top:auto; right:auto; bottom:auto; left:auto; width:auto; z-index:0;". The min-width property of this element will be set to FlexRoot minimum width, and the max-width property will be set to FlexRoot maximum width.

Regarding the shrink-to-fit algorithm, the available width input parameter is set to 0, effectively ignoring maximum width and only honor minimum width (along width the min-width and max-width properties explained in the previous paragraph). We want content that is not wider than absolutely necessary to keep boxes from overflowing (boxes that wouldn't overflow if layout viewport width were less constrained).

Deviations from CSS / HTML specifications / normal rendering mode

The previous paragraph describes all there is about FlexRoot for most type of content, but in some cases the CSS spec isn't enough:

Framesets

We use the "Smart Frames" algorithm (also used in certain ERA modes) in FlexRoot mode. No frames are allowed to create scrollbars; instead each frame is expanded until no scrollbars are needed. The top-level frameset will display scrollbars if necessary instead. Absolute widths specified on frames will be respected (unless their content is wider). Relative and percentage widths are relative to the layout viewport width of the root document, i.e. window width or the aforementioned preference defined maximum width, whichever is the larger value.

Absolutely (and fixed) positioned boxes

Absolutely positioned boxes contribute to the layout viewport width - contrary to the CSS spec (which says that such boxes should not affect the size of ancestor elements in any way).

Only absolutely positioned boxes whose containing block is the root element will contribute.

The idea here is that the layout viewport should be wide enough for all absolutely positioned boxes to fit inside it, as long as this is possible and reasonable.

If width is absolute or auto, propagate the position and minimum width to the layout viewport.

If width is percent based, calculate how wide the (initial) containing block (layout viewport) needs to be to accommodate left, margin-left, border and padding. Propagate this value along with the box's position. This effectively means that for absolutely positioned boxes with percentual widths we assume that intrinsic width (child content width) is 0.

Fixed positioned boxes

Fixed positioned boxes will lose some of their horizontal "fixedness", meaning that when scrolling horizontally, fixed positioned may scroll along with the rest of the document. This is the consequence when the layout viewport becomes wider than the visual viewport. See http://projects/Core2profiles/specifications/mobile/rendering_modes.html#viewports for details about viewports.

Percentual widths

This applies to statically and relatively positioned boxes only. It does not apply to replaced content, and it applies to table boxes only if the used value of its 'table-layout' property is 'fixed' (the used width of a table with table-layout:auto will never be less than its minimum content width).

An element whose width property is percent based needs special attention. The general assumption here is that the author doesn't want content inside a box with a percent based width to overflow it. 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. Unfortunately, that way won't do for FlexRoot, due to the aforementioned assumption that the author probably doesn't want anything to overflow.

When min/max widths are propagated from a box with percentual width, the calculated minimum width will be multiplied by 100 and divided by the percentage. This is a way to tell the containing block that "this is how wide you have to be to prevent me from overflowing you when I resolve my percentage against your width". This also applies if width > 100%. The larger percentage width a box has, the narrower its containing block needs to be to prevent its content from overflowing. The containing block width requirement 'needed_inside' for the area defined by box-sizing (either content-box or border-box) with percentage 'p' and minimum width 'minimum_width' is calculated like this:

  needed_inside = minimum_width * 100 / p

Minimum width of content inside table cells and captions are scaled in the exact same manner, as long as the table affects FlexRoot.

When a box with percentual width has non-zero horizontal margin (and for box-sizing:content-box: padding and/or border as well), it needs to fit inside the "unused percentage". The parts of the box not included by its box-sizing need to fit inside 100 minus the width property percentage.

Let 'p' be the percentage of the width property. Let 'outside_width' be the sum of margin-left and margin-right, plus, if box-sizing is content-box, border-left-width, border-right-width, padding-left and padding-right. Then, if 'p' is less than 100, the containing block width requirement 'needed_outside' is calculated like this:

 needed_outside = outside_width * 100 / (100 - p)

If 'p' is 100 or more, no matter how large the containing block width is, it cannot prevent the box from overflowing. Give up and let 'needed_inside' be 0.

Finally, the containing block width requirement 'containing_block_propagation' for this box becomes:

 containing_block_propagation = max(needed_outside, needed_inside)

Example 1:

 <div style="width:40%;">
   <div style="width:200px; height:20px;"></div>
 </div>

Width propagated from the parent DIV:

  needed_inside = 200px * 100 / 40 = 500px
  needed_outside = 0
  containing_block_propagation = max(500px, 0) = 500px

Example 2, introducing border:

 <div style="width:40%; border:20px solid blue;">
   <div style="width:200px; height:20px;"></div>
 </div>

Width propagated from the parent DIV:

  needed_inside = 200px * 100 / 40 = 500px
  needed_outside = (20px + 20px) * 100 / (100 - 40) = 66px
  containing_block_propagation = max(500px, 66px) = 500px

Example 3, larger border:

 <div style="width:40%; padding:0 178px;">
   <div style="width:200px; height:20px;"></div>
 </div>

Width propagated from the parent DIV:

  needed_inside = 200px * 100 / 40 = 500px
  needed_outside = (178px + 178px) * 100 / (100 - 40) = 593px
  containing_block_propagation = max(500px, 593px) = 593px

Non-visible overflow

If overflow is different from 'visible', 0 may be used in minimum width propagation (this depends on core version; there are arguments for setting it to 0, and arguments against it, and we have already toggled our behavior several times; see bug CORE-16940 for the most recent story). For FlexRoot, however, we propagate the actual minimum width of the child contents, i.e. we treat it like we treat any other container.

To do?

We may want to handle percent based horizontal margin and padding in a special way. Same goes for min-width and max-width.

Should we use min-width in favor of width, if min-width is non-zero?

There is currently nothing that prevents content of absolutely positioned boxes with percentual width from overflowing; we assume that intrinsic width is 0. This should probably be amended.

Absolutely positioned boxes with left:auto and non-auto right should be handled better; i.e. attempt to avoid negative overflow of initial containing block.