You are currently viewing a snapshot of www.mozilla.org taken on April 21, 2008. Most of this content is highly out of date (some pages haven't been updated since the project began in 1998) and exists for historical purposes only. If there are any pages on this archive site that you think should be added back to www.mozilla.org, please file a bug.
Reflow is the process by which the geometry of the layout engine's formatting objects are computed. The HTML formatting objects are called frames: a frame corresponds to the geometric information for (roughly) a single element in the content model; the frames are arranged into a hierarchy that parallels the containment hierarchy in the content model. A frame is rectangular, with width, height, and an offset from the parent frame that contains it.
More than one frame may be needed to represent a single element from the content model; for example, text that wraps is broken into several frames, one per wrapped line. In this case, the primary frame is the frame containing the first line of text, with continuing frames (or continuations) created for subsequent lines.
HTML uses a flow based layout model, meaning that most of the time it is possible to compute the geometry in a single pass. Elements later ``in the flow'' typically do not affect the geometry of elements that are earlier ``in the flow'', so layout can proceed left-to-right, top-to-bottom through the document. There are exceptions to this rule: most notably, HTML tables may require more than one pass.
The XUL box layout model, on the other hand, is constraint based, meaning that geometric preferences and constraints of neighboring elements are taken into consideration before the elements' final geometry can be computed. The box is the geometric primitive for the XUL layout model.
All HTML reflow, including the intial reflow, begins at
the root frame, which corresponds to the
<html>
element of the HTML document. Reflow
proceeds recursively through some or all of the frame hierarchy,
computing geometric information for each frame object that requires
it. Reflow may have the side-effect of creating new continuation
frames, for example, for a text frame when the text must be
wrapped.
Some reflows are immediate in response to user or script
actions; for example, resizing the window or changing the
document's default font. These are dispatched directly from the
presentation shell (e.g.,
nsIPresShell::StyleChangeReflow
), and affect the
entire frame tree. Other reflows are incremental and are
dealt with asynchronously; for example, when content streams in
from the network. Incremental reflows are queued by the
presentation shell for batched dispatch.
The reflow state object,
nsHTMLReflowState
, is used to pass constraining
information ``down'' from parent frames to child frames. For
example, a <div>
with a constrained width (e.g.,
set via the CSS width
property) would note this in the
reflow state object before flowing its children.
When reflow begins, the root reflow state is initialized
with information about the top-level container for the document's
presentation; e.g., the width and height of the application
window. This is passed as an argument to the Reflow
method of the root frame in the frame hierarchy.
Each container frame constructs a new reflow state object (based on its container's reflow state object) in which the container will reflow its children. Most of the constraints in the new reflow state are computed when the state is created; for example, the available space in the new reflow state is computed by subtracting the container frame's border and padding from the parent reflow state's available space.
All reflows have a reason, which is maintained in the reflow state object (and may mutate, as described below). The reflow reason controls how a frame reacts during a reflow, and is one of the following:
Initial
, for the very first time that the
frame hierarchy is flowed. In this case, a frame knows that there
is no residual state that can be used to simplify geometry
computation.
Incremental
, when something in the frame
tree changes; for example, when more content is read from the
network, or some script manipulates the DOM. An incremental reflow
is targeted at a single frame in the frame
hierarchy. During an incremental reflow, a frame can assume that
none of the constraints computed ``from above'' (for example,
available width) have changed; instead, something ``within'' the
frame has changed, which may have bottom-up impact to the frame
hierarchy.
Resize
, when the containing boundary for
the frame hierarchy changes. During a resize reflow, the frame can
assume that none of the frame's internal state (e.g., a text
frame's text) has changed; instead, a top-down change in the
layout constraints has occured.
StyleChange
, when the entire frame
hierarchy must be traversed to recover from stylistic change; for
example, a change in the default font size.
Dirty
, when a container frame has
consolidated several individual Incremental
reflows
that have been targeted at its child frames.
Initial, incremental, resize, and style change reflows may each be performed as an immediate ``global'' reflow from the presentation shell:
A dirty reflow is never performed directly from the presentation shell. Instead, a dirty reflow is detected when an incremental reflow reaches its target frame, described below.
The reflow metrics object,
nsHTMLReflowMetrics
, is used to propagate information
from child frames back to the parent. For example, the dimensions
of each child frame of an unconstrained <div>
would be passed back to the <div>
's frame via
the nsHTMLReflowMetrics
object.
Although all of the reflow in Gecko attempts to re-use as much
existing state as possible (and is in therefore some sense
``incremental'') an Incremental
reflow corresponds to
a reflow that is specifically targeted at an individual frame in
the frame hierarchy. A frame requests a Incremental
reflow (or one is requested on a frame's behalf) when something
about the frame itself has changed.
Scheduling. To request (or schedule) an
incremental reflow (e.g., in response to a change in the content
model), a reflow command object is created and passed to
the presentation shell via the
nsIPresShell::AppendReflowCommand
method. The
presentation shell does not process the command
immediately. Instead, it queues the command, and processes it
asynchronously along with other queued reflow commands en
masse.
Coalescing. As described below, the reflow command has a type and a target frame. Multiple reflow commands with the same type and target frame are coalesced: the presentation shell simply refuses to add subsequent commands of the same type for the same frame to the queue. A caller may also cancel a reflow command that is in the queue; e.g., if the target frame is destroyed.
Dispatch. The presentation shell processes the
reflow queue by removing a single reflow command from the queue and
dispatching it to its target frame.
(But cf. the
reflow tree
work, which will remove several commands from the queue at
once.)
A path is built from the target frame to the root
frame and stored in the reflow command. A reflow
state object is created with a reflow reason of
Incremental
, the reflow command is stored in the
state, and the Reflow
method of the root frame is
invoked.
Processing. The root frame notes the
Incremental
reflow reason specified in the reflow
state, and inspects the path contained within the reflow command
object. Specifically, it extracts the next frame along
the path from the reflow command object, creates its own reflow
state, also with an Incremental
reason, and invokes
the Reflow
method of the next frame.
The incremental reflow proceeds recursively through the frame hierarchy. Each frame along the incremental reflow path (as specified in the reflow command object) extracts the next frame and dispatches the reflow downward. In order to correctly dispatch the reflow to the child frame, the frame may need to perform some state recovery; for example, a block frame will traverse its line list to recover the space occupied by floated frames.
At some point, the incremental reflow reaches the target frame, at which point the reflow command's type becomes significant.
ContentChanged
indicates that the content
corresponding to the target frame has changed somehow; for
example, the text associated with a text frame has been modified.
In reality, the only frame that responds to
this sort of reflow is the block frame. The block frame treats
this sort of change as a `full reflow' (i.e., as a resize). This
makes me believe that we could probably eliminate this class
altogether.
StyleChanged
indicates that the stylistic
information corresponding to the target frame has changed; for
example, the font size has increased. This causes the frame to
mutate the reflow state's reason to
StyleChange
, which is propagated recursively to the
entire subtree beneath the target frame.
ReflowDirty
indicates that container frame
has decided to coalesce several incremental reflows targeted at
its children into a single reflow. The container frame maintains
the necessary state to determine which children must be reflowed.
UserDefined
, for ``special
situations''.
Currently, this is only used by the
viewport frame to schedule a reflow to reflow all of
the viewport's fixed-position frames. We should probably try to
eliminate it.
An incremental reflow may damage other parts of the frame hierarchy; for example, changing the size of the font used in a specific paragraph may cause the paragraph to grow or shrink. A container must therefore propagate any damage that the incremental reflow of the child frame caused, possibly reflowing other children as well.
If several incremental changes occur in the same part of the frame
hierarchy, it is possible to have several Incremental
reflows targeted at nearby frames. In this case, it is likely that
the individual Incremental
reflows will end up doing
redundant work. For example, each keystroke typed into a text
widget could generate a separate Incremental
reflow
targeted at the text frame. Were we to process each individually,
the text widget would be flowed once for each keystroke, which
would be wasteful if the latency of an individual reflow exceeds
the speed at which text is being typed. The purpose of the
Dirty
reflow is to allow these individual reflows to
be coalesced intelligently.
A frame that decides it needs a dirty reflow sets the
NS_FRAME_IS_DIRTY
state bit on itself, and then calls
the ReflowDirtyChild
method on its parent frame. In
ReflowDirtyChild
, the parent frame sets the
NS_FRAME_HAS_DIRTY_CHILD
state bit on itself, and does
any bookkeeping necessary to remember which child is dirty (for
example, the block frame marks the linebox dirty that contains the
child frame). The parent frame can then either decide to schedule a
ReflowDirty
Incremental
reflow targeted
at itself, or to delegate that responsibility to its
parent. If it decides to delegate, then it sets the
NS_FRAME_IS_DIRTY
state bit on itself and recursively
calls ReflowDirtyChild
.
Eventually, the ReflowDirty
Incremental
reflow is dispatched, and arrives at the container frame that
scheduled it. The target frame recovers its bookkeeping information
(e.g., the block frame iterates through the dirty lineboxes), and
reflows the dirty child frames.
Isn't information lost if a
ReflowDirty
Incremental
reflow coalesces
different kinds of incremental reflows (e.g., a
ContentChanged
with a
StyleChanged
)? No, because these kinds of
reflows aren't coalesced; instead, they're directly
enqueued to the presentation shell's reflow queue.
As mentioned above, HTML and XUL have fundamentally different
layout models, the former being a flow-based model, and the latter
being a constraint based model. These differences are mediated by
two adapter classes: nsBoxFrame
and
nsBoxToBlockAdaptor
.
nsBoxFrame
is an HTML frame that ``wraps'' a XUL
box. Its purpose is to convert HTML reflows their box analog.
nsBoxToBlockAdaptor
is a XUL box that wraps an HTML
block frame, used to convert changes in the box layout into HTML
reflows.