[LFC][IFC] Move some code from InlineFormattingContext::Line to InlineFormattingConte...
[WebKit-https.git] / Source / WebCore / layout / inlineformatting / InlineFormattingContextGeometry.cpp
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. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "InlineFormattingContext.h"
28
29 #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
30
31 #include "FormattingContext.h"
32 #include "InlineFormattingState.h"
33 #include "LayoutBox.h"
34 #include "LayoutContainer.h"
35 #include "LayoutFormattingState.h"
36
37 namespace WebCore {
38 namespace Layout {
39
40 WidthAndMargin InlineFormattingContext::Geometry::inlineBlockWidthAndMargin(LayoutState& layoutState, const Box& formattingContextRoot)
41 {
42     ASSERT(formattingContextRoot.isInFlow());
43
44     // 10.3.10 'Inline-block', replaced elements in normal flow
45
46     // Exactly as inline replaced elements.
47     if (formattingContextRoot.replaced())
48         return inlineReplacedWidthAndMargin(layoutState, formattingContextRoot);
49
50     // 10.3.9 'Inline-block', non-replaced elements in normal flow
51
52     // If 'width' is 'auto', the used value is the shrink-to-fit width as for floating elements.
53     // A computed value of 'auto' for 'margin-left' or 'margin-right' becomes a used value of '0'.
54     auto& containingBlock = *formattingContextRoot.containingBlock();
55     auto containingBlockWidth = layoutState.displayBoxForLayoutBox(containingBlock).contentBoxWidth();
56     // #1
57     auto width = computedValueIfNotAuto(formattingContextRoot.style().logicalWidth(), containingBlockWidth);
58     if (!width)
59         width = shrinkToFitWidth(layoutState, formattingContextRoot);
60
61     // #2
62     auto margin = computedNonCollapsedHorizontalMarginValue(layoutState, formattingContextRoot);
63
64     return WidthAndMargin { *width, margin, margin };
65 }
66
67 HeightAndMargin InlineFormattingContext::Geometry::inlineBlockHeightAndMargin(const LayoutState& layoutState, const Box& layoutBox)
68 {
69     ASSERT(layoutBox.isInFlow());
70
71     // 10.6.2 Inline replaced elements, block-level replaced elements in normal flow, 'inline-block' replaced elements in normal flow and floating replaced elements
72     if (layoutBox.replaced())
73         return inlineReplacedHeightAndMargin(layoutState, layoutBox);
74
75     // 10.6.6 Complicated cases
76     // - 'Inline-block', non-replaced elements.
77     return complicatedCases(layoutState, layoutBox);
78 }
79
80 static LayoutUnit adjustedLineLogicalLeft(TextAlignMode align, LayoutUnit lineLogicalLeft, LayoutUnit remainingWidth)
81 {
82     switch (align) {
83     case TextAlignMode::Left:
84     case TextAlignMode::WebKitLeft:
85     case TextAlignMode::Start:
86         return lineLogicalLeft;
87     case TextAlignMode::Right:
88     case TextAlignMode::WebKitRight:
89     case TextAlignMode::End:
90         return lineLogicalLeft + std::max(remainingWidth, LayoutUnit());
91     case TextAlignMode::Center:
92     case TextAlignMode::WebKitCenter:
93         return lineLogicalLeft + std::max(remainingWidth / 2, LayoutUnit());
94     case TextAlignMode::Justify:
95         ASSERT_NOT_REACHED();
96         break;
97     }
98     ASSERT_NOT_REACHED();
99     return lineLogicalLeft;
100 }
101
102 void InlineFormattingContext::Geometry::justifyRuns(InlineFormattingState& formattingState, Line& line, Line::RunRange runRange)
103 {
104     auto& inlineRuns = formattingState.inlineRuns();
105     auto& lastInlineRun = inlineRuns.last();
106
107     // Adjust (forbid) trailing expansion for the last text run on line.
108     auto expansionBehavior = lastInlineRun.expansionOpportunity().behavior;
109     // Remove allow and add forbid.
110     expansionBehavior ^= AllowTrailingExpansion;
111     expansionBehavior |= ForbidTrailingExpansion;
112     lastInlineRun.expansionOpportunity().behavior = expansionBehavior;
113
114     // Collect expansion opportunities and justify the runs.
115     auto widthToDistribute = line.availableWidth();
116     if (widthToDistribute <= 0)
117         return;
118
119     auto expansionOpportunities = 0;
120     ASSERT(*runRange.lastRunIndex < inlineRuns.size());
121     for (auto runIndex = *runRange.firstRunIndex; runIndex <= *runRange.lastRunIndex; ++runIndex)
122         expansionOpportunities += inlineRuns[runIndex].expansionOpportunity().count;
123
124     if (!expansionOpportunities)
125         return;
126
127     float expansion = widthToDistribute.toFloat() / expansionOpportunities;
128     LayoutUnit accumulatedExpansion = 0;
129     for (auto runIndex = *runRange.firstRunIndex; runIndex <= *runRange.lastRunIndex; ++runIndex) {
130         auto& inlineRun = inlineRuns[runIndex];
131         auto expansionForRun = inlineRun.expansionOpportunity().count * expansion;
132
133         inlineRun.expansionOpportunity().expansion = expansionForRun;
134         inlineRun.setLogicalLeft(inlineRun.logicalLeft() + accumulatedExpansion);
135         inlineRun.setWidth(inlineRun.width() + expansionForRun);
136         accumulatedExpansion += expansionForRun;
137     }
138 }
139
140 void InlineFormattingContext::Geometry::computeExpansionOpportunities(InlineFormattingState& formattingState, const InlineRunProvider::Run& run, InlineRunProvider::Run::Type lastRunType)
141 {
142     auto isExpansionOpportunity = [](auto currentRunIsWhitespace, auto lastRunIsWhitespace) {
143         return currentRunIsWhitespace || (!currentRunIsWhitespace && !lastRunIsWhitespace);
144     };
145
146     auto expansionBehavior = [](auto isAtExpansionOpportunity) {
147         ExpansionBehavior expansionBehavior = AllowTrailingExpansion;
148         expansionBehavior |= isAtExpansionOpportunity ? ForbidLeadingExpansion : AllowLeadingExpansion;
149         return expansionBehavior;
150     };
151
152     auto isAtExpansionOpportunity = isExpansionOpportunity(run.isWhitespace(), lastRunType == InlineRunProvider::Run::Type::Whitespace);
153
154     auto& currentInlineRun = formattingState.inlineRuns().last();
155     auto& expansionOpportunity = currentInlineRun.expansionOpportunity();
156     if (isAtExpansionOpportunity)
157         ++expansionOpportunity.count;
158
159     expansionOpportunity.behavior = expansionBehavior(isAtExpansionOpportunity);
160 }
161
162 void InlineFormattingContext::Geometry::alignRuns(InlineFormattingState& inlineFormattingState, TextAlignMode textAlign, Line& line, Line::RunRange runRange, IsLastLine isLastLine)
163 {
164     auto adjutedTextAlignment = textAlign != TextAlignMode::Justify ? textAlign : isLastLine == IsLastLine::No ? TextAlignMode::Justify : TextAlignMode::Left;
165     if (adjutedTextAlignment == TextAlignMode::Justify) {
166         justifyRuns(inlineFormattingState, line, runRange);
167         return;
168     }
169
170     auto lineLogicalLeft = line.contentLogicalLeft();
171     auto adjustedLogicalLeft = adjustedLineLogicalLeft(adjutedTextAlignment, lineLogicalLeft, line.availableWidth());
172     if (adjustedLogicalLeft == lineLogicalLeft)
173         return;
174
175     auto& inlineRuns = inlineFormattingState.inlineRuns();
176     auto delta = adjustedLogicalLeft - lineLogicalLeft;
177     ASSERT(*runRange.lastRunIndex < inlineRuns.size());
178     for (auto runIndex = *runRange.firstRunIndex; runIndex <= *runRange.lastRunIndex; ++runIndex)
179         inlineRuns[runIndex].setLogicalLeft(inlineRuns[runIndex].logicalLeft() + delta);
180 }
181
182 }
183 }
184
185 #endif