File: InlineContentBreaker.h

package info (click to toggle)
webkit2gtk 2.42.2-1~deb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 362,452 kB
  • sloc: cpp: 2,881,971; javascript: 282,447; ansic: 134,088; python: 43,789; ruby: 18,308; perl: 15,872; asm: 14,389; xml: 4,395; yacc: 2,350; sh: 2,074; java: 1,734; lex: 1,323; makefile: 288; pascal: 60
file content (194 lines) | stat: -rw-r--r-- 9,757 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/*
 * Copyright (C) 2018 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

#pragma once

#include "FormattingConstraints.h"
#include "LayoutUnits.h"

namespace WebCore {

class RenderStyle;

namespace Layout {

class InlineItem;
class InlineTextItem;
struct CandidateTextRunForBreaking;

class InlineContentBreaker {
public:
    InlineContentBreaker(std::optional<IntrinsicWidthMode>);

    struct PartialRun {
        size_t length { 0 };
        InlineLayoutUnit logicalWidth { 0 };
        // FIXME: Remove this and collapse the rest of PartialRun over to PartialTrailingContent.
        std::optional<InlineLayoutUnit> hyphenWidth { };
    };
    enum class IsEndOfLine : bool { No, Yes };
    struct Result {
        enum class Action {
            Keep, // Keep content on the current line.
            Break, // Partial content is on the current line.
            Wrap, // Content is wrapped to the next line.
            WrapWithHyphen, // Content is wrapped to the next line and the current line ends with a visible hyphen.
            // The current content overflows and can't get broken up into smaller bits.
            RevertToLastWrapOpportunity, // The content needs to be reverted back to the last wrap opportunity.
            RevertToLastNonOverflowingWrapOpportunity // The content needs to be reverted back to a wrap opportunity that still fits the line.
        };
        struct PartialTrailingContent {
            size_t trailingRunIndex { 0 };
            std::optional<PartialRun> partialRun; // nullopt partial run means the trailing run is a complete run.
            std::optional<InlineLayoutUnit> hyphenWidth { }; // Hyphen may be at the end of a full run in the middle of the continuous content (e.g. with adjacent InlineTextItems).
        };
        Action action { Action::Keep };
        IsEndOfLine isEndOfLine { IsEndOfLine::No };
        std::optional<PartialTrailingContent> partialTrailingContent { };
        const InlineItem* lastWrapOpportunityItem { nullptr };
    };

    // This struct represents the amount of continuous content committed to content breaking at a time (no in-between wrap opportunities).
    // e.g.
    // <div>text content <span>span1</span>between<span>span2</span></div>
    // [text][ ][content][ ][inline box start][span1][inline box end][between][inline box start][span2][inline box end]
    // continuous candidate content at a time:
    // 1. [text]
    // 2. [ ]
    // 3. [content]
    // 4. [ ]
    // 5. [inline box start][span1][inline box end][between][inline box start][span2][inline box end]
    // see https://drafts.csswg.org/css-text-3/#line-break-details
    struct ContinuousContent {
        InlineLayoutUnit logicalWidth() const { return m_logicalWidth; }
        InlineLayoutUnit leadingTrimmableWidth() const { return m_leadingTrimmableWidth; }
        InlineLayoutUnit trailingTrimmableWidth() const { return m_trailingTrimmableWidth; }
        InlineLayoutUnit hangingContentWidth() const { return m_hangingContentWidth; }
        bool hasTrimmableContent() const { return trailingTrimmableWidth() || leadingTrimmableWidth(); }
        bool hasHangingContent() const { return hangingContentWidth(); }
        bool isFullyTrimmable() const;
        bool isHangingContent() const { return hangingContentWidth() == logicalWidth(); }

        void append(const InlineItem&, const RenderStyle&, InlineLayoutUnit logicalWidth);
        void appendTextContent(const InlineTextItem&, const RenderStyle&, InlineLayoutUnit logicalWidth, std::optional<InlineLayoutUnit> trimmableWidth);
        void setHangingContentWidth(InlineLayoutUnit logicalWidth) { m_hangingContentWidth = logicalWidth; }
        void reset();

        struct Run {
            Run(const InlineItem&, const RenderStyle&, InlineLayoutUnit logicalWidth);
            Run(const Run&);
            Run& operator=(const Run&);

            const InlineItem& inlineItem;
            const RenderStyle& style;
            InlineLayoutUnit logicalWidth { 0 };
        };
        using RunList = Vector<Run, 3>;
        const RunList& runs() const { return m_runs; }

    private:
        void appendToRunList(const InlineItem&, const RenderStyle&, InlineLayoutUnit logicalWidth);
        void resetTrailingTrimmableContent();

        RunList m_runs;
        InlineLayoutUnit m_logicalWidth { 0.f };
        InlineLayoutUnit m_leadingTrimmableWidth { 0.f };
        InlineLayoutUnit m_trailingTrimmableWidth { 0.f };
        InlineLayoutUnit m_hangingContentWidth { 0.f };
    };

    struct LineStatus {
        InlineLayoutUnit contentLogicalRight { 0 };
        InlineLayoutUnit availableWidth { 0 };
        // Both of these types of trailing content may be ignored when checking for content fit.
        InlineLayoutUnit trimmableOrHangingWidth { 0 };
        std::optional<InlineLayoutUnit> trailingSoftHyphenWidth;
        bool hasFullyTrimmableTrailingContent { false };
        bool hasContent { false };
        bool hasWrapOpportunityAtPreviousPosition { false };
    };
    Result processInlineContent(const ContinuousContent&, const LineStatus&);
    void setHyphenationDisabled() { n_hyphenationIsDisabled = true; }

    static bool isWrappingAllowed(const ContinuousContent::Run&);

private:
    Result processOverflowingContent(const ContinuousContent&, const LineStatus&) const;

    struct OverflowingTextContent {
        size_t runIndex { 0 }; // Overflowing run index. There's always an overflowing run.
        struct BreakingPosition {
            size_t runIndex { 0 };
            struct TrailingContent {
                // Trailing content is either the run's left side (when we break the run somewhere in the middle) or the previous run.
                // Sometimes the breaking position is at the very beginning of the first run, so there's no trailing run at all.
                bool overflows { false };
                std::optional<InlineContentBreaker::PartialRun> partialRun { };
                std::optional<InlineLayoutUnit> hyphenWidth { };
            };
            std::optional<TrailingContent> trailingContent { };
        };
        std::optional<BreakingPosition> breakingPosition { }; // Where we actually break this overflowing content.
    };
    OverflowingTextContent processOverflowingContentWithText(const ContinuousContent&, const LineStatus&) const;
    std::optional<PartialRun> tryBreakingTextRun(const ContinuousContent::RunList& runs, const CandidateTextRunForBreaking&, InlineLayoutUnit availableWidth, const LineStatus&) const;
    std::optional<OverflowingTextContent::BreakingPosition> tryBreakingOverflowingRun(const LineStatus&, const ContinuousContent::RunList&, size_t overflowingRunIndex, InlineLayoutUnit nonOverflowingContentWidth) const;
    std::optional<OverflowingTextContent::BreakingPosition> tryBreakingPreviousNonOverflowingRuns(const LineStatus&, const ContinuousContent::RunList&, size_t overflowingRunIndex, InlineLayoutUnit nonOverflowingContentWidth) const;
    std::optional<OverflowingTextContent::BreakingPosition> tryBreakingNextOverflowingRuns(const LineStatus&, const ContinuousContent::RunList&, size_t overflowingRunIndex, InlineLayoutUnit nonOverflowingContentWidth) const;
    std::optional<OverflowingTextContent::BreakingPosition> tryHyphenationAcrossOverflowingInlineTextItems(const LineStatus&, const ContinuousContent::RunList&, size_t overflowingRunIndex) const;

    enum class WordBreakRule {
        AtArbitraryPositionWithinWords = 1 << 0,
        AtArbitraryPosition            = 1 << 1,
        AtHyphenationOpportunities     = 1 << 2
    };
    OptionSet<WordBreakRule> wordBreakBehavior(const RenderStyle&, bool hasWrapOpportunityAtPreviousPosition) const;
    bool isInIntrinsicWidthMode() const { return !!m_intrinsicWidthMode; }

    std::optional<IntrinsicWidthMode> m_intrinsicWidthMode;
    bool n_hyphenationIsDisabled { false };
};

inline InlineContentBreaker::ContinuousContent::Run::Run(const InlineItem& inlineItem, const RenderStyle& style, InlineLayoutUnit logicalWidth)
    : inlineItem(inlineItem)
    , style(style)
    , logicalWidth(logicalWidth)
{
}

inline InlineContentBreaker::ContinuousContent::Run::Run(const Run& other)
    : inlineItem(other.inlineItem)
    , style(other.style)
    , logicalWidth(other.logicalWidth)
{
}

inline bool InlineContentBreaker::ContinuousContent::isFullyTrimmable() const
{
    return m_leadingTrimmableWidth + m_trailingTrimmableWidth == logicalWidth();
}

}
}