diff options
Diffstat (limited to 'layout/docs')
-rw-r--r-- | layout/docs/DynamicChangeHandling.md | 83 | ||||
-rw-r--r-- | layout/docs/LayoutOverview.md | 529 | ||||
-rw-r--r-- | layout/docs/StyleSystemOverview.md | 173 | ||||
-rw-r--r-- | layout/docs/index.rst | 14 |
4 files changed, 794 insertions, 5 deletions
diff --git a/layout/docs/DynamicChangeHandling.md b/layout/docs/DynamicChangeHandling.md new file mode 100644 index 0000000000..50df613412 --- /dev/null +++ b/layout/docs/DynamicChangeHandling.md @@ -0,0 +1,83 @@ +# 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. + +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 diff --git a/layout/docs/LayoutOverview.md b/layout/docs/LayoutOverview.md new file mode 100644 index 0000000000..0375ab5480 --- /dev/null +++ b/layout/docs/LayoutOverview.md @@ -0,0 +1,529 @@ +# Layout Overview + +Much of the layout code deals with operations on the frame tree (or +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 +`nsIFrame`. 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 [frame +poisoning](http://robert.ocallahan.org/2010/10/mitigating-dangling-pointer-bugs-using_15.html), +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): + +- [layout/base/](http://dxr.mozilla.org/mozilla-central/source/layout/base/) + contains objects that coordinate everything and a bunch of other + miscellaneous things +- [layout/generic/](http://dxr.mozilla.org/mozilla-central/source/layout/generic/) + contains the basic frame classes as well as support code for their + reflow methods (ReflowInput, ReflowOutput) +- [layout/forms/](http://dxr.mozilla.org/mozilla-central/source/layout/forms/) + contains frame classes for HTML form controls +- [layout/tables/](http://dxr.mozilla.org/mozilla-central/source/layout/tables/) + contains frame classes for CSS/HTML tables +- [layout/mathml/](http://dxr.mozilla.org/mozilla-central/source/layout/mathml/) + contains frame classes for MathML +- [layout/svg/](http://dxr.mozilla.org/mozilla-central/source/layout/svg/) + contains frame classes for SVG +- [layout/xul/](http://dxr.mozilla.org/mozilla-central/source/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: [Introduction to graphics/layout + architecture](https://air.mozilla.org/introduction-to-graphics-layout-architecture/) + (Robert O\'Callahan, 2014-04-18) +- Talk: [Layout and + Styles](https://air.mozilla.org/bz-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 [table anonymous +objects](http://www.w3.org/TR/CSS21/tables.html#anonymous-boxes) 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>`{=html}can`</em>`{=html} +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: +[layout/base/nsCSSFrameConstructor.h](http://dxr.mozilla.org/mozilla-central/source/layout/base/nsCSSFrameConstructor.h) +and +[layout/base/nsCSSFrameConstructor.cpp](http://dxr.mozilla.org/mozilla-central/source/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 [CSS 2.1](http://www.w3.org/TR/CSS21/visudet.html)). 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 +[NS\_FRAME\_REFLOW\_ROOT](https://searchfox.org/mozilla-central/search?q=symbol:E_%3CT_nsFrameState%3E_NS_FRAME_REFLOW_ROOT&redirect=true) +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: +[NS\_FRAME\_IS\_DIRTY](https://searchfox.org/mozilla-central/search?q=symbol:E_%3CT_nsFrameState%3E_NS_FRAME_IS_DIRTY&redirect=true) +indicates that a frame and all of its descendants require reflow. +[NS\_FRAME\_HAS\_DIRTY\_CHILDREN](https://searchfox.org/mozilla-central/search?q=symbol:E_%3CT_nsFrameState%3E_NS_FRAME_HAS_DIRTY_CHILDREN&redirect=true) +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 +[PresShell::FrameNeedsReflow](https://searchfox.org/mozilla-central/search?q=PresShell%3A%3AFrameNeedsReflow&path=) +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 +[nsIFrame::GetMinISize](https://searchfox.org/mozilla-central/search?q=nsIFrame%3A%3AGetMinISize&path=)) +and the preferred intrinsic width (see +[nsIFrame::GetPrefISize](https://searchfox.org/mozilla-central/search?q=nsIFrame%3A%3AGetPrefISize&path=)). +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 + +Pagination (also known as fragmentation) is a concept used in printing, +print-preview, and multicolumn layout. + +### Continuations in the Frame Tree + +To render a DOM node, represented as `nsIContent` object, Gecko creates +zero or more frames (`nsIFrame` objects). Each frame represents a +rectangular area usually corresponding to the node\'s CSS box as +described by the CSS specs. Simple elements are often representable with +exactly one frame, but sometimes an element needs to be represented with +more than one frame. For example, text breaking across lines: + + xxxxxx AAAA + AAA xxxxxxx + +The A element is a single DOM node but obviously a single rectangular +frame isn\'t going to represent its layout precisely. + +Similarly, consider text breaking across pages: + + | BBBBBBBBBB | + | BBBBBBBBBB | + +------------+ + + +------------+ + | BBBBBBBBBB | + | BBBBBBBBBB | + | | + +Again, a single rectangular frame cannot represent the layout of the +node. Columns are similar. + +Another case where a single DOM node is represented by multiple frames +is when a text node contains bidirectional text (e.g. both Hebrew and +English text). In this case, the text node and its inline ancestors are +split so that each frame contains only unidirectional text. + +The first frame for an element is called the **primary frame**. The +other frames are called **continuation frames**. Primary frames are +created by `nsCSSFrameConstructor` in response to content insertion +notifications. Continuation frames are created during bidi resolution, +and during reflow, when reflow detects that a content element cannot be +fully laid out within the constraints assigned (e.g., when inline text +will not fit within a particular width constraint, or when a block +cannot be laid out within a particular height constraint). + +Continuation frames created during reflow are called \"fluid\" +continuations (or \"in-flows\"). Other continuation frames (currently, +those created during bidi resolution), are, in contrast, \"non-fluid\". +The `NS_FRAME_IS_FLUID_CONTINUATION` state bit indicates whether a +continuation frame is fluid or not. + +The frames for an element are put in a doubly-linked list. The links are +accessible via `nsIFrame::GetNextContinuation` and +`nsIFrame::GetPrevContinuation`. If only fluid continuations are to be +accessed, `nsIFrame::GetNextInFlow` and `nsIFrame::GetPrevInFlow` are +used instead. + +The following diagram shows the relationship between the original frame +tree considering just primary frames, and a possible layout with +breaking and continuations: + + Original frame tree Frame tree with A broken into three parts + Root Root + | / | \ + A A1 A2 A3 + / \ / | | | + B C B C1 C2 C3 + | /|\ | | | \ | + D E F G D E F G1 G2 + +Certain kinds of frames create multiple child frames for the same +content element: + +- `nsPageSequenceFrame` creates multiple page children, each one + associated with the entire document, separated by page breaks +- `nsColumnSetFrame` creates multiple block children, each one + associated with the column element, separated by column breaks +- `nsBlockFrame` creates multiple inline children, each one associated + with the same inline element, separated by line breaks, or by + changes in text direction +- `nsTableColFrame` creates non-fluid continuations for itself if it + has span=\"N\" and N \> 1 +- If a block frame is a multi-column container and has + `column-span:all` children, it creates multiple `nsColumnSetFrame` + children, which are linked together as non-fluid continuations. + Similarly, if a block frame is within a multi-column formatting + context and has `column-span:all` children, it is chopped into + several flows, which are linked together as non-fluid continuations + as well. See documentation and example frame trees in + [`nsCSSFrameConstructor::ConstructBlock()`](https://searchfox.org/mozilla-central/rev/d24696b5abaf9fb75f7985952eab50d5f4ed52ac/layout/base/nsCSSFrameConstructor.cpp#10431). + +#### Overflow Container Continuations + +Sometimes the content of a frame needs to break across pages even though +the frame itself is complete. This usually happens if an element with +fixed height has overflow that doesn\'t fit on one page. In this case, +the completed frame is \"overflow incomplete\", and special +continuations are created to hold its overflow. These continuations are +called \"overflow containers\". They are invisible, and are kept on a +special list in their parent. See documentation in +[nsContainerFrame.h](https://searchfox.org/mozilla-central/source/layout/generic/nsContainerFrame.h) +and example trees in [bug 379349 comment +3](https://bugzilla.mozilla.org/show_bug.cgi?id=379349#c3). + +This infrastructure was extended in [bug +154892](https://bugzilla.mozilla.org/show_bug.cgi?id=154892) to also +manage continuations for absolutely-positioned frames. + +#### Relationship of continuations to frame tree structure + +It is worth emphasizing two points about the relationship of the +prev-continuation / next-continuation linkage to the existing frame tree +structure. + +First, if you want to traverse the frame tree or a subtree thereof to +examine all the frames once, you do `<em>`{=html}not`</em>`{=html} want +to traverse next-continuation links. All continuations are reachable by +traversing the `GetNextSibling` links from the result of `GetFirstChild` +for all child lists. + +Second, the following property holds: + +- Consider two frames F1 and F2 where F1\'s next-continuation is F2 + and their respective parent frames are P1 and P2. Then either P1\'s + next continuation is P2, or P1 == P2, because P is responsible for + breaking F1 and F2. + +In other words, continuations are sometimes siblings of each other, and +sometimes not. If their parent content was broken at the same point, +then they are not siblings, since they are children of different +continuations of the parent. So in the frame tree for the markup + +` <p>This is <b><i>some <br/>text</i></b>.</p>` + +the two continuations for the `b` element are siblings (unless the line +break is also a page break), but the two continuations for the `i` +element are not. + +There is an exception to that property when F1 is a first-in-flow float +placeholder. In that case F2\'s parent will be the next-in-flow of F1\'s +containing block. + +### Reflow statuses + +The aStatus argument of Reflow reflects that. `IsComplete()` means that +we reflowed all the content and no more next-in-flows are needed. At +that point there may still be next in flows, but the parent will delete +them. `IsIncomplete()` means \"some content did not fit in this frame\". +`IsOverflowIncomplete()` means that the frame is itself complete, but +some of its content didn\'t fit: this triggers the creation of overflow +containers for the frame\'s continuations. `IsIncomplete()` and +`NextInFlowNeedsReflow()` means \"some content did not fit in this frame +AND it must be reflowed\". These values are defined and documented in +[nsIFrame.h](https://searchfox.org/mozilla-central/source/layout/generic/nsIFrame.h) +(search for \"Reflow status\"). + +### Dynamic Reflow Considerations + +When we reflow a frame F with fluid continuations, two things can +happen: + +- Some child frames do not fit in the passed-in width or height + constraint. These frames must be \"pushed\" to F\'s next-in-flow. If + F has no next-in-flow, we must create one under F\'s parent\'s + next-in-flow \-\-- or if F\'s parent is managing the breaking of F, + then we create F\'s next in flow directly under F\'s parent. If F is + a block, it pushes overflowing child frames to its \"overflow\" + child list and forces F\'s next in flow to be reflowed. When we + reflow a block, we pull the child frames from the prev-in-flow\'s + overflow list into the current frame. +- All child frames fit in the passed-in width or height constraint. + Then child frames must be \"pulled\" from F\'s next-in-flow to fill + in the available space. If F\'s next-in-flow becomes empty, we may + be able to delete it. + +In both of these situations we might end up with a frame F containing +two child frames, one of which is a continuation of the other. This is +incorrect. We might also create holes, where there are frames P1 P2 and +P3, P1 has child F1 and P3 has child F2, but P2 has no F child. + +A strategy for avoiding these issues is this: When pulling a frame F2 +from parent P2 to prev-in-flow P1, if F2 is a breakable container, then: + +- If F2 has no prev-in-flow F1 in P1, then create a new primary frame + F1 in P1 for F2\'s content, with F2 as its next-in-flow. +- Pull children from F2 to F1 until F2 is empty or we run out of + space. If F2 goes empty, pull from the next non-empty next-in-flow. + Empty continuations with no next-in-flows can be deleted. + +When pushing a frame F1 from parent P1 to P2, where F1 has a +next-in-flow F2 (which must be a child of P2): + +- Merge F2 into F1 by moving all F2\'s children into F1, then deleting + F2 + +For inline frames F, we have our own custom strategy that coalesces +adjacent inline frames. This need not change. + +We do need to implement this strategy when F is a normal in-flow block, +a floating block, and eventually an absolutely positioned block. diff --git a/layout/docs/StyleSystemOverview.md b/layout/docs/StyleSystemOverview.md new file mode 100644 index 0000000000..bed5fd057f --- /dev/null +++ b/layout/docs/StyleSystemOverview.md @@ -0,0 +1,173 @@ +# Style System Overview + +## Quantum CSS (Stylo) + +Starting with Firefox 57 and later, Gecko makes use of the parallel +style system written in Rust that comes from Servo. There\'s an +[overview](https://hacks.mozilla.org/2017/08/inside-a-super-fast-css-engine-quantum-css-aka-stylo/) +of this with graphics to help explain what\'s going on. The [Servo +wiki](https://github.com/servo/servo/wiki/Layout-Overview) 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 [CSS +loader](https://dxr.mozilla.org/mozilla-central/source/layout/style/Loader.h); +they are then tokenized by the [CSS +scanner](https://dxr.mozilla.org/mozilla-central/source/layout/style/nsCSSScanner.h) +and parsed by the [CSS +parser](https://dxr.mozilla.org/mozilla-central/source/layout/style/nsCSSParser.h). +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 [style +set](https://dxr.mozilla.org/mozilla-central/source/layout/style/nsStyleSet.h). +The style set interacts with the different types of style sheets +(representing CSS style sheets, presentational attributes, and \'style\' +attributes) through two interfaces: +[nsIStyleSheet](http://dxr.mozilla.org/mozilla-central/source/layout/style/nsIStyleSheet.h) +for basic management of style sheets and +[nsIStyleRuleProcessor](http://dxr.mozilla.org/mozilla-central/source/layout/style/nsIStyleRuleProcessor.h) +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 `sc->GetStyleText()->mTextAlign` 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 +[nsIStyleRule](http://dxr.mozilla.org/mozilla-central/source/layout/style/nsIStyleRule.h) +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 [trie](http://en.wikipedia.org/wiki/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: + [layout/style/](http://dxr.mozilla.org/mozilla-central/source/layout/style/), + where most files have useful one line descriptions at the top that + show up in DXR +- Bugzilla: Style System (CSS) +- specifications + - [CSS 2.1](http://www.w3.org/TR/CSS21/) + - [CSS 2010, listing stable css3 + modules](http://www.w3.org/TR/css-2010/) + - [CSS WG editors drafts](http://dev.w3.org/csswg/) (often more + current, but sometimes more unstable than the drafts on the + technical reports page) + - [Preventing attacks on a user\'s history through CSS :visited + selectors](http://dbaron.org/mozilla/visited-privacy) +- documentation + - [style system + documentation](http://www-archive.mozilla.org/newlayout/doc/style-system.html) + (somewhat out of date) diff --git a/layout/docs/index.rst b/layout/docs/index.rst index 16d9df8b05..39455a1075 100644 --- a/layout/docs/index.rst +++ b/layout/docs/index.rst @@ -1,8 +1,9 @@ -Layout & CSS -============ +Style system (CSS) & Layout +=========================== -Here contains design documents for the Gecko's style system and layout engine. -They live in the mozilla-central repository under layout/docs directory. +Here contains the overview and design documents for Firefox's layout engine and +style system. They live in the mozilla-central repository under `layout/docs +<https://searchfox.org/mozilla-central/source/layout/docs>`__ directory. `Layout page <https://wiki.mozilla.org/Platform/Layout>`__ on mozilla wiki contains general information about layout and the layout team at Mozilla. @@ -10,6 +11,9 @@ contains general information about layout and the layout team at Mozilla. .. toctree:: :maxdepth: 1 - AccessibleCaret + StyleSystemOverview + LayoutOverview + DynamicChangeHandling Reftest LayoutDebugger + AccessibleCaret |