1ade11b432496f6c3c968be7dde3b8cb01e48ad1
[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         // If the block container box that initiates this inline formatting contex also establishes a block context, create a new float for us.
30         ASSERT(this.formattingRoot().isBlockContainerBox());
31         if (this.formattingRoot().establishesBlockFormattingContext())
32             this.m_floatingContext = new FloatingContext(this);
33     }
34
35     layout() {
36         // 9.4.2 Inline formatting contexts
37         // In an inline formatting context, boxes are laid out horizontally, one after the other, beginning at the top of a containing block.
38         if (!this.formattingRoot().firstChild())
39             return;
40         // This is a post-order tree traversal layout.
41         // 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.
42         this.m_line = this._createNewLine();
43         this._addToLayoutQueue(this.formattingRoot().firstChild());
44         while (this._descendantNeedsLayout()) {
45             // Travers down on the descendants until we find a leaf node.
46             while (true) {
47                 let layoutBox = this._nextInLayoutQueue();
48                 if (layoutBox.establishesFormattingContext()) {
49                     this.layoutState().layout(layoutBox);
50                     break;
51                 }
52                 if (!layoutBox.isContainer() || !layoutBox.hasChild())
53                     break;
54                 this._addToLayoutQueue(layoutBox.firstChild());
55             }
56             while (this._descendantNeedsLayout()) {
57                 let layoutBox = this._nextInLayoutQueue();
58                 this._handleInlineBox(layoutBox);
59                 // We are done with laying out this box.
60                 this._removeFromLayoutQueue(layoutBox);
61                 if (layoutBox.nextSibling()) {
62                     this._addToLayoutQueue(layoutBox.nextSibling());
63                     break;
64                 }
65             }
66         }
67         //this._placeOutOfFlowDescendants(this.formattingRoot());
68         this._commitLine();
69    }
70
71     _handleInlineBox(inlineBox) {
72         if (inlineBox.text())
73             return this._handleText(inlineBox);
74     }
75
76     _handleText(inlineBox) {
77         // FIXME: This is a extremely simple line breaking algorithm.
78         let currentPosition = 0;
79         let text = inlineBox.text().content();
80         while (text.length) {
81             let textRuns = Utils.textRunsForLine(text, this._line().availableWidth(), this.formattingRoot());
82             if (!textRuns.length)
83                 break;
84             for (let run of textRuns)
85                 this._line().addTextLineBox(run.startPosition, run.endPosition, new LayoutSize(run.width, Utils.textHeight(inlineBox)));
86             text = text.slice(textRuns[textRuns.length - 1].endPosition, text.length);
87             this._commitLine();
88         }
89     }
90
91     _commitLine() {
92         if (this._line().isEmpty())
93             return;
94         this.formattingState().appendLine(this._line());
95         this.m_line = this._createNewLine();
96     }
97
98     _line() {
99         return this.m_line;
100     }
101
102     _createNewLine() {
103         // TODO: Floats need to be taken into account.
104         let contentBoxRect = this.displayBox(this.formattingRoot()).contentBox();
105         let topLeft = contentBoxRect.topLeft();
106         let lines = this.formattingState().lines();
107         if (lines.length)
108             topLeft.setTop(lines[lines.length - 1].rect().bottom());
109         return new Line(topLeft, Utils.computedLineHeight(this.formattingRoot().node()), contentBoxRect.width());
110     }
111 }
112