|
|
Line 198: |
Line 198: |
| === Style System === | | === Style System === |
|
| |
|
| ==== Quantum CSS (Stylo) ====
| | The style system section has been moved to https://firefox-source-docs.mozilla.org/layout/StyleSystemOverview.html |
| | |
| Starting with Firefox 57 and later, Gecko makes use of the parallel style system written in Rust that comes from Servo. There's an [https://hacks.mozilla.org/2017/08/inside-a-super-fast-css-engine-quantum-css-aka-stylo/ overview] of this with graphics to help explain what's going on. The [https://github.com/servo/servo/wiki/Layout-Overview Servo wiki] has some more details.
| |
| | |
| ==== Gecko ====
| |
| | |
| The rest of the style section section describes the Gecko style system used in Firefox 56 and earlier. Some bits may still apply, but it likely needs revising. | |
| | |
| In order to display the content, Gecko needs to compute the styles
| |
| relevant to each DOM node. It does this based on the model described in
| |
| the CSS specifications: this model applies to style specified in CSS
| |
| (e.g. by a 'style' element, an 'xml-stylesheet' processing instruction
| |
| or a 'style' attribute),
| |
| style specified by presentation attributes, and the default style
| |
| specified by our own user agent style sheets. There are two major
| |
| sets of data structures within the style system:
| |
| * first, data structures that represent sources of style data, such as CSS style sheets or data from stylistic HTML attributes
| |
| * second, data structures that represent computed style for a given DOM node.
| |
| These sets of data structures are mostly distinct (for example, they
| |
| store values in different ways).
| |
| | |
| The loading of CSS style sheets from the network is managed by the
| |
| [https://dxr.mozilla.org/mozilla-central/source/layout/style/Loader.h CSS loader];
| |
| they are then tokenized by the
| |
| [https://dxr.mozilla.org/mozilla-central/source/layout/style/nsCSSScanner.h CSS scanner]
| |
| and parsed by the
| |
| [https://dxr.mozilla.org/mozilla-central/source/layout/style/nsCSSParser.h CSS parser].
| |
| Those that are attached to the document also expose APIs to
| |
| script that are known as the CSS Object Model, or CSSOM.
| |
| | |
| The style sheets that apply to a document are managed by a class called
| |
| the [https://dxr.mozilla.org/mozilla-central/source/layout/style/nsStyleSet.h style set].
| |
| The style set interacts with the different types of
| |
| style sheets (representing CSS style sheets, presentational
| |
| attributes, and 'style' attributes) through two interfaces:
| |
| [http://dxr.mozilla.org/mozilla-central/source/layout/style/nsIStyleSheet.h nsIStyleSheet] for basic management of style sheets and
| |
| [http://dxr.mozilla.org/mozilla-central/source/layout/style/nsIStyleRuleProcessor.h nsIStyleRuleProcessor] for getting the style data out of them. Usually
| |
| the same object implements both interfaces, except in the most important
| |
| case, CSS style sheets, where there is a single rule processor for all
| |
| of the CSS style sheets in each origin (user/UA/author) of the CSS cascade.
| |
| | |
| The computed style data for an element/frame are exposed to the rest of
| |
| Gecko through the class mozilla::ComputedStyle (previously called
| |
| nsStyleContext). Rather than having a member variable for each
| |
| CSS property, it breaks up the properties into groups of related
| |
| properties called style structs. These style structs obey the rule that
| |
| all of the properties in a single struct either inherit by default (what
| |
| the CSS specifications call "Inherited: yes" in the definition of
| |
| properties; we call these inherited structs) or all are not inherited by
| |
| default (we call these reset structs). Separating the properties in
| |
| this way improves the ability to share the structs between similar
| |
| ComputedStyle objects and reduce the amount of memory needed to store
| |
| the style data. The ComputedStyle API exposes a method for getting each
| |
| struct, so you'll see code like
| |
| <code>sc->GetStyleText()->mTextAlign</code> for getting the value of the
| |
| text-align CSS property. (Frames (see the Layout section below) also
| |
| have the same
| |
| GetStyle* methods, which just forward the call to the frame's
| |
| ComputedStyle.)
| |
| | |
| The ComputedStyles form a tree structure, in a shape somewhat like the
| |
| content tree (except that we coalesce identical sibling ComputedStyles
| |
| rather than keeping two of them around; if the parents have been
| |
| coalesced then this can apply recursively and coalasce cousins, etc.;
| |
| we do not coalesce parent/child ComputedStyles).
| |
| The parent of a ComputedStyle has the style data that the ComputedStyle
| |
| inherits from when CSS inheritance occurs. This means that the parent
| |
| of the ComputedStyle for a DOM element is generally the ComputedStyle
| |
| for that DOM element's parent, since that's how CSS says inheritance
| |
| works.
| |
| | |
| The process of turning the style sheets into computed style data goes
| |
| through three main steps, the first two of which closely relate to the
| |
| [http://dxr.mozilla.org/mozilla-central/source/layout/style/nsIStyleRule.h nsIStyleRule] interface, which represents an immutable source of style
| |
| data, conceptually representing (and for CSS style rules, directly
| |
| storing) a set of property:value pairs. (It is similar to the idea of a
| |
| CSS style rule, except that it is immutable; this immutability allows
| |
| for significant optimization. When a CSS style rule is changed through
| |
| script, we create a new style rule.)
| |
| | |
| The first step of going from style sheets to computed style data is
| |
| finding the ordered sequence of style rules that apply to an element.
| |
| The order represents which rules override which other rules: if two
| |
| rules have a value for the same property, the higher ranking one wins.
| |
| (Note that there's another difference from CSS style rules: declarations
| |
| with !important are represented using a separate style rule.) This is
| |
| done by calling one of the nsIStyleRuleProcessor::RulesMatching methods.
| |
| The ordered sequence is stored in a
| |
| [http://en.wikipedia.org/wiki/Trie trie] called the rule tree: the path
| |
| from the root of the rule tree to any (leaf or non-leaf) node in the
| |
| rule tree represents a sequence of rules, with the highest ranking
| |
| farthest from the root. Each rule node (except for the root) has a
| |
| pointer to a rule, but since a rule may appear in many sequences, there
| |
| are sometimes many rule nodes pointing to the same rule. Once we have
| |
| this list we create a ComputedStyle (or find an appropriate existing
| |
| sibling) with the correct parent pointer (for inheritance) and rule node
| |
| pointer (for the list of rules), and a few other pieces of information
| |
| (like the pseudo-element).
| |
| | |
| The second step of going from style sheets to computed style data is
| |
| getting the winning property:value pairs from the rules. (This only
| |
| provides property:value pairs for some of the properties; the remaining
| |
| properties will fall back to inheritance or to their initial values
| |
| depending on whether the property is inherited by default.) We do this
| |
| step (and the third) for each style struct, the first time it is needed.
| |
| This is done in nsRuleNode::WalkRuleTree, where we ask each style rule
| |
| to fill in its property:value pairs by calling its MapRuleInfoInto
| |
| function. When called, the rule fills in only those pairs that haven't
| |
| been filled in already, since we're calling from the highest priority
| |
| rule to the lowest (since in many cases this allows us to stop before
| |
| going through the whole list, or to do partial computation that just
| |
| adds to data cached higher in the rule tree).
| |
| | |
| The third step of going from style sheets to computed style data (which
| |
| various caching optimizations allow us to skip in many cases) is
| |
| actually doing the computation; this generally means we transform the
| |
| style data into the data type described in the "Computed Value" line in
| |
| the property's definition in the CSS specifications. This
| |
| transformation happens in functions called nsRuleNode::Compute*Data,
| |
| where the * in the middle represents the name of the style struct. This
| |
| is where the transformation from the style sheet value storage format to
| |
| the computed value storage format happens.
| |
| | |
| Once we have the computed style data, we then store it: if a style struct
| |
| in the computed style data doesn't
| |
| depend on inherited values or on data from other style structs, then we
| |
| can cache it in the rule tree (and then reuse it, without recomputing
| |
| it, for any ComputedStyles pointing to that rule node). Otherwise, we
| |
| store it on the ComputedStyle (in which case it may be shared
| |
| with the ComputedStyle's descendant ComputedStyles).
| |
| This is where keeping inherited and
| |
| non-inherited properties separate is useful: in the common case of
| |
| relatively few properties being specified, we can generally cache the
| |
| non-inherited structs in the rule tree, and we can generally share the
| |
| inherited structs up and down the ComputedStyle tree.
| |
| | |
| The ownership models in style sheet structures are a mix of reference
| |
| counted structures (for things accessible from script) and directly
| |
| owned structures. ComputedStyles are reference counted, and own their
| |
| parents (from which they inherit), and rule nodes are garbage collected
| |
| with a simple mark and sweep collector (which often never needs to run).
| |
| | |
| * code: [http://dxr.mozilla.org/mozilla-central/source/layout/style/ layout/style/], where most files have useful one line descriptions at the top that show up in DXR
| |
| * Bugzilla: Style System (CSS)
| |
| * specifications
| |
| ** [http://www.w3.org/TR/CSS21/ CSS 2.1]
| |
| ** [http://www.w3.org/TR/css-2010/ CSS 2010, listing stable css3 modules]
| |
| ** [http://dev.w3.org/csswg/ CSS WG editors drafts] (often more current, but sometimes more unstable than the drafts on the technical reports page)
| |
| ** [http://dbaron.org/mozilla/visited-privacy Preventing attacks on a user's history through CSS :visited selectors]
| |
| * documentation
| |
| ** [http://www-archive.mozilla.org/newlayout/doc/style-system.html style system documentation] (somewhat out of date)
| |
|
| |
|
| === Layout === | | === Layout === |
|
| |
|
| Much of the layout code deals with operations on the frame tree (or
| | The layout section has been moved to https://firefox-source-docs.mozilla.org/layout/LayoutOverview.html |
| rendering tree). In the frame tree, each node represents a rectangle
| |
| (or, for SVG, other shapes). The frame tree has a shape similar to the
| |
| content tree, since many content nodes have one corresponding frame,
| |
| though it differs in a few ways, since some content nodes have more than
| |
| one frame or don't have any frames at all. When elements are
| |
| display:none in CSS or undisplayed for certain other reasons, they won't
| |
| have any frames. When elements are broken across lines or pages, they
| |
| have multiple frames; elements may also have multiple frames when
| |
| multiple frames nested inside each other are needed to display a single
| |
| element (for example, a table, a table cell, or many types of form
| |
| controls).
| |
| | |
| Each node in the frame tree is an instance of a class derived from
| |
| <code>nsIFrame</code>. As with the content tree, there is a substantial
| |
| type hierarchy, but the type hierarchy is very different: it includes
| |
| types like text frames, blocks and inlines, the various parts of tables,
| |
| flex and grid containers, and the various types of HTML form controls.
| |
| | |
| Frames are allocated within an arena owned by the PresShell. Each
| |
| frame is owned by its parent; frames are not reference counted, and code
| |
| must not hold on to pointers to frames. To mitigate potential security
| |
| bugs when pointers to destroyed frames, we use
| |
| [http://robert.ocallahan.org/2010/10/mitigating-dangling-pointer-bugs-using_15.html frame poisoning], which takes two parts. When a frame is destroyed
| |
| other than at the end of life of the presentation, we fill its memory
| |
| with a pattern consisting of a repeated pointer to inaccessible memory,
| |
| and then put the memory on a per-frame-class freelist. This means that
| |
| if code accesses the memory through a dangling pointer, it will either
| |
| crash quickly by dereferencing the poison pattern or it will find a
| |
| valid frame.
| |
| | |
| Like the content tree, frames must be accessed only from the UI thread.
| |
| | |
| The frame tree should not store any important data, i.e. any data that cannot
| |
| be recomputed on-the-fly. While the frame tree does
| |
| usually persist while a page is being displayed, frames are often
| |
| destroyed and recreated in response to certain style changes, and in the
| |
| future we may do the same to reduce memory use for pages that are
| |
| currently inactive. There were a number of cases where this rule was
| |
| violated in the past and we stored important data in the frame tree;
| |
| however, most (though not quite all) such cases are now fixed.
| |
| | |
| The rectangle represented by the frame is what CSS calls the element's
| |
| border box. This is the outside edge of the border (or the inside edge
| |
| of the margin). The margin lives outside the border; and the padding
| |
| lives inside the border. In addition to nsIFrame::GetRect, we also have
| |
| the APIs nsIFrame::GetPaddingRect to get the padding box (the outside
| |
| edge of the padding, or inside edge of the border) and
| |
| nsIFrame::GetContentRect to get the content box (the outside edge of the
| |
| content, or inside edge of the padding). These APIs may produce out of
| |
| date results when reflow is needed (or has not yet occurred).
| |
| | |
| In addition to tracking a rectangle, frames also track two overflow
| |
| areas: ink overflow and scrollable overflow. These overflow areas
| |
| represent the union of the area needed by the frame and by all its
| |
| descendants. The ink overflow is used for painting-related
| |
| optimizations: it is a rectangle covering all of the area that might be
| |
| painted when the frame and all of its descendants paint. The scrollable
| |
| overflow represents the area that the user should be able to scroll to
| |
| to see the frame and all of its descendants. In some cases differences
| |
| between the frame's rect and its overflow happen because of descendants
| |
| that stick out of the frame; in other cases they occur because of some
| |
| characteristic of the frame itself. The two overflow areas are
| |
| similar, but there are differences: for example, margins are part of
| |
| scrollable overflow but not ink overflow, whereas text-shadows are
| |
| part of ink overflow but not scrollable overflow.
| |
| | |
| When frames are broken across lines, columns, or pages, we create
| |
| multiple frames representing the multiple rectangles of the element.
| |
| The first one is the primary frame, and the rest are its continuations
| |
| (which are more likely to be destroyed and recreated during reflow).
| |
| These frames are linked together as continuations: they have a
| |
| doubly-linked list that can be used to traverse the continuations using
| |
| nsIFrame::GetPrevContinuation and nsIFrame::GetNextContinuation.
| |
| (Currently continuations always have the same style data, though we may
| |
| at some point want to break that invariant.)
| |
| | |
| Continuations are sometimes siblings of each other (i.e.
| |
| nsIFrame::GetNextContinuation and nsIFrame::GetNextSibling might return
| |
| the same frame), and sometimes not.
| |
| For example, if a paragraph contains a span which contains a link, and
| |
| the link is split across lines, then the continuations of the span are
| |
| siblings (since they are both children of the paragraph), but the
| |
| continuations of the link are not siblings (since each continuation of
| |
| the link is descended from a different continuation of the span).
| |
| Traversing the entire frame tree does '''not''' require explicit traversal
| |
| of any frames' continuations-list, since all of the continuations are
| |
| descendants of the element containing the break.
| |
| | |
| We also use continuations for cases (most importantly, bidi reordering,
| |
| where left-to-right text and right-to-left text need to be separated
| |
| into different continuations since they may not form a contiguous
| |
| rectangle) where the continuations should not be rewrapped during
| |
| reflow: we call these continuations fixed rather than fluid.
| |
| nsIFrame::GetNextInFlow and nsIFrame::GetPrevInFlow traverse only the
| |
| fluid continuations and do not cross fixed continuation boundaries.
| |
| | |
| If an inline frame has non-inline children, then we split the original
| |
| inline frame into parts. The original inline's children are
| |
| distributed into these parts like so: The children of the original
| |
| inline are grouped into runs of inline and non-inline, and runs of
| |
| inline get an inline parent, while runs of non-inline get an anonymous
| |
| block parent. We call this 'ib-splitting' or 'block-inside-inline splitting'.
| |
| This splitting proceeds recursively up the frame tree until all
| |
| non-inlines inside inlines are ancestors of a block frame with anonymous
| |
| block wrappers in between. This splitting maintains the relative order
| |
| between these child frames, and the relationship between the parts of a
| |
| split inline is maintained using an ib-sibling chain. It is important
| |
| to note that any wrappers created during frame construction (such as | |
| for tables) might not be included in the ib-sibling chain depending on
| |
| when this wrapper creation takes place.
| |
| | |
| TODO: nsBox craziness from https://bugzilla.mozilla.org/show_bug.cgi?id=524925#c64
| |
| | |
| TODO: link to documentation of block and inline layout
| |
| | |
| TODO: link to documentation of scrollframes
| |
| | |
| TODO: link to documentation of XUL frame classes
| |
| | |
| Code (note that most files in base and generic have useful one line descriptions at the top that show up in DXR):
| |
| * [http://dxr.mozilla.org/mozilla-central/source/layout/base/ layout/base/] contains objects that coordinate everything and a bunch of other miscellaneous things
| |
| * [http://dxr.mozilla.org/mozilla-central/source/layout/generic/ layout/generic/] contains the basic frame classes as well as support code for their reflow methods (ReflowInput, ReflowOutput)
| |
| * [http://dxr.mozilla.org/mozilla-central/source/layout/forms/ layout/forms/] contains frame classes for HTML form controls
| |
| * [http://dxr.mozilla.org/mozilla-central/source/layout/tables/ layout/tables/] contains frame classes for CSS/HTML tables
| |
| * [http://dxr.mozilla.org/mozilla-central/source/layout/mathml/ layout/mathml/] contains frame classes for MathML
| |
| * [http://dxr.mozilla.org/mozilla-central/source/layout/svg/ layout/svg/] contains frame classes for SVG
| |
| * [http://dxr.mozilla.org/mozilla-central/source/layout/xul/ layout/xul/] contains frame classes for the XUL box model and for various XUL widgets
| |
| | |
| Bugzilla:
| |
| * All of the components whose names begin with "Layout" in the "Core" product
| |
| | |
| Further documentation:
| |
| * Talk: [https://air.mozilla.org/introduction-to-graphics-layout-architecture/ Introduction to graphics/layout architecture] (Robert O'Callahan, 2014-04-18)
| |
| * Talk: [https://air.mozilla.org/bz-layout-and-styles/ Layout and Styles] (Boris Zbarsky, 2014-10-14)
| |
| | |
| | |
| ==== Frame Construction ====
| |
| | |
| Frame construction is the process of creating frames. This is done when styles change in ways that require frames to be created or recreated or when nodes are inserted into the document. The content tree and the frame tree don't have quite the same shape, and the frame construction process does some of the work of creating the right shape for the frame tree. It handles the aspects of creating the right shape that don't depend on layout information. So for example, frame construction handles the work needed to implement [http://www.w3.org/TR/CSS21/tables.html#anonymous-boxes table anonymous objects] but does not handle frames that need to be created when an element is broken across lines or pages.
| |
| | |
| The basic unit of frame construction is a run of contiguous children of a single parent element. When asked to construct frames for such a run of children, the frame constructor first determines, based on the siblings and parent of the nodes involved, where in the frame tree the new frames should be inserted. Then the frame constructor walks through the list of content nodes involved and for each one creates a temporary data structure called a '''frame construction item'''. The frame construction item encapsulates various information needed to create the frames for the content node: its style data, some metadata about how one would create a frame for this node based on its namespace, tag name, and styles, and some data about what sort of frame will be created. This list of frame construction items is then analyzed to see whether constructing frames based on it and inserting them at the chosen insertion point will produce a valid frame tree. If it will not, the frame constructor either fixes up the list of frame construction items so that the resulting frame tree would be valid or throws away the list of frame construction items and requests the destruction and re-creation of the frame for the parent element so that it has a chance to create a list of frame construction items that it <em>can</em> fix up.
| |
| | |
| Once the frame constructor has a list of frame construction items and an insertion point that would lead to a valid frame tree, it goes ahead and creates frames based on those items. Creation of a non-leaf frame recursively attempts to create frames for the children of that frame's element, so in effect frames are created in a depth-first traversal of the content tree.
| |
| | |
| The vast majority of the code in the frame constructor, therefore, falls into one of these categories:
| |
| * Code to determine the correct insertion point in the frame tree for new frames.
| |
| * Code to create, for a given content node, frame construction items. This involves some searches through static data tables for metadata about the frame to be created.
| |
| * Code to analyze the list of frame construction items.
| |
| * Code to fix up the list of frame construction items.
| |
| * Code to create frames from frame construction items.
| |
| | |
| Code: [http://dxr.mozilla.org/mozilla-central/source/layout/base/nsCSSFrameConstructor.h layout/base/nsCSSFrameConstructor.h] and [http://dxr.mozilla.org/mozilla-central/source/layout/base/nsCSSFrameConstructor.cpp layout/base/nsCSSFrameConstructor.cpp]
| |
| | |
| ==== Physical Sizes vs. Logical Sizes ====
| |
| | |
| TODO: Discuss inline-size (typically width) and block size (typically height), writing modes, and the various logical vs. physical size/rect types.
| |
| | |
| ==== Reflow ====
| |
| | |
| Reflow is the process of computing the positions and sizes of frames. (After all,
| |
| frames represent rectangles, and at some point we need to figure out
| |
| exactly *what* rectangle.) Reflow is done recursively, with each
| |
| frame's Reflow method calling the Reflow methods on that frame's
| |
| descendants.
| |
| | |
| In many cases, the correct results are defined by CSS specifications
| |
| (particularly [http://www.w3.org/TR/CSS21/visudet.html CSS 2.1]). In some cases, the details are not defined by
| |
| CSS, though in some (but not all) of those cases we are constrained by
| |
| Web compatibility. When the details are defined by CSS, however, the
| |
| code to compute the layout is generally structured somewhat differently
| |
| from the way it is described in the CSS specifications, since the CSS
| |
| specifications are generally written in terms of constraints, whereas
| |
| our layout code consists of algorithms optimized for incremental
| |
| recomputation.
| |
| | |
| The reflow generally starts from the root of the frame tree, though some other
| |
| types of frame can act as "reflow roots" and start a reflow from them
| |
| (nsTextControlFrame is one example; see the
| |
| [https://searchfox.org/mozilla-central/search?q=symbol:E_%3CT_nsFrameState%3E_NS_FRAME_REFLOW_ROOT&redirect=true NS_FRAME_REFLOW_ROOT] frame state bit).
| |
| Reflow roots must obey the invariant that a change inside one of their
| |
| descendants never changes their rect or overflow areas (though currently
| |
| scrollbars are reflow roots but don't quite obey this invariant).
| |
| | |
| In many cases, we want to reflow a part of the frame tree, and we want
| |
| this reflow to be efficient. For example, when content is added or
| |
| removed from the document tree or when styles change, we want the amount
| |
| of work we need to redo to be proportional to the amount of content. We
| |
| also want to efficiently handle a series of changes to the same content.
| |
| | |
| To do this, we maintain two bits on frames:
| |
| [https://searchfox.org/mozilla-central/search?q=symbol:E_%3CT_nsFrameState%3E_NS_FRAME_IS_DIRTY&redirect=true NS_FRAME_IS_DIRTY]
| |
| indicates that a frame and all of its descendants require reflow.
| |
| [https://searchfox.org/mozilla-central/search?q=symbol:E_%3CT_nsFrameState%3E_NS_FRAME_HAS_DIRTY_CHILDREN&redirect=true NS_FRAME_HAS_DIRTY_CHILDREN]
| |
| indicates that a frame has a descendant that
| |
| is dirty or has had a descendant removed (i.e., that it has a child that
| |
| has NS_FRAME_IS_DIRTY or NS_FRAME_HAS_DIRTY_CHILDREN or it had a child
| |
| removed). These bits allow coalescing of multiple updates; this
| |
| coalescing is done in PresShell, which tracks the set of reflow roots
| |
| that require reflow. The bits are set during calls to
| |
| [https://searchfox.org/mozilla-central/search?q=PresShell%3A%3AFrameNeedsReflow&path= PresShell::FrameNeedsReflow]
| |
| and are cleared during reflow.
| |
| | |
| The layout algorithms used by many of the frame classes are those
| |
| specified in CSS, which are based on the traditional document formatting
| |
| model, where widths are input and heights are output.
| |
| | |
| In some cases, however, widths need to be determined based on the
| |
| content. This depends on two ''intrinsic widths'': the minimum
| |
| intrinsic width (see [https://searchfox.org/mozilla-central/search?q=nsIFrame%3A%3AGetMinISize&path= nsIFrame::GetMinISize]) and the preferred intrinsic
| |
| width (see [https://searchfox.org/mozilla-central/search?q=nsIFrame%3A%3AGetPrefISize&path= nsIFrame::GetPrefISize]). The concept of what these widths
| |
| represent is best explained by describing what they are on a paragraph
| |
| containing only text: in such a paragraph the minimum intrinsic width
| |
| is the width of the longest word, and the preferred intrinsic width is
| |
| the width of the entire paragraph laid out on one line.
| |
| | |
| Intrinsic widths are invalidated separately from the dirty bits
| |
| described above. When a caller informs the pres shell that a frame
| |
| needs reflow (PresShell::FrameNeedsReflow), it passes one of three
| |
| options:
| |
| * eResize indicates that no intrinsic widths are dirty
| |
| * eTreeChange indicates that intrinsic widths on it and its ancestors are dirty (which happens, for example, if new children are added to it)
| |
| * eStyleChange indicates that intrinsic widths on it, its ancestors, and its descendants are dirty (for example, if the font-size changes)
| |
| | |
| Reflow is the area where the XUL frame classes (those that inherit from
| |
| nsBoxFrame or nsLeafBoxFrame) are most different from the rest. Instead
| |
| of using nsIFrame::Reflow, they do their layout computations using
| |
| intrinsic size methods called GetMinSize, GetPrefSize, and GetMaxSize
| |
| (which report intrinsic sizes in two dimensions) and a final layout
| |
| method called Layout. In many cases these methods defer some of the
| |
| computation to a separate object called a layout manager.
| |
| | |
| When an individual frame's Reflow method is called, most of the input is
| |
| provided on an object called ReflowInput and the output is filled
| |
| in to an object called ReflowOutput. After reflow, the caller
| |
| (usually the parent) is responsible for setting the frame's size based
| |
| on the metrics reported. (This can make some computations during reflow
| |
| difficult, since the new size is found in either the reflow state or the
| |
| metrics, but the frame's size is still the old size. However, it's
| |
| useful for invalidating the correct areas that need to be repainted.)
| |
| | |
| One major difference worth noting is that in XUL layout, the size of the
| |
| child is set prior to its parent calling its Layout method. (Once
| |
| invalidation uses display lists and is no longer tangled up in Reflow,
| |
| it may be worth switching non-XUL layout to work this way as well.)
| |
| | |
| ==== Painting ====
| |
| | |
| TODO: display lists (and event handling)
| |
| | |
| TODO: layers
| |
| | |
| ==== Pagination ====
| |
| | |
| The concepts behind pagination (also known as fragmentation) are a bit complicated, so for now we've split them off into a separate document: [[Gecko:Continuation_Model]]. This code is used for printing, print-preview, and multicolumn frames.
| |
|
| |
|
| === Dynamic change handling along the rendering pipeline === | | === Dynamic change handling along the rendering pipeline === |
|
| |
|
| The ability to make changes to the DOM from script is a major feature of the Web platform. Web authors rely on the concept (though there are a few exceptions, such as animations) that changing the DOM from script leads to the same rendering that would have resulted from starting from that DOM tree. They also rely on the performance characteristics of these changes: small changes to the DOM that have small effects should have proportionally small processing time. This means that Gecko needs to efficiently propagate changes from the content tree to style, the frame tree, the geometry of the frame tree, and the screen. | | The dynamic change handling section has been moved to https://firefox-source-docs.mozilla.org/layout/DynamicChangeHandling.html |
| | |
| For many types of changes, however, there is substantial overhead to processing a change, no matter how small. For example, reflow must propagate from the top of the frame tree down to the frames that are dirty, no matter how small the change. One very common way around this is to batch up changes. We batch up changes in lots of ways, for example:
| |
| * The content sink adds multiple nodes to the DOM tree before notifying listeners that they've been added. This allows notifying once about an ancestor rather than for each of its descendants, or notifying about a group of descendants all at once, which speeds up the processing of those notifications.
| |
| * We batch up nodes that require style reresolution (recomputation of selector matching and processing the resulting style changes). This batching is tree based, so it not only merges multiple notifications on the same element, but also merges a notification on an ancestor with a notification on its descendant (since ''some'' of these notifications imply that style reresolution is required on all descendants).
| |
| * We wait to reconstruct frames that require reconstruction (after destroying frames eagerly). This, like the tree-based style reresolution batching, avoids duplication both for same-element notifications and ancestor-descendant notifications, even though it doesn't actually do any tree-based caching.
| |
| * We postpone doing reflows until needed. As for style reresolution, this maintains tree-based dirty bits (see the description of NS_FRAME_IS_DIRTY and NS_FRAME_HAS_DIRTY_CHILDREN under Reflow).
| |
| * We allow the OS to queue up multiple invalidates before repainting (though we will likely switch to controlling that ourselves). This leads to a single repaint of some set of pixels where there might otherwise have been multiple (though it may also lead to more pixels being repainted if multiple rectangles are merged to a single one).
| |
| | |
| Having changes buffered up means, however, that various pieces of information (layout, style, etc.) may not be up-to-date. Some things require up-to-date information: for example, we don't want to expose the details of our buffering to Web page script since the programming model of Web page script assumes that DOM changes take effect "immediately", i.e., that the script shouldn't be able to detect any buffering. Many Web pages depend on this.
| |
| | |
| We therefore have ways to flush these different sorts of buffers. There are methods called FlushPendingNotifications on nsIDocument and nsIPresShell, that take an argument of what things to flush:
| |
| * Flush_Content: create all the content nodes from data buffered in the parser
| |
| * Flush_ContentAndNotify: the above, plus notify document observers about the creation of all nodes created so far
| |
| * Flush_Style: the above, plus make sure style data are up-to-date
| |
| * Flush_Frames: the above, plus make sure all frame construction has happened (currently the same as Flush_Style)
| |
| * Flush_InterruptibleLayout: the above, plus perform layout (Reflow), but allow interrupting layout if it takes too long
| |
| * Flush_Layout: the above, plus ensure layout (Reflow) runs to completion
| |
| * Flush_Display (should never be used): the above, plus ensure repainting happens
| |
| | |
| The major way that notifications of changes propagate from the content code to layout and other areas of code is through the nsIDocumentObserver and nsIMutationObserver interfaces. Classes can implement this interface to listen to notifications of changes for an entire document or for a subtree of the content tree.
| |
| | |
| WRITE ME: ... layout document observer implementations
| |
| | |
| TODO: how style system optimizes away rerunning selector matching
| |
| | |
| TODO: style changes and nsChangeHint
| |
|
| |
|
| === Refresh driver === | | === Refresh driver === |