New Layout: HTML LayoutAuthors: Troy Chevalier, Kipp Hickman, Rick Gessner, Steve Clark
Updated: 30 April 1998
HTML layout is the set of classes that implement the HTML layout semantics.
The major components of HTML layout are body, block, inline, list item, table, and the leaf components like BR, IMG, and SPACER.
Content Model. The base class for leaf HTML content objects is nsHTMLTagContent, which is for content objects that have HTML attributes. Most HTML container tags (for example, P, DIV, SPAN, etc.) have content objects that are instances of class nsHTMLContainer. nsHTMLContainer is a derived class of nsHTMLTagContent which manages an array of child content nodes.
Frame creation. Whether an element is displayed as block or inline is not a function of the HTML tag, but of the associated stylistic information (see struct nsStyleDisplay). Class nsHTMLContent is the abstract base class for all HTML content objects. It creates the one and only content delegate (class ContentDelegate) that is used for all HTML content. When asked to create a frame the HTML content delegate asks the content object to create the frame.
nsHTMLContainer responds by looking at the display type. If it's a block then it creates a block frame, if it's inline then it creates an inline frame, and if it's a list item then it creates a list item frame.
The mapping of HTML elements to either block or inline is controlled by our default UA style sheet (file ua.css).
Display None. To simplify managing of the child frames, we always create and insert a frame even if the display type is set to none. The frame that's created is of type nsFrame, and it has a size of (0, 0) and no stylistic information (no border, padding, ...). Because nsFrame is a leaf frame no child frames are generated, regardless of whether the associated content is a container.
|Body, Block, and Inline Implementation|
Body Frame. Class nsBodyFrame is the frame class that's used for displaying the BODY tag. The associated content object is an instance of class BodyPart (derived class of nsHTMLContainer). nsBodyFrame supports being used as a pseudo frame, and it's often used that way. For example, table cells create a body pseudo frame for displaying the table cell data.
The body frame is basically a vertical box that arranges its block-level elements from top to bottom. If it encounters an inline element, the body frame wraps the inline element in a block pseudo frame.
Block Frame. Class nsCSSBlockFrame is the frame class that's used for displaying block-level elements. Block frame is a 2D layout class that lays out its block-level elements from top to bottom, and its inline elements from left to right (or right to left). Block frame has logical lines, but it does not actually have line child frames. This greatly reduces the number of frames, which means we lay out faster and we use less memory.
Block frame also supports being used as a pseudo frame.
Class nsListItemFrame, which handles list items, is derived from nsCSSBlockFrame. It manages the placement and painting of the list item bullet.
Inline Frame. Class nsCSSInlineFrame is the frame class that's used for displaying inline elements. Inline frames are simple horizontal boxes that arrange their child frames either left to right or right to left depending on the stylistic direction.
Inline frames do not support being used as a pseudo frame. This reflects the fact that we haven't needed to use
Reflow Unmapped. Reflow Mapped. As you look through the HTML frame clases you'll see some similarities in the code, and you'll see some common terminology:
- Reflow Unmapped
- This refers to the process of creating new frames for content that hasn't yet been mapped.
- Reflow Mapped
- This refers to the process of reflowing the existing child frames.
- When reflowing mapped children, if a child frame doesn't fit within the available space then the child frame is "pushed" to the next-in-flow.
- When reflowing mapped children, if all the existing child frames fit and there's still room left, child frames are "pulled up" from the next-in-flow.
- Overflow List
- This is a list of child frames that don't fit within the available space of their current geometric parent. Because the frame doesn't have a next-in-flow there's no place to "push" the child frames, so instead they're placed on an overflow list.
All the table code is contained within the layout/html/table sub-directory.
Table content is stored as a normalized HTML 4.0 table. Table layout reflects the content according to a set of constraints. The constraints are a combination of physical (i.e. the width of the browser window), contentual (content of each cell), and stylistic (i.e. the HTML WIDTH attribute, CSS Style, or a compatibility mode, etc).
The hierarchy for a table is:
- one nsTablePart (mandatory)
- any number of nsTableCaptions (optional). Multiple captions are supported even though they are not legal in HTML
- one nsTableColumnGroup (mandatory)
- any number of nsTableColumns (optional)
- any number of nsTableRowGroups, one TBODY row group is mandatory. Additional TBODY row groups are optional. Any number of THEAD and TFOOT row groups are optional
- any number of nsTableRows (at least one per row group is mandatory)
- any number of nsTableCells (at least one per row is mandatory)
- one nsBodyPart per cell, containing arbitrary HTML content
nsTablePart maintains an nsCellMap, which is a structured description of all the cells in a table. It is the nsCellMap that holds information about the effects of row and column spans on cells. The cell map is a regular n x m grid onto which cells are overlaid, allowing us to easily answer questions about what cell occupies a logical region in the table, even for irregular tables that include complex row and column spans.
Layout. Table layout is represented by a hierarchy of frame objects that describe the geometry of the table. The frame model closely mimics the content model, with a few exceptions noted below.
- one nsTableOuterFrame (mandatory)
- any number of nsTableCaptionFrames (optional)
- one nsTableFrame (mandatory, referred to as the "inner table frame")
- any number of nsTableRowGroupFrames (at least one is mandatory)
- any number of nsTableRowFrames (at least one is mandatory per row group frame)
- any number of nsTableCellFrames (at least one is mandatory per row frame)
- one nsBodyFrame pseudo frame per cell, containing arbitrary frames
Table layout is determined in a 3-step process. In the first step, the table is flowed into an infinitely wide and tall space. This gives us the minimum and desired sizes for every cell in the table. In the second step, the table constraints are factored in and widths are assigned to every cell. In the third step, heights are assigned to every cell based on the computed width constraint. The results of the first step are cached and only need to be recomputed when content or constraints are changed.
The near future. Table layout is designed to support multiple notions of "correct" table layout. An nsITableLayoutStrategy interface will soon be added that defines how a table should be laid out. We envision at least three implementations of nsITableLayoutStrategy: one for Netscape Navigator compatibility, one for Internet Explorer compatibility, and one for full HTML 4.0/CSS 2.0 support. An implementation of nsITableLayoutStrategy will be selected at run-time for a particular view of a table based on the stylistic properties of the table.
Table layout is designed to support intelligent pagination. This means that tables will break atreasonable places across boundaries (pages, columns, etc). So for example, table pagination will be biased to keep captions and tables together, to break on row boundaries so cell data is kept together as much as possible, and THEAD and TFOOT information is replicated across breaks.
For the latest information on where tables are going, see the table to-do list.
The placeholder frame creates the actual floater frame, but it is not inserted in the flow. Instead, the placeholder frame walks the frame hierarchy (geometric parents) looking for a frame that implements the nsIFloaterContainer interface. This is the containing block for the floater.
The containing block is notified of the floater (see the AddFloater() member function). The floater is then either placed at the current line, or added to a to-do list of floaters to be placed below the current line. The frame itself is inserted as a child of the body frame; this keeps it out of the list of flowed children. The containing block finds the frame where it should insert the floater by walking the frame hierarchy looking for a frame that implements the nsIAnchoredItems interface.
The containing block also notifies the space manager (nsISpaceManager) that the space is now unavailable (see AddRectRegion() member function).
Runaround (flowing text around the anchored item) happens because the block frame calls the space manager to get a band of available space. The band data indicates which parts of the band are available, and which parts are unavailable.
The space manager is created by the body frame. Each body frame has its own space manager, which means that each table cell has an associated space manager.
How runaround works depends on the block-level frame being reflowed. If you're willing to interact directly with the space manager, implement interface nsIRunaround and you get access to the band data. Do this if your frame supports being non-rectangular. This is what block frames does.
If you don't implement nsIRunaround then your parent frame will place you in the available space between the left and right floaters.
The HTML layout code has dependencies on the content/document model, the style system, the view hierarchy, and the graphics system.
- In May we'll be finishing up incremental reflow and doing performance tuning. Table incremental reflow won't be finished until June.
- In June we'll be implementing the sub-document architecture, session history, and framesets
- In July we'll be doing the threading work
- Later in July we'll begin work on applets and plugins
|Areas to Help|
- Enhance the CSS parser to support CSS 2.0
- The threading support has not been implemented
- Incremental reflow is not complete
- Tables are not yet 100% backwards compatible. They should be fully compatible by July