OLD | NEW |
(Empty) | |
| 1 # Whitespace LayoutObjects |
| 2 |
| 3 Text nodes which only contain whitespace sometimes have a layout object and |
| 4 sometimes they don't. This document tries to explain why and how these layout |
| 5 objects are created. |
| 6 |
| 7 ## Why |
| 8 |
| 9 For layout purposes, whitespace nodes are sometimes significant, but not |
| 10 always. In Blink, we try to create as few of them as possible to save memory, |
| 11 and save CPU by having fewer layout objects to traverse. |
| 12 |
| 13 ### Inline flow |
| 14 |
| 15 Whitespace typically matters in an inline flow context. Example: |
| 16 |
| 17 <span>A</span> </span>B</span> |
| 18 |
| 19 If we didn't create a LayoutObject for the whitespace node between the two |
| 20 spans, we would have rendered the markup above as "AB" as the span layout |
| 21 objects would have been siblings in the layout tree. |
| 22 |
| 23 ### Block flow |
| 24 |
| 25 Whitespace typically doesn't matter in a block flow context. Example: |
| 26 |
| 27 <div>A</div> <div>B</div> |
| 28 |
| 29 In the example above, the whitespace node between the divs would not contribute |
| 30 to layout/rendering. Hence, we can skip creating a LayoutText for it. |
| 31 |
| 32 ### Out-of-flow |
| 33 |
| 34 Out-of-flow elements like absolutely positioned elements do not affect inline |
| 35 or block in-flow layout. That means we can skip such elements when considering |
| 36 the need whitespace layout objects. |
| 37 |
| 38 Example: |
| 39 |
| 40 <div><span style="position:absolute">A</span> </span>B</span></div> |
| 41 |
| 42 In the example above, we don't need to create a whitespace layout object since |
| 43 it will be the first in-flow child of the block, and will not contribute to the |
| 44 layout/rendering. |
| 45 |
| 46 Example: |
| 47 |
| 48 <span>A</span> <span style="position:absolute">Z</span> <span>B</span> |
| 49 |
| 50 In the example above, we need to create a whitespace layout object to separate |
| 51 the A and B in the rendering. However, we only need to create a layout object |
| 52 for one of the whitespace nodes as whitespaces collapse. |
| 53 |
| 54 ### Preformatted text and editing |
| 55 |
| 56 Some values of the CSS white-space property will cause whitespace not to |
| 57 collapse and affect layout and rendering also in block layout. In those cases |
| 58 we always create layout objects for whitespace nodes. |
| 59 |
| 60 Whitespace nodes are also significant in editing mode. |
| 61 |
| 62 ## How |
| 63 |
| 64 ### Initial layout tree attachment |
| 65 |
| 66 When attaching a layout tree, we walk the flat-tree in a depth-first order |
| 67 walking the siblings from left to right. As we reach a text node, we check if |
| 68 we need to create a layout object in textLayoutObjectIsNeeded(). In particular, |
| 69 if we found that it only contains whitespace, we start traversing the |
| 70 previously added layout object siblings, skipping out-of-flow elements, to |
| 71 decide if we need to create a whitespace layout object. |
| 72 |
| 73 Important methods: |
| 74 |
| 75 Text::attachLayoutTree() |
| 76 Text::textLayoutObjectIsNeeded() |
| 77 |
| 78 ### Layout object re-attachment |
| 79 |
| 80 During style recalculation, elements whose computed value for display change |
| 81 will have its layout sub-tree re-attached. Attachment of the descendant layout |
| 82 objects happens the same way as for inital layout tree attachment, but the |
| 83 interesting part for whitespace layout objects is how they are affected by |
| 84 re-attachment of sibling elements. Sibling nodes may or may not be re-attached |
| 85 during the same style recalc traversal depending on whether they change their |
| 86 computed display value or not. |
| 87 |
| 88 #### Style recalc traversal |
| 89 |
| 90 An important pre-requisite for how whitespace layout objects are re-attached |
| 91 is the traversal order we use for style recalc. The current traversal order |
| 92 makes it hard or costly to implement whitespace re-attachement without bugs in |
| 93 the presence of shadow trees, but let's describe what we do here. |
| 94 |
| 95 Style recalc happens in the shadow-including tree order with the exception that |
| 96 siblings are traversed from right to left. The ::before and ::after pseudo |
| 97 elements are recalculated in left-to-right (!?!) order, before and after |
| 98 shadow-including descendants respectively. |
| 99 |
| 100 Inheritance happens down the flat-tree. Since we are not traversing in |
| 101 flat-tree order, we implement this propagation from slot/content elements down |
| 102 to assigned/distributed nodes by marking these nodes with LocalStyleChange when |
| 103 we need to do inheritance propagation (done in HTMLSlotElement::willRecalcStyle |
| 104 for instance). This works since light-tree children are traversed after the |
| 105 shadow tree(s). |
| 106 |
| 107 #### Re-attaching whitespace layout objects |
| 108 |
| 109 When the computed display value changes, the requirement for whitespace |
| 110 siblings may change. |
| 111 |
| 112 Example: |
| 113 |
| 114 <span style="position:absolute">A</span> <span>B</span> |
| 115 |
| 116 Initially, we don't need a layout object for the whitespace above. If we change |
| 117 the position of the first span to static, we need a layout object for the |
| 118 whitespace. During style recalc we keep track of the last text node sibling we |
| 119 traversed. The text node is reset when we traverse an element with a layout |
| 120 box. Remember that we traverse from right to left. That means we have stored |
| 121 the whitespace node above when we re-attach the left-most span. After |
| 122 re-attachment we re-attach the stored text node to see if the need for a |
| 123 layout object changed. If the text node re-attach changed the need for a layout |
| 124 object we continue to re-attach following layout object siblings until we |
| 125 reach an element with a layout object, or the re-attach was a no-op. |
| 126 |
| 127 The need for a whitespace layout object is dictated by the layout tree |
| 128 structure which is based on the flat-tree. That means the tracked text node |
| 129 solution we use does not work properly when (re-)attaching slotted and |
| 130 distributed nodes. |
| 131 |
| 132 Example: |
| 133 |
| 134 <div id="host"> |
| 135 <:shadow-root> |
| 136 <span style="position:absolute">A</span><slot></slot> |
| 137 </:shadow-root> |
| 138 <span>B</span> |
| 139 </div> |
| 140 |
| 141 Initially, the whitespace before the B span above does not get a layout object. |
| 142 If we change the absolute positioned span in the shadow tree to static, we need |
| 143 to have a layout object for that whitespace node. However, since we traverse |
| 144 traverse the light-tree children of #host after the shadow tree, we do not see |
| 145 the text node before re-attaching the absolute positioned span. |
| 146 |
| 147 Likewise we currently have issues with ::before and ::after elements because we |
| 148 do not keep track of text nodes and pass them to ::before/::after element |
| 149 re-attachments. See the "Known issues" below. |
| 150 |
| 151 Important methods: |
| 152 |
| 153 Element::recalcStyle() |
| 154 ContainerNode::recalcDescendantStyles() |
| 155 Node::reattachWhitespaceSiblingsIfNeeded() |
| 156 Text::textLayoutObjectIsNeeded() |
| 157 Text::reattachLayoutTreeIfNeeded() |
| 158 Text::recalcTextStyle() |
| 159 |
| 160 Known issues: |
| 161 |
| 162 https://crbug.com/648931 |
| 163 https://crbug.com/648951 |
| 164 https://crbug.com/650168 |
OLD | NEW |