michael@0: michael@0: michael@0: michael@0: michael@0: michael@0: michael@0: michael@0: michael@0: michael@0: michael@0:

michael@0: Block Layout

michael@0: This document attempts to describe how "block" layout works in the mozilla michael@0: layout engine. michael@0:

nsBlockFrame implements layout behavior that conforms to the michael@0: CSS "display:block" and "display: list-item" layout. It has several responsibilities: michael@0:

    michael@0:
  1. michael@0:  Line layout. The block is responsible for flowing inline elements michael@0: into "lines" and applying all of the css behavior as one might expect, michael@0: including line-height, vertical-align, relative positioning, etc.
  2. michael@0: michael@0:
  3. michael@0: Float management. The block is responsible for the reflow and placement michael@0: of floating elements.
  4. michael@0: michael@0:
  5. michael@0: Child block management. Blocks can contain inline elements and block elements. michael@0: Hence, blocks are responsible for reflowing child blocks. The majority michael@0: of that logic has been split out into nsBlockReflowContext, but a fair michael@0: amount remains here.
  6. michael@0: michael@0:
  7. michael@0: Supporting table reflow. The block has to carefully compute the "max-element-size" michael@0: information needed by tables. Hence, any time changes are made here one michael@0: should always run the table regression tests because the odds are you broke michael@0: one of them!
  8. michael@0:
michael@0: michael@0:

michael@0: The Big Picture for Block Reflow

michael@0: The block frame uses a list of nsLineBox's to keep track of each "line" michael@0: of frames it manages. There are two types of lines: michael@0:
"inline" lines which contain only inline elements michael@0:
"block" lines which contain exactly one block element
michael@0: Each line has a "dirty" bit which indicates that it needs reflow. Reflow michael@0: consists of identifying which lines need to be marked dirty and then reflowing michael@0: all lines. For lines which are "clean" the reflow logic will endeavor to michael@0: recover the state of reflow as if the line had been reflowed. This michael@0: saves time and allows for a faster incremental reflow. For lines which michael@0: are dirty, the line is reflowed appropriately. michael@0:

The only special thing about incremental reflow command handling is michael@0: that it marks lines dirty before proceeding, and keeps track of the child michael@0: frame that is the next frame on the reflow command path. michael@0:

Here is a list of the various classes involved in block layout: michael@0:

nsBlockFrame michael@0:

