[LayoutReloaded] Introduce FloatingState.
[WebKit-https.git] / Tools / LayoutReloaded / FormattingContext / InlineFormatting / InlineFormattingContext.js
1 /*
2  * Copyright (C) 2018 Apple Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 class InlineFormattingContext extends FormattingContext {
27     constructor(inlineFormattingState) {
28         super(inlineFormattingState);
29         ASSERT(this.formattingRoot().isBlockContainerBox());
30     }
31
32     layout() {
33         // 9.4.2 Inline formatting contexts
34         // In an inline formatting context, boxes are laid out horizontally, one after the other, beginning at the top of a containing block.
35         if (!this.formattingRoot().firstChild())
36             return;
37         // This is a post-order tree traversal layout.
38         // The root container layout is done in the formatting context it lives in, not that one it creates, so let's start with the first child.
39         this.m_line = this._createNewLine();
40         this._addToLayoutQueue(this.formattingRoot().firstChild());
41         while (this._descendantNeedsLayout()) {
42             // Travers down on the descendants until we find a leaf node.
43             while (true) {
44                 let layoutBox = this._nextInLayoutQueue();
45                 if (layoutBox.establishesFormattingContext()) {
46                     this.layoutState().layout(layoutBox);
47                     break;
48                 }
49                 if (!layoutBox.isContainer() || !layoutBox.hasChild())
50                     break;
51                 this._addToLayoutQueue(layoutBox.firstChild());
52             }
53             while (this._descendantNeedsLayout()) {
54                 let layoutBox = this._nextInLayoutQueue();
55                 this._handleInlineBox(layoutBox);
56                 // We are done with laying out this box.
57                 this._removeFromLayoutQueue(layoutBox);
58                 if (layoutBox.nextSibling()) {
59                     this._addToLayoutQueue(layoutBox.nextSibling());
60                     break;
61                 }
62             }
63         }
64         //this._placeOutOfFlowDescendants(this.formattingRoot());
65         this._commitLine();
66    }
67
68     _handleInlineBox(inlineBox) {
69         if (inlineBox.text())
70             return this._handleText(inlineBox);
71     }
72
73     _handleText(inlineBox) {
74         // FIXME: This is a extremely simple line breaking algorithm.
75         let currentPosition = 0;
76         let text = inlineBox.text().content();
77         while (text.length) {
78             let textRuns = Utils.textRunsForLine(text, this._line().availableWidth(), this.formattingRoot());
79             if (!textRuns.length)
80                 break;
81             for (let run of textRuns)
82                 this._line().addTextLineBox(run.startPosition, run.endPosition, new LayoutSize(run.width, Utils.textHeight(inlineBox)));
83             text = text.slice(textRuns[textRuns.length - 1].endPosition, text.length);
84             this._commitLine();
85         }
86     }
87
88     _commitLine() {
89         if (this._line().isEmpty())
90             return;
91         this.formattingState().appendLine(this._line());
92         this.m_line = this._createNewLine();
93     }
94
95     _line() {
96         return this.m_line;
97     }
98
99     _createNewLine() {
100         // TODO: Floats need to be taken into account.
101         let contentBoxRect = this.displayBox(this.formattingRoot()).contentBox();
102         let topLeft = contentBoxRect.topLeft();
103         let lines = this.formattingState().lines();
104         if (lines.length)
105             topLeft.setTop(lines[lines.length - 1].rect().bottom());
106         return new Line(topLeft, Utils.computedLineHeight(this.formattingRoot().node()), contentBoxRect.width());
107     }
108 }
109