The primary culprit.
michael@0: nsBlockReflowState michael@0:
This helper class is used to augment the nsHTMLReflowState michael@0: with other information needed by the block reflow logic during reflow. michael@0: It is a temporary object that is designed to live on the processor stack michael@0: and contains "running" state used by the blocks reflow logic.
michael@0: nsBlockBandData michael@0:
Another helper class that wraps up management of a space manager michael@0: (nsISpaceManager, nsSpaceManager) and nsBandData. It also assists in management michael@0: of floating elements. While nsSpaceManager is policy free, nsBlockBandData michael@0: provides specific HTML and CSS policy.
michael@0: nsBlockReflowContext michael@0:
A helper class that encapsulates the logic needed to reflow michael@0: a child block frame. This is used by the block code reflow a child block michael@0: and to reflow floating elements (which are to be treated as blocks according michael@0: to the CSS2 spec).
michael@0: nsLineBox michael@0:
A data class used to store line information for the block frame michael@0: code. Each line has a list of children (though the frames are linked together michael@0: across lines to maintain the sibling list for nsIFrame::FirstChild) and michael@0: some other state used to assist in incremental reflow.
michael@0: nsLineLayout michael@0:
This class is the line layout engine. Its a passive entity michael@0: in the sense that its the responsibility of the block/inline code to use michael@0: the class (this is done so that the line layout engine doesn't have to michael@0: manage child frame lists so that both nsBlockFrame and nsInlineFrame can michael@0: use the class).
michael@0: nsTextRun michael@0:
This is a data class used to store text run information. Text michael@0: runs are logically contiguous runs of text (they may or may not michael@0: be structurally contiguous). The block frame stores a pointer to a list michael@0: of nsTextRun's and during line layout provides the list to the nsLineLayout michael@0: engine so that when text is reflowed the text layout code (nsTextFrame) michael@0: can find related text to properly handle word breaking.
michael@0: michael@0:

michael@0: Frame construction methods

michael@0: When the blocks child list is modified (AppendFrames, InsertFrames, RemoveFrame) michael@0: the block code updates its nsLineBox list. Since each nsLineBox is typed michael@0: (some are marked "inline" and some are marked "block"), the update logic michael@0: maintains the invariant of "one block frame per block line". michael@0:

When structural changes are made to the blocks children (append/insert/remove) michael@0: the block code updates the line's and then marks the affected lines "dirty" michael@0: (each nsLineBox has a dirty bit). After the structural changes are finished michael@0: then the block will generate an incremental reflow command of type "ReflowDirty". michael@0:

michael@0: Line Layout

michael@0: Line layout consists of the placement of inline elements on a line until michael@0: there is no more room on the line. At that point the line is "broken" and michael@0: continued on the next line. This process continues until all inline elements michael@0: have been exhausted. The block code maintains a list of "nsLineBox"'s to michael@0: facilitate this. These are used instead of frames because they use less michael@0: memory and because it allows the block to directly control their behavior. michael@0:

The helper class nsLineLayout provides the majority of the line layout michael@0: behavior needed by the block. michael@0:

The block does keep "text-run" information around for the nsLineLayout michael@0: logic to use during reflow. Text runs keep track of logically adjacent michael@0: pieces of text within a block. This information is essential for properly michael@0: computing line and word breaking. Why? Well, because in html you can write michael@0: something like this: michael@0:

  <p>I <b>W</b>as thinking one day</p> michael@0:

Notice that the word "Was" is composed of two pieces of text, and that michael@0: they do not have the same parent (content or frame). To properly michael@0: reflow this and not break the word prematurely after the "W", the text-run michael@0: information is used by the text frame code to "look ahead" and prevent michael@0: premature breaking. michael@0:

Lines also keep track of the type of "break" that occurred on the line. michael@0: This is used, for example, to support html's "<br clear=left>" behavior. michael@0:

michael@0: Float Management

michael@0: Since child block elements are containing blocks for floats, the only michael@0: place where a block frame will see a float is as part of an inline line. michael@0: Consequently, the nsLineBox will only keep track of floats on inline michael@0: lines (saving storage for block lines). michael@0:

The nsLineLayout class and the block frame cooperate in the management michael@0: of floats. Since the frame construction code leaves a "placeholder" frame michael@0: in-flow where the float was found, when nsLineLayout reflows a placeholder michael@0: frame it knows to inform the block about it. That triggers the blocks "AddFloat" michael@0: logic which then determines where the float should be placed (on the michael@0: current line or below the current line). michael@0:

The block frame uses the space manager to manage the effects of floats, michael@0: namely the consumption of available space. For example, for a left aligned michael@0: floating element, the inline elements must be placed to the right of the michael@0: float. To simplify this process, the spacemanager is used to keep track michael@0: of available and busy space. Floats when placed mark space as busy and michael@0: the spacemanager will them compute the available space. Most of this logic michael@0: is handled by the nsBlockReflowState which uses a helper class, nsBlockBandData, michael@0: in concert with the space manager, to do the available space computations. michael@0:

michael@0: Child Block Placement

michael@0: Child block reflow is done primarily by using the nsBlockReflowContext michael@0: code. However, a key detail worth mentioning here is how margins are handled. michael@0: When the nsHTMLReflowState was created, we placed into it the logic for michael@0: computing margins, border and padding (among other things). Unfortunately, michael@0: given the css rules for sibling and generational margin collapsing, the michael@0: nsHTMLReflowState is unable to properly compute top and bottom margins. michael@0: Hence, the block frame and the nsBlockReflowContext code perform that function. michael@0: At the time that the nsBlockReflowContext was designed and implemented michael@0: we thought that it could compute the top-margin itself and then proceed michael@0: to place the child block element. However, that turned out to be wrong michael@0: (oh well) because the correct available space isn't known until after michael@0: the top margin is computed. Hence, there is some unfortunate duplication michael@0: of reflow state calculations present in the block frame code. michael@0:

michael@0: Bullets

michael@0: Another type of block frame is the "display: list-item". List-items use michael@0: nsBulletFrame's to manage bullet reflow. However, the block is responsible michael@0: for bullet placement. In most situations, the nsLineLayout class is used michael@0: to do the placement. However, if the first effective child of the block michael@0: is another block, then the block has to do the placement itself. michael@0:

michael@0: Blank lines

michael@0: Because our content model contains as much of the original source documents michael@0: content as possible, we end up with a lot of white space that ends up being michael@0: compressed into nothingness. This white space ends up impacting this logic michael@0: in several ways. For example: michael@0:

  <div> michael@0:
   <p>abc</p> michael@0:
   <p>def</p> michael@0:
  </div> michael@0:

In the content model for the above html, there is white space between michael@0: the various block elements (some after the <div>, some after the first michael@0: </p>, again after the second </p>). michael@0:

For css margin collapsing to work properly, each of those instances michael@0: of white space has to behave as if they didn't exist. Consequently, there michael@0: is special logic in the inline line reflow code, and in the nsBlockReflowContext michael@0: code and in the GetTopBlockChild method, to basically ignore such lines. michael@0:

michael@0: First-letter style

michael@0: The block contributes, in a small way, to first-letter style reflow. The michael@0: frame construction code is responsible for creating the list of child frames michael@0: for all frames, including the block. It manages the creation of letter-frames, michael@0: where appropriate, so that all the block has to do is reflow them almost michael@0: normally like other inline frames. michael@0:

There are two things different that the block does: michael@0:

It is responsible for calling nsLineLayout::SetFirstLetterStyleOK michael@0:
It is responsible for continuing to place frames on a line, even after michael@0: a frame has said "it can't fit". Normally during inline reflow, if a frame michael@0: comes back and says it can't fit, the block will end the line, push all michael@0: remaining frames to the next line and pick up the reflow from there after michael@0: making sure the frame that didn't fit is continued. For letter-frames, michael@0: this would result in the first-letter being on one line with the remaining michael@0: text on subsequent lines. Hence, the block code handles this special case. michael@0:
  michael@0:

michael@0: First-line style

michael@0: First-line is handled entirely by the frame construction code. michael@0:
  michael@0:
  michael@0: michael@0: