Diff
Modified: trunk/Source/WebCore/ChangeLog (252864 => 252865)
--- trunk/Source/WebCore/ChangeLog 2019-11-25 18:15:50 UTC (rev 252864)
+++ trunk/Source/WebCore/ChangeLog 2019-11-25 18:33:52 UTC (rev 252865)
@@ -1,3 +1,121 @@
+2019-11-25 Zalan Bujtas <[email protected]>
+
+ [LFC][IFC] Rename Line to LineBuilder
+ https://bugs.webkit.org/show_bug.cgi?id=204584
+ <rdar://problem/57474964>
+
+ Reviewed by Antti Koivisto.
+
+ It really is a line builder.
+
+ * Sources.txt:
+ * WebCore.xcodeproj/project.pbxproj:
+ * layout/inlineformatting/InlineFormattingContext.cpp:
+ (WebCore::Layout::InlineFormattingContext::lineLayout):
+ (WebCore::Layout::InlineFormattingContext::computedIntrinsicWidthForConstraint const):
+ (WebCore::Layout::InlineFormattingContext::constraintsForLine):
+ * layout/inlineformatting/InlineFormattingContext.h:
+ * layout/inlineformatting/InlineFormattingContextQuirks.cpp:
+ (WebCore::Layout::InlineFormattingContext::Quirks::lineDescentNeedsCollapsing const):
+ (WebCore::Layout::InlineFormattingContext::Quirks::lineHeightConstraints const):
+ * layout/inlineformatting/InlineLineBreaker.cpp:
+ * layout/inlineformatting/InlineLineBuilder.cpp: Renamed from Source/WebCore/layout/inlineformatting/InlineLine.cpp.
+ (WebCore::Layout::InlineItemRun::layoutBox const):
+ (WebCore::Layout::InlineItemRun::logicalRect const):
+ (WebCore::Layout::InlineItemRun::textContext const):
+ (WebCore::Layout::InlineItemRun::isText const):
+ (WebCore::Layout::InlineItemRun::isBox const):
+ (WebCore::Layout::InlineItemRun::isContainerStart const):
+ (WebCore::Layout::InlineItemRun::isContainerEnd const):
+ (WebCore::Layout::InlineItemRun::isForcedLineBreak const):
+ (WebCore::Layout::InlineItemRun::type const):
+ (WebCore::Layout::InlineItemRun::setIsCollapsed):
+ (WebCore::Layout::InlineItemRun::isCollapsed const):
+ (WebCore::Layout::InlineItemRun::isCollapsedToZeroAdvanceWidth const):
+ (WebCore::Layout::InlineItemRun::isCollapsible const):
+ (WebCore::Layout::InlineItemRun::isWhitespace const):
+ (WebCore::Layout::InlineItemRun::hasExpansionOpportunity const):
+ (WebCore::Layout::InlineItemRun::InlineItemRun):
+ (WebCore::Layout::InlineItemRun::setCollapsesToZeroAdvanceWidth):
+ (WebCore::Layout::ContinousContent::canBeExpanded):
+ (WebCore::Layout::ContinousContent::canBeMerged const):
+ (WebCore::Layout::ContinousContent::ContinousContent):
+ (WebCore::Layout::ContinousContent::append):
+ (WebCore::Layout::ContinousContent::close):
+ (WebCore::Layout::LineBuilder::Run::Run):
+ (WebCore::Layout::LineBuilder::Run::adjustExpansionBehavior):
+ (WebCore::Layout::LineBuilder::Run::expansionBehavior const):
+ (WebCore::Layout::LineBuilder::Run::setComputedHorizontalExpansion):
+ (WebCore::Layout::LineBuilder::LineBuilder):
+ (WebCore::Layout::LineBuilder::~LineBuilder):
+ (WebCore::Layout::LineBuilder::initialize):
+ (WebCore::Layout::shouldPreserveTrailingContent):
+ (WebCore::Layout::shouldPreserveLeadingContent):
+ (WebCore::Layout::LineBuilder::close):
+ (WebCore::Layout::LineBuilder::alignContentVertically):
+ (WebCore::Layout::LineBuilder::justifyRuns const):
+ (WebCore::Layout::LineBuilder::alignContentHorizontally const):
+ (WebCore::Layout::LineBuilder::removeTrailingTrimmableContent):
+ (WebCore::Layout::LineBuilder::moveLogicalLeft):
+ (WebCore::Layout::LineBuilder::moveLogicalRight):
+ (WebCore::Layout::LineBuilder::append):
+ (WebCore::Layout::LineBuilder::appendNonBreakableSpace):
+ (WebCore::Layout::LineBuilder::appendInlineContainerStart):
+ (WebCore::Layout::LineBuilder::appendInlineContainerEnd):
+ (WebCore::Layout::LineBuilder::appendTextContent):
+ (WebCore::Layout::LineBuilder::appendNonReplacedInlineBox):
+ (WebCore::Layout::LineBuilder::appendReplacedInlineBox):
+ (WebCore::Layout::LineBuilder::appendLineBreak):
+ (WebCore::Layout::LineBuilder::adjustBaselineAndLineHeight):
+ (WebCore::Layout::LineBuilder::runContentHeight const):
+ (WebCore::Layout::LineBuilder::TrimmableContent::append):
+ (WebCore::Layout::LineBuilder::halfLeadingMetrics):
+ (WebCore::Layout::LineBuilder::layoutState const):
+ (WebCore::Layout::LineBuilder::formattingContext const):
+ * layout/inlineformatting/InlineLineBuilder.h: Renamed from Source/WebCore/layout/inlineformatting/InlineLine.h.
+ (WebCore::Layout::LineBuilder::isVisuallyEmpty const):
+ (WebCore::Layout::LineBuilder::hasIntrusiveFloat const):
+ (WebCore::Layout::LineBuilder::availableWidth const):
+ (WebCore::Layout::LineBuilder::trailingTrimmableWidth const):
+ (WebCore::Layout::LineBuilder::lineBox const):
+ (WebCore::Layout::LineBuilder::setHasIntrusiveFloat):
+ (WebCore::Layout::LineBuilder::Run::isText const):
+ (WebCore::Layout::LineBuilder::Run::isBox const):
+ (WebCore::Layout::LineBuilder::Run::isForcedLineBreak const):
+ (WebCore::Layout::LineBuilder::Run::isContainerStart const):
+ (WebCore::Layout::LineBuilder::Run::isContainerEnd const):
+ (WebCore::Layout::LineBuilder::Run::layoutBox const):
+ (WebCore::Layout::LineBuilder::Run::logicalRect const):
+ (WebCore::Layout::LineBuilder::Run::textContext const):
+ (WebCore::Layout::LineBuilder::Run::isCollapsedToVisuallyEmpty const):
+ (WebCore::Layout::LineBuilder::Run::adjustLogicalTop):
+ (WebCore::Layout::LineBuilder::Run::moveHorizontally):
+ (WebCore::Layout::LineBuilder::Run::moveVertically):
+ (WebCore::Layout::LineBuilder::Run::setLogicalHeight):
+ (WebCore::Layout::LineBuilder::Run::hasExpansionOpportunity const):
+ (WebCore::Layout::LineBuilder::Run::expansionOpportunityCount const):
+ (WebCore::Layout::LineBuilder::logicalTop const):
+ (WebCore::Layout::LineBuilder::logicalBottom const):
+ (WebCore::Layout::LineBuilder::logicalLeft const):
+ (WebCore::Layout::LineBuilder::logicalRight const):
+ (WebCore::Layout::LineBuilder::logicalWidth const):
+ (WebCore::Layout::LineBuilder::logicalHeight const):
+ (WebCore::Layout::LineBuilder::contentLogicalWidth const):
+ (WebCore::Layout::LineBuilder::contentLogicalRight const):
+ (WebCore::Layout::LineBuilder::baselineOffset const):
+ (WebCore::Layout::LineBuilder::isTextAlignJustify const):
+ (WebCore::Layout::LineBuilder::TrimmableContent::width const):
+ (WebCore::Layout::LineBuilder::TrimmableContent::runs):
+ (WebCore::Layout::LineBuilder::TrimmableContent::isEmpty const):
+ (WebCore::Layout::LineBuilder::TrimmableContent::clear):
+ * layout/inlineformatting/LineLayoutContext.cpp:
+ (WebCore::Layout::LineLayoutContext::layoutLine):
+ (WebCore::Layout::LineLayoutContext::commitPendingContent):
+ (WebCore::Layout::LineLayoutContext::close):
+ (WebCore::Layout::LineLayoutContext::placeInlineItem):
+ (WebCore::Layout::LineLayoutContext::processUncommittedContent):
+ * layout/inlineformatting/LineLayoutContext.h:
+
2019-11-25 Zan Dobersek <[email protected]>
Support OffscreenCanvas as a CanvasImageSource type in CanvasDrawImage, CanvasFillStrokeStyles
Modified: trunk/Source/WebCore/Sources.txt (252864 => 252865)
--- trunk/Source/WebCore/Sources.txt 2019-11-25 18:15:50 UTC (rev 252864)
+++ trunk/Source/WebCore/Sources.txt 2019-11-25 18:33:52 UTC (rev 252865)
@@ -1438,8 +1438,8 @@
layout/inlineformatting/InlineFormattingContextQuirks.cpp
layout/inlineformatting/InlineFormattingState.cpp
layout/inlineformatting/InlineItem.cpp
-layout/inlineformatting/InlineLine.cpp
layout/inlineformatting/InlineLineBreaker.cpp
+layout/inlineformatting/InlineLineBuilder.cpp
layout/inlineformatting/InlineTextItem.cpp
layout/inlineformatting/LineLayoutContext.cpp
layout/inlineformatting/text/TextUtil.cpp
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (252864 => 252865)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2019-11-25 18:15:50 UTC (rev 252864)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2019-11-25 18:33:52 UTC (rev 252865)
@@ -2020,7 +2020,7 @@
6ED878C5147493F4004C3597 /* RenderTableCaption.h in Headers */ = {isa = PBXBuildFile; fileRef = 6ED878C3147493F4004C3597 /* RenderTableCaption.h */; };
6ED8C37A183BFF8C009E53BD /* BoxShape.h in Headers */ = {isa = PBXBuildFile; fileRef = 6ED8C378183BFF8C009E53BD /* BoxShape.h */; settings = {ATTRIBUTES = (Private, ); }; };
6EE8A77310F803F3005A4A24 /* JSWebGLContextAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EE8A77110F803F3005A4A24 /* JSWebGLContextAttributes.h */; };
- 6F0CD695229ED32700C5994E /* InlineLine.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F0CD694229ED32700C5994E /* InlineLine.h */; };
+ 6F0CD695229ED32700C5994E /* InlineLineBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F0CD694229ED32700C5994E /* InlineLineBuilder.h */; };
6F1CC1DE225F8B4900720AD2 /* InlineTextItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F1CC1DD225F8B4200720AD2 /* InlineTextItem.h */; settings = {ATTRIBUTES = (Private, ); }; };
6F26BB6C23343E6F002F2BEA /* LayoutContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F26BB6B23343E5B002F2BEA /* LayoutContext.h */; settings = {ATTRIBUTES = (Private, ); }; };
6F26EB48234004A5006906E2 /* LineLayoutContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F26EB46234004A5006906E2 /* LineLayoutContext.h */; };
@@ -9245,8 +9245,8 @@
6EE8A77010F803F3005A4A24 /* JSWebGLContextAttributes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSWebGLContextAttributes.cpp; sourceTree = "<group>"; };
6EE8A77110F803F3005A4A24 /* JSWebGLContextAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSWebGLContextAttributes.h; sourceTree = "<group>"; };
6F0830DF20B46951008A945B /* BlockFormattingContextGeometry.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = BlockFormattingContextGeometry.cpp; sourceTree = "<group>"; };
- 6F0CD692229ED31900C5994E /* InlineLine.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InlineLine.cpp; sourceTree = "<group>"; };
- 6F0CD694229ED32700C5994E /* InlineLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InlineLine.h; sourceTree = "<group>"; };
+ 6F0CD692229ED31900C5994E /* InlineLineBuilder.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InlineLineBuilder.cpp; sourceTree = "<group>"; };
+ 6F0CD694229ED32700C5994E /* InlineLineBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InlineLineBuilder.h; sourceTree = "<group>"; };
6F10B08622B8568D0090E69C /* InlineFormattingContextQuirks.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InlineFormattingContextQuirks.cpp; sourceTree = "<group>"; };
6F1CC1DC225F8B4100720AD2 /* InlineTextItem.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InlineTextItem.cpp; sourceTree = "<group>"; };
6F1CC1DD225F8B4200720AD2 /* InlineTextItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InlineTextItem.h; sourceTree = "<group>"; };
@@ -16686,11 +16686,11 @@
115CFA7C208B8E10001E6991 /* InlineFormattingState.h */,
6F7B8CEC23626E6600C9FF15 /* InlineItem.cpp */,
6FE7CFA02177EEF1005B1573 /* InlineItem.h */,
- 6F0CD692229ED31900C5994E /* InlineLine.cpp */,
- 6F0CD694229ED32700C5994E /* InlineLine.h */,
6FB47E612277425A00C7BCB0 /* InlineLineBox.h */,
6FE198132178397B00446F08 /* InlineLineBreaker.cpp */,
6FE198152178397C00446F08 /* InlineLineBreaker.h */,
+ 6F0CD692229ED31900C5994E /* InlineLineBuilder.cpp */,
+ 6F0CD694229ED32700C5994E /* InlineLineBuilder.h */,
6F1CC1DC225F8B4100720AD2 /* InlineTextItem.cpp */,
6F1CC1DD225F8B4200720AD2 /* InlineTextItem.h */,
6F25B200220A85AB0000011B /* LineLayoutContext.cpp */,
@@ -30173,9 +30173,9 @@
115CFA7E208B8E10001E6991 /* InlineFormattingState.h in Headers */,
6FE7CFA22177EEF2005B1573 /* InlineItem.h in Headers */,
BCE789161120D6080060ECE5 /* InlineIterator.h in Headers */,
- 6F0CD695229ED32700C5994E /* InlineLine.h in Headers */,
6FB47E632277425A00C7BCB0 /* InlineLineBox.h in Headers */,
6FE198172178397C00446F08 /* InlineLineBreaker.h in Headers */,
+ 6F0CD695229ED32700C5994E /* InlineLineBuilder.h in Headers */,
AA4C3A770B2B1679002334A2 /* InlineStyleSheetOwner.h in Headers */,
BCEA485A097D93020094C9E4 /* InlineTextBox.h in Headers */,
1C010701192594DF008A4201 /* InlineTextBoxStyle.h in Headers */,
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp (252864 => 252865)
--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp 2019-11-25 18:15:50 UTC (rev 252864)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp 2019-11-25 18:33:52 UTC (rev 252865)
@@ -95,12 +95,12 @@
auto lineLogicalTop = geometryForBox(root()).contentBoxTop();
unsigned leadingInlineItemIndex = 0;
Optional<LineLayoutContext::PartialContent> leadingPartialContent;
- auto line = Line { *this, root().style().textAlign(), Line::SkipAlignment::No };
+ auto lineBuilder = LineBuilder { *this, root().style().textAlign(), LineBuilder::SkipAlignment::No };
auto lineLayoutContext = LineLayoutContext { *this, inlineItems };
while (leadingInlineItemIndex < inlineItems.size()) {
- line.initialize(constraintsForLine(usedHorizontalValues, lineLogicalTop));
- auto lineContent = lineLayoutContext.layoutLine(line, leadingInlineItemIndex, leadingPartialContent);
+ lineBuilder.initialize(constraintsForLine(usedHorizontalValues, lineLogicalTop));
+ auto lineContent = lineLayoutContext.layoutLine(lineBuilder, leadingInlineItemIndex, leadingPartialContent);
setDisplayBoxesForLine(lineContent, usedHorizontalValues);
leadingPartialContent = { };
@@ -116,7 +116,7 @@
leadingInlineItemIndex = *lineContent.trailingInlineItemIndex + 1;
} else {
// Floats prevented us placing any content on the line.
- ASSERT(line.hasIntrusiveFloat());
+ ASSERT(lineBuilder.hasIntrusiveFloat());
// Move the next line below the intrusive float.
auto floatingContext = FloatingContext { root(), *this, formattingState().floatingState() };
auto floatConstraints = floatingContext.constraints({ lineLogicalTop });
@@ -233,12 +233,12 @@
auto& inlineItems = formattingState().inlineItems();
LayoutUnit maximumLineWidth;
unsigned leadingInlineItemIndex = 0;
- auto line = Line { *this, root().style().textAlign(), Line::SkipAlignment::Yes };
+ auto lineBuilder = LineBuilder { *this, root().style().textAlign(), LineBuilder::SkipAlignment::Yes };
auto lineLayoutContext = LineLayoutContext { *this, inlineItems };
while (leadingInlineItemIndex < inlineItems.size()) {
// Only the horiztonal available width is constrained when computing intrinsic width.
- line.initialize(Line::Constraints { { }, usedHorizontalValues.constraints.width, false, { } });
- auto lineContent = lineLayoutContext.layoutLine(line, leadingInlineItemIndex, { });
+ lineBuilder.initialize(LineBuilder::Constraints { { }, usedHorizontalValues.constraints.width, false, { } });
+ auto lineContent = lineLayoutContext.layoutLine(lineBuilder, leadingInlineItemIndex, { });
leadingInlineItemIndex = *lineContent.trailingInlineItemIndex + 1;
LayoutUnit floatsWidth;
@@ -371,7 +371,7 @@
}
}
-Line::Constraints InlineFormattingContext::constraintsForLine(const UsedHorizontalValues& usedHorizontalValues, const LayoutUnit lineLogicalTop)
+LineBuilder::Constraints InlineFormattingContext::constraintsForLine(const UsedHorizontalValues& usedHorizontalValues, const LayoutUnit lineLogicalTop)
{
auto lineLogicalLeft = geometryForBox(root()).contentBoxLeft();
auto availableWidth = usedHorizontalValues.constraints.width;
@@ -404,7 +404,7 @@
availableWidth = floatConstraints.right->x - lineLogicalLeft;
}
}
- return Line::Constraints { { lineLogicalLeft, lineLogicalTop }, availableWidth, lineIsConstrainedByFloat, quirks().lineHeightConstraints(root()) };
+ return LineBuilder::Constraints { { lineLogicalLeft, lineLogicalTop }, availableWidth, lineIsConstrainedByFloat, quirks().lineHeightConstraints(root()) };
}
void InlineFormattingContext::setDisplayBoxesForLine(const LineLayoutContext::LineContent& lineContent, const UsedHorizontalValues& usedHorizontalValues)
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h (252864 => 252865)
--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h 2019-11-25 18:15:50 UTC (rev 252864)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h 2019-11-25 18:33:52 UTC (rev 252865)
@@ -50,8 +50,8 @@
class Quirks : public FormattingContext::Quirks {
public:
- bool lineDescentNeedsCollapsing(const Line::RunList&) const;
- Line::Constraints::HeightAndBaseline lineHeightConstraints(const Box& formattingRoot) const;
+ bool lineDescentNeedsCollapsing(const LineBuilder::RunList&) const;
+ LineBuilder::Constraints::HeightAndBaseline lineHeightConstraints(const Box& formattingRoot) const;
private:
friend class InlineFormattingContext;
@@ -89,7 +89,7 @@
void computeWidthAndMargin(const Box&, const UsedHorizontalValues&);
void collectInlineContentIfNeeded();
- Line::Constraints constraintsForLine(const UsedHorizontalValues&, const LayoutUnit lineLogicalTop);
+ LineBuilder::Constraints constraintsForLine(const UsedHorizontalValues&, const LayoutUnit lineLogicalTop);
void setDisplayBoxesForLine(const LineLayoutContext::LineContent&, const UsedHorizontalValues&);
void invalidateFormattingState(const InvalidationState&);
@@ -96,7 +96,7 @@
const InlineFormattingState& formattingState() const { return downcast<InlineFormattingState>(FormattingContext::formattingState()); }
InlineFormattingState& formattingState() { return downcast<InlineFormattingState>(FormattingContext::formattingState()); }
// FIXME: Come up with a structure that requires no friending.
- friend class Line;
+ friend class LineBuilder;
};
inline InlineFormattingContext::Geometry::Geometry(const InlineFormattingContext& inlineFormattingContext)
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextQuirks.cpp (252864 => 252865)
--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextQuirks.cpp 2019-11-25 18:15:50 UTC (rev 252864)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextQuirks.cpp 2019-11-25 18:33:52 UTC (rev 252865)
@@ -28,13 +28,13 @@
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
-#include "InlineLine.h"
+#include "InlineLineBuilder.h"
#include "LayoutState.h"
namespace WebCore {
namespace Layout {
-bool InlineFormattingContext::Quirks::lineDescentNeedsCollapsing(const Line::RunList& runList) const
+bool InlineFormattingContext::Quirks::lineDescentNeedsCollapsing(const LineBuilder::RunList& runList) const
{
// Collapse line descent in limited and full quirk mode when there's no baseline aligned content or
// the baseline aligned content has no descent.
@@ -75,23 +75,23 @@
return true;
}
-Line::Constraints::HeightAndBaseline InlineFormattingContext::Quirks::lineHeightConstraints(const Box& formattingRoot) const
+LineBuilder::Constraints::HeightAndBaseline InlineFormattingContext::Quirks::lineHeightConstraints(const Box& formattingRoot) const
{
// computedLineHeight takes font-size into account when line-height is not set.
// Strut is the imaginary box that we put on every line. It sets the initial vertical constraints for each new line.
auto strutHeight = formattingRoot.style().computedLineHeight();
- auto strutBaselineOffset = Line::halfLeadingMetrics(formattingRoot.style().fontMetrics(), strutHeight).ascent();
+ auto strutBaselineOffset = LineBuilder::halfLeadingMetrics(formattingRoot.style().fontMetrics(), strutHeight).ascent();
if (layoutState().inNoQuirksMode())
return { strutHeight, strutBaselineOffset, { } };
auto lineHeight = formattingRoot.style().lineHeight();
if (lineHeight.isPercentOrCalculated()) {
- auto initialBaselineOffset = Line::halfLeadingMetrics(formattingRoot.style().fontMetrics(), { }).ascent();
+ auto initialBaselineOffset = LineBuilder::halfLeadingMetrics(formattingRoot.style().fontMetrics(), { }).ascent();
return { initialBaselineOffset, initialBaselineOffset, LineBox::Baseline { strutBaselineOffset, strutHeight - strutBaselineOffset } };
}
// FIXME: The only reason why we use intValue() here is to match current inline tree (integral)behavior.
auto initialLineHeight = LayoutUnit { lineHeight.intValue() };
- auto initialBaselineOffset = Line::halfLeadingMetrics(formattingRoot.style().fontMetrics(), initialLineHeight).ascent();
+ auto initialBaselineOffset = LineBuilder::halfLeadingMetrics(formattingRoot.style().fontMetrics(), initialLineHeight).ascent();
return { initialLineHeight, initialBaselineOffset, LineBox::Baseline { strutBaselineOffset, strutHeight - strutBaselineOffset } };
}
Deleted: trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp (252864 => 252865)
--- trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp 2019-11-25 18:15:50 UTC (rev 252864)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp 2019-11-25 18:33:52 UTC (rev 252865)
@@ -1,715 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#include "config.h"
-#include "InlineLine.h"
-
-#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
-
-#include "InlineFormattingContext.h"
-#include "TextUtil.h"
-#include <wtf/IsoMallocInlines.h>
-
-namespace WebCore {
-namespace Layout {
-
-WTF_MAKE_ISO_ALLOCATED_IMPL(Line);
-
-class InlineItemRun {
-WTF_MAKE_ISO_ALLOCATED_INLINE(InlineItemRun);
-public:
- InlineItemRun(const InlineItem&, const Display::Rect&, WTF::Optional<Display::Run::TextContext> = WTF::nullopt);
-
- const Box& layoutBox() const { return m_inlineItem.layoutBox(); }
- const Display::Rect& logicalRect() const { return m_logicalRect; }
- Optional<Display::Run::TextContext> textContext() const { return m_textContext; }
-
- bool isText() const { return m_inlineItem.isText(); }
- bool isBox() const { return m_inlineItem.isBox(); }
- bool isContainerStart() const { return m_inlineItem.isContainerStart(); }
- bool isContainerEnd() const { return m_inlineItem.isContainerEnd(); }
- bool isForcedLineBreak() const { return m_inlineItem.isForcedLineBreak(); }
- InlineItem::Type type() const { return m_inlineItem.type(); }
-
- void setIsCollapsed() { m_isCollapsed = true; }
- bool isCollapsed() const { return m_isCollapsed; }
-
- void setCollapsesToZeroAdvanceWidth();
- bool isCollapsedToZeroAdvanceWidth() const { return m_collapsedToZeroAdvanceWidth; }
-
- bool isCollapsible() const { return is<InlineTextItem>(m_inlineItem) && downcast<InlineTextItem>(m_inlineItem).isCollapsible(); }
- bool isWhitespace() const { return is<InlineTextItem>(m_inlineItem) && downcast<InlineTextItem>(m_inlineItem).isWhitespace(); }
-
- bool hasExpansionOpportunity() const { return isWhitespace() && !isCollapsedToZeroAdvanceWidth(); }
-
-private:
- const InlineItem& m_inlineItem;
- Display::Rect m_logicalRect;
- const Optional<Display::Run::TextContext> m_textContext;
- bool m_isCollapsed { false };
- bool m_collapsedToZeroAdvanceWidth { false };
-};
-
-InlineItemRun::InlineItemRun(const InlineItem& inlineItem, const Display::Rect& logicalRect, WTF::Optional<Display::Run::TextContext> textContext)
- : m_inlineItem(inlineItem)
- , m_logicalRect(logicalRect)
- , m_textContext(textContext)
-{
-}
-
-void InlineItemRun::setCollapsesToZeroAdvanceWidth()
-{
- m_collapsedToZeroAdvanceWidth = true;
- m_logicalRect.setWidth({ });
-}
-
-struct ContinousContent {
-public:
- ContinousContent(const InlineItemRun&, bool textIsAlignJustify);
-
- bool append(const InlineItemRun&);
- Line::Run close();
-
-private:
- static bool canBeExpanded(const InlineItemRun& run) { return run.isText() && !run.isCollapsed() && !run.isCollapsedToZeroAdvanceWidth(); }
- bool canBeMerged(const InlineItemRun& run) const { return run.isText() && !run.isCollapsedToZeroAdvanceWidth() && &m_initialInlineRun.layoutBox() == &run.layoutBox(); }
-
- const InlineItemRun& m_initialInlineRun;
- const bool m_textIsAlignJustify { false };
- unsigned m_expandedLength { 0 };
- LayoutUnit m_expandedWidth;
- bool m_trailingRunCanBeExpanded { false };
- bool m_hasTrailingExpansionOpportunity { false };
- unsigned m_expansionOpportunityCount { 0 };
-};
-
-ContinousContent::ContinousContent(const InlineItemRun& initialInlineRun, bool textIsAlignJustify)
- : m_initialInlineRun(initialInlineRun)
- , m_textIsAlignJustify(textIsAlignJustify)
- , m_trailingRunCanBeExpanded(canBeExpanded(initialInlineRun))
-{
-}
-
-bool ContinousContent::append(const InlineItemRun& inlineItemRun)
-{
- // Merged content needs to be continuous.
- if (!m_trailingRunCanBeExpanded)
- return false;
- if (!canBeMerged(inlineItemRun))
- return false;
-
- m_trailingRunCanBeExpanded = canBeExpanded(inlineItemRun);
-
- ASSERT(inlineItemRun.isText());
- m_expandedLength += inlineItemRun.textContext()->length();
- m_expandedWidth += inlineItemRun.logicalRect().width();
-
- if (m_textIsAlignJustify) {
- m_hasTrailingExpansionOpportunity = inlineItemRun.hasExpansionOpportunity();
- if (m_hasTrailingExpansionOpportunity)
- ++m_expansionOpportunityCount;
- }
- return true;
-}
-
-Line::Run ContinousContent::close()
-{
- if (!m_expandedLength)
- return { m_initialInlineRun };
- // Expand the text content and set the expansion opportunities.
- ASSERT(m_initialInlineRun.isText());
- auto logicalRect = m_initialInlineRun.logicalRect();
- logicalRect.expandHorizontally(m_expandedWidth);
-
- auto textContext = *m_initialInlineRun.textContext();
- auto length = textContext.length() + m_expandedLength;
- textContext.expand(m_initialInlineRun.layoutBox().textContext()->content.substring(textContext.start(), length), length);
-
- if (m_textIsAlignJustify) {
- // FIXME: This is a very simple expansion merge. We should eventually switch over to FontCascade::expansionOpportunityCount.
- ExpansionBehavior expansionBehavior = m_hasTrailingExpansionOpportunity ? (ForbidLeadingExpansion | AllowTrailingExpansion) : (AllowLeadingExpansion | AllowTrailingExpansion);
- if (m_initialInlineRun.hasExpansionOpportunity())
- ++m_expansionOpportunityCount;
- textContext.setExpansion({ expansionBehavior, { } });
- }
- return { m_initialInlineRun, logicalRect, textContext, m_expansionOpportunityCount };
-}
-
-Line::Run::Run(const InlineItemRun& inlineItemRun)
- : m_layoutBox(&inlineItemRun.layoutBox())
- , m_type(inlineItemRun.type())
- , m_logicalRect(inlineItemRun.logicalRect())
- , m_textContext(inlineItemRun.textContext())
- , m_isCollapsedToVisuallyEmpty(inlineItemRun.isCollapsedToZeroAdvanceWidth())
-{
- if (inlineItemRun.hasExpansionOpportunity()) {
- m_expansionOpportunityCount = 1;
- ASSERT(m_textContext);
- m_textContext->setExpansion({ DefaultExpansion, { } });
- }
-}
-
-Line::Run::Run(const InlineItemRun& inlineItemRun, const Display::Rect& logicalRect, const Display::Run::TextContext& textContext, unsigned expansionOpportunityCount)
- : m_layoutBox(&inlineItemRun.layoutBox())
- , m_type(inlineItemRun.type())
- , m_logicalRect(logicalRect)
- , m_textContext(textContext)
- , m_expansionOpportunityCount(expansionOpportunityCount)
- , m_isCollapsedToVisuallyEmpty(inlineItemRun.isCollapsedToZeroAdvanceWidth())
-{
-}
-
-void Line::Run::adjustExpansionBehavior(ExpansionBehavior expansionBehavior)
-{
- ASSERT(isText());
- ASSERT(hasExpansionOpportunity());
- m_textContext->setExpansion({ expansionBehavior, m_textContext->expansion()->horizontalExpansion });
-}
-
-inline Optional<ExpansionBehavior> Line::Run::expansionBehavior() const
-{
- ASSERT(isText());
- if (auto expansionContext = m_textContext->expansion())
- return expansionContext->behavior;
- return { };
-}
-
-void Line::Run::setComputedHorizontalExpansion(LayoutUnit logicalExpansion)
-{
- ASSERT(isText());
- ASSERT(hasExpansionOpportunity());
- m_logicalRect.expandHorizontally(logicalExpansion);
- m_textContext->setExpansion({ m_textContext->expansion()->behavior, logicalExpansion });
-}
-
-Line::Line(const InlineFormattingContext& inlineFormattingContext, Optional<TextAlignMode> horizontalAlignment, SkipAlignment skipAlignment)
- : m_inlineFormattingContext(inlineFormattingContext)
- , m_horizontalAlignment(horizontalAlignment)
- , m_skipAlignment(skipAlignment == SkipAlignment::Yes)
-{
-}
-
-Line::~Line()
-{
-}
-
-void Line::initialize(const Constraints& constraints)
-{
- ASSERT(m_skipAlignment || constraints.heightAndBaseline);
-
- LayoutUnit initialLineHeight;
- LayoutUnit initialBaselineOffset;
- if (constraints.heightAndBaseline) {
- m_initialStrut = constraints.heightAndBaseline->strut;
- initialLineHeight = constraints.heightAndBaseline->height;
- initialBaselineOffset = constraints.heightAndBaseline->baselineOffset;
- } else
- m_initialStrut = { };
-
- auto lineRect = Display::Rect { constraints.logicalTopLeft, { }, initialLineHeight };
- auto baseline = LineBox::Baseline { initialBaselineOffset, initialLineHeight - initialBaselineOffset };
- m_lineBox = LineBox { lineRect, baseline, initialBaselineOffset };
- m_lineLogicalWidth = constraints.availableLogicalWidth;
- m_hasIntrusiveFloat = constraints.lineIsConstrainedByFloat;
-
- m_inlineItemRuns.clear();
- m_trimmableContent.clear();
-}
-
-static bool shouldPreserveTrailingContent(const InlineTextItem& inlineTextItem)
-{
- if (!inlineTextItem.isWhitespace())
- return true;
- auto whitespace = inlineTextItem.style().whiteSpace();
- return whitespace == WhiteSpace::Pre || whitespace == WhiteSpace::PreWrap;
-}
-
-static bool shouldPreserveLeadingContent(const InlineTextItem& inlineTextItem)
-{
- if (!inlineTextItem.isWhitespace())
- return true;
- auto whitespace = inlineTextItem.style().whiteSpace();
- return whitespace == WhiteSpace::Pre || whitespace == WhiteSpace::PreWrap || whitespace == WhiteSpace::BreakSpaces;
-}
-
-Line::RunList Line::close(IsLastLineWithInlineContent isLastLineWithInlineContent)
-{
- // 1. Remove trimmable trailing content.
- // 2. Join text runs together when possible [foo][ ][bar] -> [foo bar].
- // 3. Align merged runs both vertically and horizontally.
- removeTrailingTrimmableContent();
- RunList runList;
- unsigned runIndex = 0;
- while (runIndex < m_inlineItemRuns.size()) {
- // Merge eligible runs.
- auto continousContent = ContinousContent { *m_inlineItemRuns[runIndex], isTextAlignJustify() };
- while (++runIndex < m_inlineItemRuns.size()) {
- if (!continousContent.append(*m_inlineItemRuns[runIndex]))
- break;
- }
- runList.append(continousContent.close());
- }
-
- if (!m_skipAlignment) {
- if (isVisuallyEmpty()) {
- m_lineBox.resetBaseline();
- m_lineBox.setLogicalHeight({ });
- }
- // Remove descent when all content is baseline aligned but none of them have descent.
- if (formattingContext().quirks().lineDescentNeedsCollapsing(runList)) {
- m_lineBox.shrinkVertically(m_lineBox.baseline().descent());
- m_lineBox.resetDescent();
- }
- alignContentVertically(runList);
- alignContentHorizontally(runList, isLastLineWithInlineContent);
- }
- return runList;
-}
-
-void Line::alignContentVertically(RunList& runList)
-{
- ASSERT(!m_skipAlignment);
- for (auto& run : runList) {
- adjustBaselineAndLineHeight(run);
- run.setLogicalHeight(runContentHeight(run));
- }
-
- for (auto& run : runList) {
- LayoutUnit logicalTop;
- auto& layoutBox = run.layoutBox();
- auto verticalAlign = layoutBox.style().verticalAlign();
- auto ascent = layoutBox.style().fontMetrics().ascent();
-
- switch (verticalAlign) {
- case VerticalAlign::Baseline:
- if (run.isForcedLineBreak() || run.isText())
- logicalTop = baselineOffset() - ascent;
- else if (run.isContainerStart()) {
- auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
- logicalTop = baselineOffset() - ascent - boxGeometry.borderTop() - boxGeometry.paddingTop().valueOr(0);
- } else if (layoutBox.isInlineBlockBox() && layoutBox.establishesInlineFormattingContext()) {
- auto& formattingState = downcast<InlineFormattingState>(layoutState().establishedFormattingState(downcast<Container>(layoutBox)));
- // Spec makes us generate at least one line -even if it is empty.
- ASSERT(!formattingState.lineBoxes().isEmpty());
- auto inlineBlockBaselineOffset = formattingState.lineBoxes().last()->baselineOffset();
- // The inline-block's baseline offset is relative to its content box. Let's convert it relative to the margin box.
- // inline-block
- // \
- // _______________ <- margin box
- // |
- // | ____________ <- border box
- // | |
- // | | _________ <- content box
- // | | | ^
- // | | | | <- baseline offset
- // | | | |
- // text | | | v text
- // -----|-|-|---------- <- baseline
- //
- auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
- auto baselineOffsetFromMarginBox = boxGeometry.marginBefore() + boxGeometry.borderTop() + boxGeometry.paddingTop().valueOr(0) + inlineBlockBaselineOffset;
- logicalTop = baselineOffset() - baselineOffsetFromMarginBox;
- } else
- logicalTop = baselineOffset() - run.logicalRect().height();
- break;
- case VerticalAlign::Top:
- logicalTop = { };
- break;
- case VerticalAlign::Bottom:
- logicalTop = logicalBottom() - run.logicalRect().height();
- break;
- default:
- ASSERT_NOT_IMPLEMENTED_YET();
- break;
- }
- run.adjustLogicalTop(logicalTop);
- // Convert runs from relative to the line top/left to the formatting root's border box top/left.
- run.moveVertically(this->logicalTop());
- run.moveHorizontally(this->logicalLeft());
- }
-}
-
-void Line::justifyRuns(RunList& runList) const
-{
- ASSERT(!runList.isEmpty());
- ASSERT(availableWidth() > 0);
- // Need to fix up the last run first.
- auto& lastRun = runList.last();
- if (lastRun.hasExpansionOpportunity())
- lastRun.adjustExpansionBehavior(*lastRun.expansionBehavior() | ForbidTrailingExpansion);
- // Collect the expansion opportunity numbers.
- auto expansionOpportunityCount = 0;
- for (auto& run : runList)
- expansionOpportunityCount += run.expansionOpportunityCount();
- // Nothing to distribute?
- if (!expansionOpportunityCount)
- return;
- // Distribute the extra space.
- auto expansionToDistribute = availableWidth() / expansionOpportunityCount;
- LayoutUnit accumulatedExpansion;
- for (auto& run : runList) {
- // Expand and moves runs by the accumulated expansion.
- if (!run.hasExpansionOpportunity()) {
- run.moveHorizontally(accumulatedExpansion);
- continue;
- }
- ASSERT(run.expansionOpportunityCount());
- auto computedExpansion = expansionToDistribute * run.expansionOpportunityCount();
- run.setComputedHorizontalExpansion(computedExpansion);
- run.moveHorizontally(accumulatedExpansion);
- accumulatedExpansion += computedExpansion;
- }
-}
-
-void Line::alignContentHorizontally(RunList& runList, IsLastLineWithInlineContent lastLine) const
-{
- ASSERT(!m_skipAlignment);
- if (runList.isEmpty() || availableWidth() <= 0)
- return;
-
- if (isTextAlignJustify()) {
- // Do not justify align the last line.
- if (lastLine == IsLastLineWithInlineContent::No)
- justifyRuns(runList);
- return;
- }
-
- auto adjustmentForAlignment = [&]() -> Optional<LayoutUnit> {
- switch (*m_horizontalAlignment) {
- case TextAlignMode::Left:
- case TextAlignMode::WebKitLeft:
- case TextAlignMode::Start:
- return { };
- case TextAlignMode::Right:
- case TextAlignMode::WebKitRight:
- case TextAlignMode::End:
- return std::max(availableWidth(), 0_lu);
- case TextAlignMode::Center:
- case TextAlignMode::WebKitCenter:
- return std::max(availableWidth() / 2, 0_lu);
- case TextAlignMode::Justify:
- ASSERT_NOT_REACHED();
- break;
- }
- ASSERT_NOT_REACHED();
- return { };
- };
-
- auto adjustment = adjustmentForAlignment();
- if (!adjustment)
- return;
-
- for (auto& run : runList)
- run.moveHorizontally(*adjustment);
-}
-
-void Line::removeTrailingTrimmableContent()
-{
- if (m_trimmableContent.isEmpty() || m_inlineItemRuns.isEmpty())
- return;
-
- // Collapse trimmable trailing content
- for (auto* trimmableRun : m_trimmableContent.runs()) {
- ASSERT(trimmableRun->isText());
- // FIXME: We might need to be able to differentiate between trimmed and collapsed runs.
- trimmableRun->setCollapsesToZeroAdvanceWidth();
- }
- m_lineBox.shrinkHorizontally(m_trimmableContent.width());
- m_trimmableContent.clear();
-}
-
-void Line::moveLogicalLeft(LayoutUnit delta)
-{
- if (!delta)
- return;
- ASSERT(delta > 0);
- m_lineBox.moveHorizontally(delta);
- m_lineLogicalWidth -= delta;
-}
-
-void Line::moveLogicalRight(LayoutUnit delta)
-{
- ASSERT(delta > 0);
- m_lineLogicalWidth -= delta;
-}
-
-void Line::append(const InlineItem& inlineItem, LayoutUnit logicalWidth)
-{
- if (inlineItem.isText())
- return appendTextContent(downcast<InlineTextItem>(inlineItem), logicalWidth);
- if (inlineItem.isForcedLineBreak())
- return appendLineBreak(inlineItem);
- if (inlineItem.isContainerStart())
- return appendInlineContainerStart(inlineItem, logicalWidth);
- if (inlineItem.isContainerEnd())
- return appendInlineContainerEnd(inlineItem, logicalWidth);
- if (inlineItem.layoutBox().replaced())
- return appendReplacedInlineBox(inlineItem, logicalWidth);
- appendNonReplacedInlineBox(inlineItem, logicalWidth);
-}
-
-void Line::appendNonBreakableSpace(const InlineItem& inlineItem, const Display::Rect& logicalRect)
-{
- m_inlineItemRuns.append(makeUnique<InlineItemRun>(inlineItem, logicalRect));
- m_lineBox.expandHorizontally(logicalRect.width());
- if (logicalRect.width())
- m_lineBox.setIsConsideredNonEmpty();
-}
-
-void Line::appendInlineContainerStart(const InlineItem& inlineItem, LayoutUnit logicalWidth)
-{
- // This is really just a placeholder to mark the start of the inline level container <span>.
- appendNonBreakableSpace(inlineItem, Display::Rect { 0, contentLogicalWidth(), logicalWidth, { } });
-}
-
-void Line::appendInlineContainerEnd(const InlineItem& inlineItem, LayoutUnit logicalWidth)
-{
- // This is really just a placeholder to mark the end of the inline level container </span>.
- appendNonBreakableSpace(inlineItem, Display::Rect { 0, contentLogicalRight(), logicalWidth, { } });
-}
-
-void Line::appendTextContent(const InlineTextItem& inlineItem, LayoutUnit logicalWidth)
-{
- auto isTrimmable = !shouldPreserveTrailingContent(inlineItem);
- if (!isTrimmable)
- m_trimmableContent.clear();
-
- auto willCollapseCompletely = [&] {
- // Empty run.
- if (!inlineItem.length()) {
- ASSERT(!logicalWidth);
- return true;
- }
- // Leading whitespace.
- if (m_inlineItemRuns.isEmpty())
- return !shouldPreserveLeadingContent(inlineItem);
-
- if (!inlineItem.isCollapsible())
- return false;
- // Check if the last item is collapsed as well.
- for (auto i = m_inlineItemRuns.size(); i--;) {
- auto& run = m_inlineItemRuns[i];
- if (run->isBox())
- return false;
- // https://drafts.csswg.org/css-text-3/#white-space-phase-1
- // Any collapsible space immediately following another collapsible space—even one outside the boundary of the inline containing that space,
- // provided both spaces are within the same inline formatting context—is collapsed to have zero advance width.
- // : "<span> </span> " <- the trailing whitespace collapses completely.
- // Not that when the inline container has preserve whitespace style, "<span style="white-space: pre"> </span> " <- this whitespace stays around.
- if (run->isText())
- return run->isCollapsible();
- ASSERT(run->isContainerStart() || run->isContainerEnd());
- }
- return true;
- };
-
- auto collapsedRun = inlineItem.isCollapsible() && inlineItem.length() > 1;
- auto contentStart = inlineItem.start();
- auto contentLength = collapsedRun ? 1 : inlineItem.length();
- auto lineRun = makeUnique<InlineItemRun>(inlineItem, Display::Rect { 0, contentLogicalWidth(), logicalWidth, { } },
- Display::Run::TextContext { contentStart, contentLength, inlineItem.layoutBox().textContext()->content.substring(contentStart, contentLength) });
-
- auto collapsesToZeroAdvanceWidth = willCollapseCompletely();
- if (collapsesToZeroAdvanceWidth)
- lineRun->setCollapsesToZeroAdvanceWidth();
- else
- m_lineBox.setIsConsideredNonEmpty();
-
- if (collapsedRun)
- lineRun->setIsCollapsed();
- if (isTrimmable)
- m_trimmableContent.append(*lineRun);
-
- m_lineBox.expandHorizontally(lineRun->logicalRect().width());
- m_inlineItemRuns.append(WTFMove(lineRun));
-}
-
-void Line::appendNonReplacedInlineBox(const InlineItem& inlineItem, LayoutUnit logicalWidth)
-{
- auto& layoutBox = inlineItem.layoutBox();
- auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
- auto horizontalMargin = boxGeometry.horizontalMargin();
- m_inlineItemRuns.append(makeUnique<InlineItemRun>(inlineItem, Display::Rect { 0, contentLogicalWidth() + horizontalMargin.start, logicalWidth, { } }));
- m_lineBox.expandHorizontally(logicalWidth + horizontalMargin.start + horizontalMargin.end);
- m_lineBox.setIsConsideredNonEmpty();
- m_trimmableContent.clear();
- if (!layoutBox.establishesFormattingContext() || !boxGeometry.isEmpty())
- m_lineBox.setIsConsideredNonEmpty();
-}
-
-void Line::appendReplacedInlineBox(const InlineItem& inlineItem, LayoutUnit logicalWidth)
-{
- ASSERT(inlineItem.layoutBox().isReplaced());
- // FIXME: Surely replaced boxes behave differently.
- appendNonReplacedInlineBox(inlineItem, logicalWidth);
- m_lineBox.setIsConsideredNonEmpty();
-}
-
-void Line::appendLineBreak(const InlineItem& inlineItem)
-{
- m_lineBox.setIsConsideredNonEmpty();
- m_inlineItemRuns.append(makeUnique<InlineItemRun>(inlineItem, Display::Rect { 0, contentLogicalWidth(), { }, { } }));
-}
-
-void Line::adjustBaselineAndLineHeight(const Run& run)
-{
- auto& baseline = m_lineBox.baseline();
- if (run.isText() || run.isForcedLineBreak()) {
- // For text content we set the baseline either through the initial strut (set by the formatting context root) or
- // through the inline container (start) -see above. Normally the text content itself does not stretch the line.
- if (!m_initialStrut)
- return;
- m_lineBox.setAscentIfGreater(m_initialStrut->ascent());
- m_lineBox.setDescentIfGreater(m_initialStrut->descent());
- m_lineBox.setLogicalHeightIfGreater(baseline.height());
- m_initialStrut = { };
- return;
- }
-
- auto& layoutBox = run.layoutBox();
- auto& style = layoutBox.style();
- if (run.isContainerStart()) {
- // Inline containers stretch the line by their font size.
- // Vertical margins, paddings and borders don't contribute to the line height.
- auto& fontMetrics = style.fontMetrics();
- if (style.verticalAlign() == VerticalAlign::Baseline) {
- auto halfLeading = halfLeadingMetrics(fontMetrics, style.computedLineHeight());
- // Both halfleading ascent and descent could be negative (tall font vs. small line-height value)
- if (halfLeading.descent() > 0)
- m_lineBox.setDescentIfGreater(halfLeading.descent());
- if (halfLeading.ascent() > 0)
- m_lineBox.setAscentIfGreater(halfLeading.ascent());
- m_lineBox.setLogicalHeightIfGreater(baseline.height());
- } else
- m_lineBox.setLogicalHeightIfGreater(fontMetrics.height());
- return;
- }
-
- if (run.isContainerEnd()) {
- // The line's baseline and height have already been adjusted at ContainerStart.
- return;
- }
-
- if (run.isBox()) {
- auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
- auto marginBoxHeight = boxGeometry.marginBoxHeight();
-
- switch (style.verticalAlign()) {
- case VerticalAlign::Baseline: {
- if (layoutBox.isInlineBlockBox() && layoutBox.establishesInlineFormattingContext()) {
- // Inline-blocks with inline content always have baselines.
- auto& formattingState = downcast<InlineFormattingState>(layoutState().establishedFormattingState(downcast<Container>(layoutBox)));
- // Spec makes us generate at least one line -even if it is empty.
- ASSERT(!formattingState.lineBoxes().isEmpty());
- auto& lastLineBox = *formattingState.lineBoxes().last();
- auto inlineBlockBaseline = lastLineBox.baseline();
- auto beforeHeight = boxGeometry.marginBefore() + boxGeometry.borderTop() + boxGeometry.paddingTop().valueOr(0);
-
- m_lineBox.setAscentIfGreater(inlineBlockBaseline.ascent());
- m_lineBox.setDescentIfGreater(inlineBlockBaseline.descent());
- m_lineBox.setBaselineOffsetIfGreater(beforeHeight + lastLineBox.baselineOffset());
- m_lineBox.setLogicalHeightIfGreater(marginBoxHeight);
- } else {
- // Non inline-block boxes sit on the baseline (including their bottom margin).
- m_lineBox.setAscentIfGreater(marginBoxHeight);
- // Ignore negative descent (yes, negative descent is a thing).
- m_lineBox.setLogicalHeightIfGreater(marginBoxHeight + std::max(LayoutUnit(), baseline.descent()));
- }
- break;
- }
- case VerticalAlign::Top:
- // Top align content never changes the baseline, it only pushes the bottom of the line further down.
- m_lineBox.setLogicalHeightIfGreater(marginBoxHeight);
- break;
- case VerticalAlign::Bottom: {
- // Bottom aligned, tall content pushes the baseline further down from the line top.
- auto lineLogicalHeight = m_lineBox.logicalHeight();
- if (marginBoxHeight > lineLogicalHeight) {
- m_lineBox.setLogicalHeightIfGreater(marginBoxHeight);
- m_lineBox.setBaselineOffsetIfGreater(m_lineBox.baselineOffset() + (marginBoxHeight - lineLogicalHeight));
- }
- break;
- }
- default:
- ASSERT_NOT_IMPLEMENTED_YET();
- break;
- }
- return;
- }
- ASSERT_NOT_REACHED();
-}
-
-LayoutUnit Line::runContentHeight(const Run& run) const
-{
- ASSERT(!m_skipAlignment);
- auto& fontMetrics = run.layoutBox().style().fontMetrics();
- if (run.isText() || run.isForcedLineBreak())
- return fontMetrics.height();
-
- if (run.isContainerStart() || run.isContainerEnd())
- return fontMetrics.height();
-
- auto& layoutBox = run.layoutBox();
- auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
- if (layoutBox.replaced() || layoutBox.isFloatingPositioned())
- return boxGeometry.contentBoxHeight();
-
- // Non-replaced inline box (e.g. inline-block). It looks a bit misleading but their margin box is considered the content height here.
- return boxGeometry.marginBoxHeight();
-}
-
-void Line::TrimmableContent::append(InlineItemRun& inlineItemRun)
-{
- ASSERT(inlineItemRun.logicalRect().width() >= 0);
- m_width += inlineItemRun.logicalRect().width();
- m_inlineItemRuns.append(&inlineItemRun);
-}
-
-LineBox::Baseline Line::halfLeadingMetrics(const FontMetrics& fontMetrics, LayoutUnit lineLogicalHeight)
-{
- auto ascent = fontMetrics.ascent();
- auto descent = fontMetrics.descent();
- // 10.8.1 Leading and half-leading
- auto leading = lineLogicalHeight - (ascent + descent);
- // Inline tree is all integer based.
- auto adjustedAscent = std::max((ascent + leading / 2).floor(), 0);
- auto adjustedDescent = std::max((descent + leading / 2).ceil(), 0);
- return { adjustedAscent, adjustedDescent };
-}
-
-LayoutState& Line::layoutState() const
-{
- return formattingContext().layoutState();
-}
-
-const InlineFormattingContext& Line::formattingContext() const
-{
- return m_inlineFormattingContext;
-}
-
-}
-}
-
-#endif
Deleted: trunk/Source/WebCore/layout/inlineformatting/InlineLine.h (252864 => 252865)
--- trunk/Source/WebCore/layout/inlineformatting/InlineLine.h 2019-11-25 18:15:50 UTC (rev 252864)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLine.h 2019-11-25 18:33:52 UTC (rev 252865)
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2019 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
-
-#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
-
-#include "DisplayRun.h"
-#include "InlineItem.h"
-#include "InlineLineBox.h"
-#include "InlineTextItem.h"
-#include <wtf/IsoMalloc.h>
-
-namespace WebCore {
-namespace Layout {
-
-struct ContinousContent;
-class InlineFormattingContext;
-class InlineItemRun;
-
-class Line {
- WTF_MAKE_ISO_ALLOCATED(Line);
-public:
- struct Constraints {
- LayoutPoint logicalTopLeft;
- LayoutUnit availableLogicalWidth;
- bool lineIsConstrainedByFloat { false };
- struct HeightAndBaseline {
- LayoutUnit height;
- LayoutUnit baselineOffset;
- Optional<LineBox::Baseline> strut;
- };
- Optional<HeightAndBaseline> heightAndBaseline;
- };
- enum class SkipAlignment { No, Yes };
- Line(const InlineFormattingContext&, Optional<TextAlignMode>, SkipAlignment);
- ~Line();
-
- void initialize(const Constraints&);
- void append(const InlineItem&, LayoutUnit logicalWidth);
- bool isVisuallyEmpty() const { return m_lineBox.isConsideredEmpty(); }
- bool hasIntrusiveFloat() const { return m_hasIntrusiveFloat; }
- LayoutUnit availableWidth() const { return logicalWidth() - contentLogicalWidth(); }
-
- LayoutUnit trailingTrimmableWidth() const { return m_trimmableContent.width(); }
-
- const LineBox& lineBox() const { return m_lineBox; }
- void moveLogicalLeft(LayoutUnit);
- void moveLogicalRight(LayoutUnit);
- void setHasIntrusiveFloat() { m_hasIntrusiveFloat = true; }
-
- struct Run {
- Run(const InlineItemRun&);
- Run(const InlineItemRun&, const Display::Rect&, const Display::Run::TextContext&, unsigned expansionOpportunityCount);
- Run(Run&&) = default;
- Run& operator=(Run&& other) = default;
-
- bool isText() const { return m_type == InlineItem::Type::Text; }
- bool isBox() const { return m_type == InlineItem::Type::Box; }
- bool isForcedLineBreak() const { return m_type == InlineItem::Type::ForcedLineBreak; }
- bool isContainerStart() const { return m_type == InlineItem::Type::ContainerStart; }
- bool isContainerEnd() const { return m_type == InlineItem::Type::ContainerEnd; }
-
- const Box& layoutBox() const { return *m_layoutBox; }
- const Display::Rect& logicalRect() const { return m_logicalRect; }
- Optional<Display::Run::TextContext> textContext() const { return m_textContext; }
- bool isCollapsedToVisuallyEmpty() const { return m_isCollapsedToVisuallyEmpty; }
-
- private:
- friend class Line;
-
- void adjustLogicalTop(LayoutUnit logicalTop) { m_logicalRect.setTop(logicalTop); }
- void moveHorizontally(LayoutUnit offset) { m_logicalRect.moveHorizontally(offset); }
- void moveVertically(LayoutUnit offset) { m_logicalRect.moveVertically(offset); }
- void setLogicalHeight(LayoutUnit logicalHeight) { m_logicalRect.setHeight(logicalHeight); }
-
- bool hasExpansionOpportunity() const { return m_expansionOpportunityCount; }
- Optional<ExpansionBehavior> expansionBehavior() const;
- unsigned expansionOpportunityCount() const { return m_expansionOpportunityCount; }
- void setComputedHorizontalExpansion(LayoutUnit logicalExpansion);
- void adjustExpansionBehavior(ExpansionBehavior);
-
- const Box* m_layoutBox { nullptr };
- InlineItem::Type m_type;
- Display::Rect m_logicalRect;
- Optional<Display::Run::TextContext> m_textContext;
- unsigned m_expansionOpportunityCount { 0 };
- bool m_isCollapsedToVisuallyEmpty { false };
- };
- using RunList = Vector<Run, 50>;
- enum class IsLastLineWithInlineContent { No, Yes };
- RunList close(IsLastLineWithInlineContent = IsLastLineWithInlineContent::No);
-
- static LineBox::Baseline halfLeadingMetrics(const FontMetrics&, LayoutUnit lineLogicalHeight);
-
-private:
- LayoutUnit logicalTop() const { return m_lineBox.logicalTop(); }
- LayoutUnit logicalBottom() const { return m_lineBox.logicalBottom(); }
-
- LayoutUnit logicalLeft() const { return m_lineBox.logicalLeft(); }
- LayoutUnit logicalRight() const { return logicalLeft() + logicalWidth(); }
-
- LayoutUnit logicalWidth() const { return m_lineLogicalWidth; }
- LayoutUnit logicalHeight() const { return m_lineBox.logicalHeight(); }
-
- LayoutUnit contentLogicalWidth() const { return m_lineBox.logicalWidth(); }
- LayoutUnit contentLogicalRight() const { return m_lineBox.logicalRight(); }
- LayoutUnit baselineOffset() const { return m_lineBox.baselineOffset(); }
-
- void appendNonBreakableSpace(const InlineItem&, const Display::Rect& logicalRect);
- void appendTextContent(const InlineTextItem&, LayoutUnit logicalWidth);
- void appendNonReplacedInlineBox(const InlineItem&, LayoutUnit logicalWidth);
- void appendReplacedInlineBox(const InlineItem&, LayoutUnit logicalWidth);
- void appendInlineContainerStart(const InlineItem&, LayoutUnit logicalWidth);
- void appendInlineContainerEnd(const InlineItem&, LayoutUnit logicalWidth);
- void appendLineBreak(const InlineItem&);
-
- void removeTrailingTrimmableContent();
- void alignContentHorizontally(RunList&, IsLastLineWithInlineContent) const;
- void alignContentVertically(RunList&);
-
- void adjustBaselineAndLineHeight(const Run&);
- LayoutUnit runContentHeight(const Run&) const;
-
- bool isTextAlignJustify() const { return m_horizontalAlignment == TextAlignMode::Justify; };
- void justifyRuns(RunList&) const;
-
- LayoutState& layoutState() const;
- const InlineFormattingContext& formattingContext() const;
-
- const InlineFormattingContext& m_inlineFormattingContext;
- Vector<std::unique_ptr<InlineItemRun>, 50> m_inlineItemRuns;
- struct TrimmableContent {
- void append(InlineItemRun&);
- void clear();
-
- LayoutUnit width() const { return m_width; }
- using TrimmableList = Vector<InlineItemRun*, 5>;
- TrimmableList& runs() { return m_inlineItemRuns; }
- bool isEmpty() const { return m_inlineItemRuns.isEmpty(); }
-
- private:
- TrimmableList m_inlineItemRuns;
- LayoutUnit m_width;
- };
- TrimmableContent m_trimmableContent;
- Optional<LineBox::Baseline> m_initialStrut;
- LayoutUnit m_lineLogicalWidth;
- Optional<TextAlignMode> m_horizontalAlignment;
- bool m_skipAlignment { false };
- bool m_hasIntrusiveFloat { false };
- LineBox m_lineBox;
-};
-
-inline void Line::TrimmableContent::clear()
-{
- m_inlineItemRuns.clear();
- m_width = { };
-}
-
-}
-}
-#endif
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBreaker.cpp (252864 => 252865)
--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBreaker.cpp 2019-11-25 18:15:50 UTC (rev 252864)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBreaker.cpp 2019-11-25 18:33:52 UTC (rev 252865)
@@ -31,6 +31,7 @@
#include "Hyphenation.h"
#include "InlineItem.h"
#include "InlineTextItem.h"
+#include "TextUtil.h"
namespace WebCore {
namespace Layout {
Copied: trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp (from rev 252864, trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp) (0 => 252865)
--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp (rev 0)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp 2019-11-25 18:33:52 UTC (rev 252865)
@@ -0,0 +1,713 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "config.h"
+#include "InlineLineBuilder.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "InlineFormattingContext.h"
+#include "TextUtil.h"
+#include <wtf/IsoMallocInlines.h>
+
+namespace WebCore {
+namespace Layout {
+
+class InlineItemRun {
+WTF_MAKE_ISO_ALLOCATED_INLINE(InlineItemRun);
+public:
+ InlineItemRun(const InlineItem&, const Display::Rect&, WTF::Optional<Display::Run::TextContext> = WTF::nullopt);
+
+ const Box& layoutBox() const { return m_inlineItem.layoutBox(); }
+ const Display::Rect& logicalRect() const { return m_logicalRect; }
+ Optional<Display::Run::TextContext> textContext() const { return m_textContext; }
+
+ bool isText() const { return m_inlineItem.isText(); }
+ bool isBox() const { return m_inlineItem.isBox(); }
+ bool isContainerStart() const { return m_inlineItem.isContainerStart(); }
+ bool isContainerEnd() const { return m_inlineItem.isContainerEnd(); }
+ bool isForcedLineBreak() const { return m_inlineItem.isForcedLineBreak(); }
+ InlineItem::Type type() const { return m_inlineItem.type(); }
+
+ void setIsCollapsed() { m_isCollapsed = true; }
+ bool isCollapsed() const { return m_isCollapsed; }
+
+ void setCollapsesToZeroAdvanceWidth();
+ bool isCollapsedToZeroAdvanceWidth() const { return m_collapsedToZeroAdvanceWidth; }
+
+ bool isCollapsible() const { return is<InlineTextItem>(m_inlineItem) && downcast<InlineTextItem>(m_inlineItem).isCollapsible(); }
+ bool isWhitespace() const { return is<InlineTextItem>(m_inlineItem) && downcast<InlineTextItem>(m_inlineItem).isWhitespace(); }
+
+ bool hasExpansionOpportunity() const { return isWhitespace() && !isCollapsedToZeroAdvanceWidth(); }
+
+private:
+ const InlineItem& m_inlineItem;
+ Display::Rect m_logicalRect;
+ const Optional<Display::Run::TextContext> m_textContext;
+ bool m_isCollapsed { false };
+ bool m_collapsedToZeroAdvanceWidth { false };
+};
+
+InlineItemRun::InlineItemRun(const InlineItem& inlineItem, const Display::Rect& logicalRect, WTF::Optional<Display::Run::TextContext> textContext)
+ : m_inlineItem(inlineItem)
+ , m_logicalRect(logicalRect)
+ , m_textContext(textContext)
+{
+}
+
+void InlineItemRun::setCollapsesToZeroAdvanceWidth()
+{
+ m_collapsedToZeroAdvanceWidth = true;
+ m_logicalRect.setWidth({ });
+}
+
+struct ContinousContent {
+public:
+ ContinousContent(const InlineItemRun&, bool textIsAlignJustify);
+
+ bool append(const InlineItemRun&);
+ LineBuilder::Run close();
+
+private:
+ static bool canBeExpanded(const InlineItemRun& run) { return run.isText() && !run.isCollapsed() && !run.isCollapsedToZeroAdvanceWidth(); }
+ bool canBeMerged(const InlineItemRun& run) const { return run.isText() && !run.isCollapsedToZeroAdvanceWidth() && &m_initialInlineRun.layoutBox() == &run.layoutBox(); }
+
+ const InlineItemRun& m_initialInlineRun;
+ const bool m_textIsAlignJustify { false };
+ unsigned m_expandedLength { 0 };
+ LayoutUnit m_expandedWidth;
+ bool m_trailingRunCanBeExpanded { false };
+ bool m_hasTrailingExpansionOpportunity { false };
+ unsigned m_expansionOpportunityCount { 0 };
+};
+
+ContinousContent::ContinousContent(const InlineItemRun& initialInlineRun, bool textIsAlignJustify)
+ : m_initialInlineRun(initialInlineRun)
+ , m_textIsAlignJustify(textIsAlignJustify)
+ , m_trailingRunCanBeExpanded(canBeExpanded(initialInlineRun))
+{
+}
+
+bool ContinousContent::append(const InlineItemRun& inlineItemRun)
+{
+ // Merged content needs to be continuous.
+ if (!m_trailingRunCanBeExpanded)
+ return false;
+ if (!canBeMerged(inlineItemRun))
+ return false;
+
+ m_trailingRunCanBeExpanded = canBeExpanded(inlineItemRun);
+
+ ASSERT(inlineItemRun.isText());
+ m_expandedLength += inlineItemRun.textContext()->length();
+ m_expandedWidth += inlineItemRun.logicalRect().width();
+
+ if (m_textIsAlignJustify) {
+ m_hasTrailingExpansionOpportunity = inlineItemRun.hasExpansionOpportunity();
+ if (m_hasTrailingExpansionOpportunity)
+ ++m_expansionOpportunityCount;
+ }
+ return true;
+}
+
+LineBuilder::Run ContinousContent::close()
+{
+ if (!m_expandedLength)
+ return { m_initialInlineRun };
+ // Expand the text content and set the expansion opportunities.
+ ASSERT(m_initialInlineRun.isText());
+ auto logicalRect = m_initialInlineRun.logicalRect();
+ logicalRect.expandHorizontally(m_expandedWidth);
+
+ auto textContext = *m_initialInlineRun.textContext();
+ auto length = textContext.length() + m_expandedLength;
+ textContext.expand(m_initialInlineRun.layoutBox().textContext()->content.substring(textContext.start(), length), length);
+
+ if (m_textIsAlignJustify) {
+ // FIXME: This is a very simple expansion merge. We should eventually switch over to FontCascade::expansionOpportunityCount.
+ ExpansionBehavior expansionBehavior = m_hasTrailingExpansionOpportunity ? (ForbidLeadingExpansion | AllowTrailingExpansion) : (AllowLeadingExpansion | AllowTrailingExpansion);
+ if (m_initialInlineRun.hasExpansionOpportunity())
+ ++m_expansionOpportunityCount;
+ textContext.setExpansion({ expansionBehavior, { } });
+ }
+ return { m_initialInlineRun, logicalRect, textContext, m_expansionOpportunityCount };
+}
+
+LineBuilder::Run::Run(const InlineItemRun& inlineItemRun)
+ : m_layoutBox(&inlineItemRun.layoutBox())
+ , m_type(inlineItemRun.type())
+ , m_logicalRect(inlineItemRun.logicalRect())
+ , m_textContext(inlineItemRun.textContext())
+ , m_isCollapsedToVisuallyEmpty(inlineItemRun.isCollapsedToZeroAdvanceWidth())
+{
+ if (inlineItemRun.hasExpansionOpportunity()) {
+ m_expansionOpportunityCount = 1;
+ ASSERT(m_textContext);
+ m_textContext->setExpansion({ DefaultExpansion, { } });
+ }
+}
+
+LineBuilder::Run::Run(const InlineItemRun& inlineItemRun, const Display::Rect& logicalRect, const Display::Run::TextContext& textContext, unsigned expansionOpportunityCount)
+ : m_layoutBox(&inlineItemRun.layoutBox())
+ , m_type(inlineItemRun.type())
+ , m_logicalRect(logicalRect)
+ , m_textContext(textContext)
+ , m_expansionOpportunityCount(expansionOpportunityCount)
+ , m_isCollapsedToVisuallyEmpty(inlineItemRun.isCollapsedToZeroAdvanceWidth())
+{
+}
+
+void LineBuilder::Run::adjustExpansionBehavior(ExpansionBehavior expansionBehavior)
+{
+ ASSERT(isText());
+ ASSERT(hasExpansionOpportunity());
+ m_textContext->setExpansion({ expansionBehavior, m_textContext->expansion()->horizontalExpansion });
+}
+
+inline Optional<ExpansionBehavior> LineBuilder::Run::expansionBehavior() const
+{
+ ASSERT(isText());
+ if (auto expansionContext = m_textContext->expansion())
+ return expansionContext->behavior;
+ return { };
+}
+
+void LineBuilder::Run::setComputedHorizontalExpansion(LayoutUnit logicalExpansion)
+{
+ ASSERT(isText());
+ ASSERT(hasExpansionOpportunity());
+ m_logicalRect.expandHorizontally(logicalExpansion);
+ m_textContext->setExpansion({ m_textContext->expansion()->behavior, logicalExpansion });
+}
+
+LineBuilder::LineBuilder(const InlineFormattingContext& inlineFormattingContext, Optional<TextAlignMode> horizontalAlignment, SkipAlignment skipAlignment)
+ : m_inlineFormattingContext(inlineFormattingContext)
+ , m_horizontalAlignment(horizontalAlignment)
+ , m_skipAlignment(skipAlignment == SkipAlignment::Yes)
+{
+}
+
+LineBuilder::~LineBuilder()
+{
+}
+
+void LineBuilder::initialize(const Constraints& constraints)
+{
+ ASSERT(m_skipAlignment || constraints.heightAndBaseline);
+
+ LayoutUnit initialLineHeight;
+ LayoutUnit initialBaselineOffset;
+ if (constraints.heightAndBaseline) {
+ m_initialStrut = constraints.heightAndBaseline->strut;
+ initialLineHeight = constraints.heightAndBaseline->height;
+ initialBaselineOffset = constraints.heightAndBaseline->baselineOffset;
+ } else
+ m_initialStrut = { };
+
+ auto lineRect = Display::Rect { constraints.logicalTopLeft, { }, initialLineHeight };
+ auto baseline = LineBox::Baseline { initialBaselineOffset, initialLineHeight - initialBaselineOffset };
+ m_lineBox = LineBox { lineRect, baseline, initialBaselineOffset };
+ m_lineLogicalWidth = constraints.availableLogicalWidth;
+ m_hasIntrusiveFloat = constraints.lineIsConstrainedByFloat;
+
+ m_inlineItemRuns.clear();
+ m_trimmableContent.clear();
+}
+
+static bool shouldPreserveTrailingContent(const InlineTextItem& inlineTextItem)
+{
+ if (!inlineTextItem.isWhitespace())
+ return true;
+ auto whitespace = inlineTextItem.style().whiteSpace();
+ return whitespace == WhiteSpace::Pre || whitespace == WhiteSpace::PreWrap;
+}
+
+static bool shouldPreserveLeadingContent(const InlineTextItem& inlineTextItem)
+{
+ if (!inlineTextItem.isWhitespace())
+ return true;
+ auto whitespace = inlineTextItem.style().whiteSpace();
+ return whitespace == WhiteSpace::Pre || whitespace == WhiteSpace::PreWrap || whitespace == WhiteSpace::BreakSpaces;
+}
+
+LineBuilder::RunList LineBuilder::close(IsLastLineWithInlineContent isLastLineWithInlineContent)
+{
+ // 1. Remove trimmable trailing content.
+ // 2. Join text runs together when possible [foo][ ][bar] -> [foo bar].
+ // 3. Align merged runs both vertically and horizontally.
+ removeTrailingTrimmableContent();
+ RunList runList;
+ unsigned runIndex = 0;
+ while (runIndex < m_inlineItemRuns.size()) {
+ // Merge eligible runs.
+ auto continousContent = ContinousContent { *m_inlineItemRuns[runIndex], isTextAlignJustify() };
+ while (++runIndex < m_inlineItemRuns.size()) {
+ if (!continousContent.append(*m_inlineItemRuns[runIndex]))
+ break;
+ }
+ runList.append(continousContent.close());
+ }
+
+ if (!m_skipAlignment) {
+ if (isVisuallyEmpty()) {
+ m_lineBox.resetBaseline();
+ m_lineBox.setLogicalHeight({ });
+ }
+ // Remove descent when all content is baseline aligned but none of them have descent.
+ if (formattingContext().quirks().lineDescentNeedsCollapsing(runList)) {
+ m_lineBox.shrinkVertically(m_lineBox.baseline().descent());
+ m_lineBox.resetDescent();
+ }
+ alignContentVertically(runList);
+ alignContentHorizontally(runList, isLastLineWithInlineContent);
+ }
+ return runList;
+}
+
+void LineBuilder::alignContentVertically(RunList& runList)
+{
+ ASSERT(!m_skipAlignment);
+ for (auto& run : runList) {
+ adjustBaselineAndLineHeight(run);
+ run.setLogicalHeight(runContentHeight(run));
+ }
+
+ for (auto& run : runList) {
+ LayoutUnit logicalTop;
+ auto& layoutBox = run.layoutBox();
+ auto verticalAlign = layoutBox.style().verticalAlign();
+ auto ascent = layoutBox.style().fontMetrics().ascent();
+
+ switch (verticalAlign) {
+ case VerticalAlign::Baseline:
+ if (run.isForcedLineBreak() || run.isText())
+ logicalTop = baselineOffset() - ascent;
+ else if (run.isContainerStart()) {
+ auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
+ logicalTop = baselineOffset() - ascent - boxGeometry.borderTop() - boxGeometry.paddingTop().valueOr(0);
+ } else if (layoutBox.isInlineBlockBox() && layoutBox.establishesInlineFormattingContext()) {
+ auto& formattingState = downcast<InlineFormattingState>(layoutState().establishedFormattingState(downcast<Container>(layoutBox)));
+ // Spec makes us generate at least one line -even if it is empty.
+ ASSERT(!formattingState.lineBoxes().isEmpty());
+ auto inlineBlockBaselineOffset = formattingState.lineBoxes().last()->baselineOffset();
+ // The inline-block's baseline offset is relative to its content box. Let's convert it relative to the margin box.
+ // inline-block
+ // \
+ // _______________ <- margin box
+ // |
+ // | ____________ <- border box
+ // | |
+ // | | _________ <- content box
+ // | | | ^
+ // | | | | <- baseline offset
+ // | | | |
+ // text | | | v text
+ // -----|-|-|---------- <- baseline
+ //
+ auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
+ auto baselineOffsetFromMarginBox = boxGeometry.marginBefore() + boxGeometry.borderTop() + boxGeometry.paddingTop().valueOr(0) + inlineBlockBaselineOffset;
+ logicalTop = baselineOffset() - baselineOffsetFromMarginBox;
+ } else
+ logicalTop = baselineOffset() - run.logicalRect().height();
+ break;
+ case VerticalAlign::Top:
+ logicalTop = { };
+ break;
+ case VerticalAlign::Bottom:
+ logicalTop = logicalBottom() - run.logicalRect().height();
+ break;
+ default:
+ ASSERT_NOT_IMPLEMENTED_YET();
+ break;
+ }
+ run.adjustLogicalTop(logicalTop);
+ // Convert runs from relative to the line top/left to the formatting root's border box top/left.
+ run.moveVertically(this->logicalTop());
+ run.moveHorizontally(this->logicalLeft());
+ }
+}
+
+void LineBuilder::justifyRuns(RunList& runList) const
+{
+ ASSERT(!runList.isEmpty());
+ ASSERT(availableWidth() > 0);
+ // Need to fix up the last run first.
+ auto& lastRun = runList.last();
+ if (lastRun.hasExpansionOpportunity())
+ lastRun.adjustExpansionBehavior(*lastRun.expansionBehavior() | ForbidTrailingExpansion);
+ // Collect the expansion opportunity numbers.
+ auto expansionOpportunityCount = 0;
+ for (auto& run : runList)
+ expansionOpportunityCount += run.expansionOpportunityCount();
+ // Nothing to distribute?
+ if (!expansionOpportunityCount)
+ return;
+ // Distribute the extra space.
+ auto expansionToDistribute = availableWidth() / expansionOpportunityCount;
+ LayoutUnit accumulatedExpansion;
+ for (auto& run : runList) {
+ // Expand and moves runs by the accumulated expansion.
+ if (!run.hasExpansionOpportunity()) {
+ run.moveHorizontally(accumulatedExpansion);
+ continue;
+ }
+ ASSERT(run.expansionOpportunityCount());
+ auto computedExpansion = expansionToDistribute * run.expansionOpportunityCount();
+ run.setComputedHorizontalExpansion(computedExpansion);
+ run.moveHorizontally(accumulatedExpansion);
+ accumulatedExpansion += computedExpansion;
+ }
+}
+
+void LineBuilder::alignContentHorizontally(RunList& runList, IsLastLineWithInlineContent lastLine) const
+{
+ ASSERT(!m_skipAlignment);
+ if (runList.isEmpty() || availableWidth() <= 0)
+ return;
+
+ if (isTextAlignJustify()) {
+ // Do not justify align the last line.
+ if (lastLine == IsLastLineWithInlineContent::No)
+ justifyRuns(runList);
+ return;
+ }
+
+ auto adjustmentForAlignment = [&]() -> Optional<LayoutUnit> {
+ switch (*m_horizontalAlignment) {
+ case TextAlignMode::Left:
+ case TextAlignMode::WebKitLeft:
+ case TextAlignMode::Start:
+ return { };
+ case TextAlignMode::Right:
+ case TextAlignMode::WebKitRight:
+ case TextAlignMode::End:
+ return std::max(availableWidth(), 0_lu);
+ case TextAlignMode::Center:
+ case TextAlignMode::WebKitCenter:
+ return std::max(availableWidth() / 2, 0_lu);
+ case TextAlignMode::Justify:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+ ASSERT_NOT_REACHED();
+ return { };
+ };
+
+ auto adjustment = adjustmentForAlignment();
+ if (!adjustment)
+ return;
+
+ for (auto& run : runList)
+ run.moveHorizontally(*adjustment);
+}
+
+void LineBuilder::removeTrailingTrimmableContent()
+{
+ if (m_trimmableContent.isEmpty() || m_inlineItemRuns.isEmpty())
+ return;
+
+ // Collapse trimmable trailing content
+ for (auto* trimmableRun : m_trimmableContent.runs()) {
+ ASSERT(trimmableRun->isText());
+ // FIXME: We might need to be able to differentiate between trimmed and collapsed runs.
+ trimmableRun->setCollapsesToZeroAdvanceWidth();
+ }
+ m_lineBox.shrinkHorizontally(m_trimmableContent.width());
+ m_trimmableContent.clear();
+}
+
+void LineBuilder::moveLogicalLeft(LayoutUnit delta)
+{
+ if (!delta)
+ return;
+ ASSERT(delta > 0);
+ m_lineBox.moveHorizontally(delta);
+ m_lineLogicalWidth -= delta;
+}
+
+void LineBuilder::moveLogicalRight(LayoutUnit delta)
+{
+ ASSERT(delta > 0);
+ m_lineLogicalWidth -= delta;
+}
+
+void LineBuilder::append(const InlineItem& inlineItem, LayoutUnit logicalWidth)
+{
+ if (inlineItem.isText())
+ return appendTextContent(downcast<InlineTextItem>(inlineItem), logicalWidth);
+ if (inlineItem.isForcedLineBreak())
+ return appendLineBreak(inlineItem);
+ if (inlineItem.isContainerStart())
+ return appendInlineContainerStart(inlineItem, logicalWidth);
+ if (inlineItem.isContainerEnd())
+ return appendInlineContainerEnd(inlineItem, logicalWidth);
+ if (inlineItem.layoutBox().replaced())
+ return appendReplacedInlineBox(inlineItem, logicalWidth);
+ appendNonReplacedInlineBox(inlineItem, logicalWidth);
+}
+
+void LineBuilder::appendNonBreakableSpace(const InlineItem& inlineItem, const Display::Rect& logicalRect)
+{
+ m_inlineItemRuns.append(makeUnique<InlineItemRun>(inlineItem, logicalRect));
+ m_lineBox.expandHorizontally(logicalRect.width());
+ if (logicalRect.width())
+ m_lineBox.setIsConsideredNonEmpty();
+}
+
+void LineBuilder::appendInlineContainerStart(const InlineItem& inlineItem, LayoutUnit logicalWidth)
+{
+ // This is really just a placeholder to mark the start of the inline level container <span>.
+ appendNonBreakableSpace(inlineItem, Display::Rect { 0, contentLogicalWidth(), logicalWidth, { } });
+}
+
+void LineBuilder::appendInlineContainerEnd(const InlineItem& inlineItem, LayoutUnit logicalWidth)
+{
+ // This is really just a placeholder to mark the end of the inline level container </span>.
+ appendNonBreakableSpace(inlineItem, Display::Rect { 0, contentLogicalRight(), logicalWidth, { } });
+}
+
+void LineBuilder::appendTextContent(const InlineTextItem& inlineItem, LayoutUnit logicalWidth)
+{
+ auto isTrimmable = !shouldPreserveTrailingContent(inlineItem);
+ if (!isTrimmable)
+ m_trimmableContent.clear();
+
+ auto willCollapseCompletely = [&] {
+ // Empty run.
+ if (!inlineItem.length()) {
+ ASSERT(!logicalWidth);
+ return true;
+ }
+ // Leading whitespace.
+ if (m_inlineItemRuns.isEmpty())
+ return !shouldPreserveLeadingContent(inlineItem);
+
+ if (!inlineItem.isCollapsible())
+ return false;
+ // Check if the last item is collapsed as well.
+ for (auto i = m_inlineItemRuns.size(); i--;) {
+ auto& run = m_inlineItemRuns[i];
+ if (run->isBox())
+ return false;
+ // https://drafts.csswg.org/css-text-3/#white-space-phase-1
+ // Any collapsible space immediately following another collapsible space—even one outside the boundary of the inline containing that space,
+ // provided both spaces are within the same inline formatting context—is collapsed to have zero advance width.
+ // : "<span> </span> " <- the trailing whitespace collapses completely.
+ // Not that when the inline container has preserve whitespace style, "<span style="white-space: pre"> </span> " <- this whitespace stays around.
+ if (run->isText())
+ return run->isCollapsible();
+ ASSERT(run->isContainerStart() || run->isContainerEnd());
+ }
+ return true;
+ };
+
+ auto collapsedRun = inlineItem.isCollapsible() && inlineItem.length() > 1;
+ auto contentStart = inlineItem.start();
+ auto contentLength = collapsedRun ? 1 : inlineItem.length();
+ auto lineRun = makeUnique<InlineItemRun>(inlineItem, Display::Rect { 0, contentLogicalWidth(), logicalWidth, { } },
+ Display::Run::TextContext { contentStart, contentLength, inlineItem.layoutBox().textContext()->content.substring(contentStart, contentLength) });
+
+ auto collapsesToZeroAdvanceWidth = willCollapseCompletely();
+ if (collapsesToZeroAdvanceWidth)
+ lineRun->setCollapsesToZeroAdvanceWidth();
+ else
+ m_lineBox.setIsConsideredNonEmpty();
+
+ if (collapsedRun)
+ lineRun->setIsCollapsed();
+ if (isTrimmable)
+ m_trimmableContent.append(*lineRun);
+
+ m_lineBox.expandHorizontally(lineRun->logicalRect().width());
+ m_inlineItemRuns.append(WTFMove(lineRun));
+}
+
+void LineBuilder::appendNonReplacedInlineBox(const InlineItem& inlineItem, LayoutUnit logicalWidth)
+{
+ auto& layoutBox = inlineItem.layoutBox();
+ auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
+ auto horizontalMargin = boxGeometry.horizontalMargin();
+ m_inlineItemRuns.append(makeUnique<InlineItemRun>(inlineItem, Display::Rect { 0, contentLogicalWidth() + horizontalMargin.start, logicalWidth, { } }));
+ m_lineBox.expandHorizontally(logicalWidth + horizontalMargin.start + horizontalMargin.end);
+ m_lineBox.setIsConsideredNonEmpty();
+ m_trimmableContent.clear();
+ if (!layoutBox.establishesFormattingContext() || !boxGeometry.isEmpty())
+ m_lineBox.setIsConsideredNonEmpty();
+}
+
+void LineBuilder::appendReplacedInlineBox(const InlineItem& inlineItem, LayoutUnit logicalWidth)
+{
+ ASSERT(inlineItem.layoutBox().isReplaced());
+ // FIXME: Surely replaced boxes behave differently.
+ appendNonReplacedInlineBox(inlineItem, logicalWidth);
+ m_lineBox.setIsConsideredNonEmpty();
+}
+
+void LineBuilder::appendLineBreak(const InlineItem& inlineItem)
+{
+ m_lineBox.setIsConsideredNonEmpty();
+ m_inlineItemRuns.append(makeUnique<InlineItemRun>(inlineItem, Display::Rect { 0, contentLogicalWidth(), { }, { } }));
+}
+
+void LineBuilder::adjustBaselineAndLineHeight(const Run& run)
+{
+ auto& baseline = m_lineBox.baseline();
+ if (run.isText() || run.isForcedLineBreak()) {
+ // For text content we set the baseline either through the initial strut (set by the formatting context root) or
+ // through the inline container (start) -see above. Normally the text content itself does not stretch the line.
+ if (!m_initialStrut)
+ return;
+ m_lineBox.setAscentIfGreater(m_initialStrut->ascent());
+ m_lineBox.setDescentIfGreater(m_initialStrut->descent());
+ m_lineBox.setLogicalHeightIfGreater(baseline.height());
+ m_initialStrut = { };
+ return;
+ }
+
+ auto& layoutBox = run.layoutBox();
+ auto& style = layoutBox.style();
+ if (run.isContainerStart()) {
+ // Inline containers stretch the line by their font size.
+ // Vertical margins, paddings and borders don't contribute to the line height.
+ auto& fontMetrics = style.fontMetrics();
+ if (style.verticalAlign() == VerticalAlign::Baseline) {
+ auto halfLeading = halfLeadingMetrics(fontMetrics, style.computedLineHeight());
+ // Both halfleading ascent and descent could be negative (tall font vs. small line-height value)
+ if (halfLeading.descent() > 0)
+ m_lineBox.setDescentIfGreater(halfLeading.descent());
+ if (halfLeading.ascent() > 0)
+ m_lineBox.setAscentIfGreater(halfLeading.ascent());
+ m_lineBox.setLogicalHeightIfGreater(baseline.height());
+ } else
+ m_lineBox.setLogicalHeightIfGreater(fontMetrics.height());
+ return;
+ }
+
+ if (run.isContainerEnd()) {
+ // The line's baseline and height have already been adjusted at ContainerStart.
+ return;
+ }
+
+ if (run.isBox()) {
+ auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
+ auto marginBoxHeight = boxGeometry.marginBoxHeight();
+
+ switch (style.verticalAlign()) {
+ case VerticalAlign::Baseline: {
+ if (layoutBox.isInlineBlockBox() && layoutBox.establishesInlineFormattingContext()) {
+ // Inline-blocks with inline content always have baselines.
+ auto& formattingState = downcast<InlineFormattingState>(layoutState().establishedFormattingState(downcast<Container>(layoutBox)));
+ // Spec makes us generate at least one line -even if it is empty.
+ ASSERT(!formattingState.lineBoxes().isEmpty());
+ auto& lastLineBox = *formattingState.lineBoxes().last();
+ auto inlineBlockBaseline = lastLineBox.baseline();
+ auto beforeHeight = boxGeometry.marginBefore() + boxGeometry.borderTop() + boxGeometry.paddingTop().valueOr(0);
+
+ m_lineBox.setAscentIfGreater(inlineBlockBaseline.ascent());
+ m_lineBox.setDescentIfGreater(inlineBlockBaseline.descent());
+ m_lineBox.setBaselineOffsetIfGreater(beforeHeight + lastLineBox.baselineOffset());
+ m_lineBox.setLogicalHeightIfGreater(marginBoxHeight);
+ } else {
+ // Non inline-block boxes sit on the baseline (including their bottom margin).
+ m_lineBox.setAscentIfGreater(marginBoxHeight);
+ // Ignore negative descent (yes, negative descent is a thing).
+ m_lineBox.setLogicalHeightIfGreater(marginBoxHeight + std::max(LayoutUnit(), baseline.descent()));
+ }
+ break;
+ }
+ case VerticalAlign::Top:
+ // Top align content never changes the baseline, it only pushes the bottom of the line further down.
+ m_lineBox.setLogicalHeightIfGreater(marginBoxHeight);
+ break;
+ case VerticalAlign::Bottom: {
+ // Bottom aligned, tall content pushes the baseline further down from the line top.
+ auto lineLogicalHeight = m_lineBox.logicalHeight();
+ if (marginBoxHeight > lineLogicalHeight) {
+ m_lineBox.setLogicalHeightIfGreater(marginBoxHeight);
+ m_lineBox.setBaselineOffsetIfGreater(m_lineBox.baselineOffset() + (marginBoxHeight - lineLogicalHeight));
+ }
+ break;
+ }
+ default:
+ ASSERT_NOT_IMPLEMENTED_YET();
+ break;
+ }
+ return;
+ }
+ ASSERT_NOT_REACHED();
+}
+
+LayoutUnit LineBuilder::runContentHeight(const Run& run) const
+{
+ ASSERT(!m_skipAlignment);
+ auto& fontMetrics = run.layoutBox().style().fontMetrics();
+ if (run.isText() || run.isForcedLineBreak())
+ return fontMetrics.height();
+
+ if (run.isContainerStart() || run.isContainerEnd())
+ return fontMetrics.height();
+
+ auto& layoutBox = run.layoutBox();
+ auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
+ if (layoutBox.replaced() || layoutBox.isFloatingPositioned())
+ return boxGeometry.contentBoxHeight();
+
+ // Non-replaced inline box (e.g. inline-block). It looks a bit misleading but their margin box is considered the content height here.
+ return boxGeometry.marginBoxHeight();
+}
+
+void LineBuilder::TrimmableContent::append(InlineItemRun& inlineItemRun)
+{
+ ASSERT(inlineItemRun.logicalRect().width() >= 0);
+ m_width += inlineItemRun.logicalRect().width();
+ m_inlineItemRuns.append(&inlineItemRun);
+}
+
+LineBox::Baseline LineBuilder::halfLeadingMetrics(const FontMetrics& fontMetrics, LayoutUnit lineLogicalHeight)
+{
+ auto ascent = fontMetrics.ascent();
+ auto descent = fontMetrics.descent();
+ // 10.8.1 Leading and half-leading
+ auto leading = lineLogicalHeight - (ascent + descent);
+ // Inline tree is all integer based.
+ auto adjustedAscent = std::max((ascent + leading / 2).floor(), 0);
+ auto adjustedDescent = std::max((descent + leading / 2).ceil(), 0);
+ return { adjustedAscent, adjustedDescent };
+}
+
+LayoutState& LineBuilder::layoutState() const
+{
+ return formattingContext().layoutState();
+}
+
+const InlineFormattingContext& LineBuilder::formattingContext() const
+{
+ return m_inlineFormattingContext;
+}
+
+}
+}
+
+#endif
Copied: trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.h (from rev 252864, trunk/Source/WebCore/layout/inlineformatting/InlineLine.h) (0 => 252865)
--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.h (rev 0)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.h 2019-11-25 18:33:52 UTC (rev 252865)
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2019 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
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "DisplayRun.h"
+#include "InlineItem.h"
+#include "InlineLineBox.h"
+#include "InlineTextItem.h"
+
+namespace WebCore {
+namespace Layout {
+
+struct ContinousContent;
+class InlineFormattingContext;
+class InlineItemRun;
+
+class LineBuilder {
+public:
+ struct Constraints {
+ LayoutPoint logicalTopLeft;
+ LayoutUnit availableLogicalWidth;
+ bool lineIsConstrainedByFloat { false };
+ struct HeightAndBaseline {
+ LayoutUnit height;
+ LayoutUnit baselineOffset;
+ Optional<LineBox::Baseline> strut;
+ };
+ Optional<HeightAndBaseline> heightAndBaseline;
+ };
+ enum class SkipAlignment { No, Yes };
+ LineBuilder(const InlineFormattingContext&, Optional<TextAlignMode>, SkipAlignment);
+ ~LineBuilder();
+
+ void initialize(const Constraints&);
+ void append(const InlineItem&, LayoutUnit logicalWidth);
+ bool isVisuallyEmpty() const { return m_lineBox.isConsideredEmpty(); }
+ bool hasIntrusiveFloat() const { return m_hasIntrusiveFloat; }
+ LayoutUnit availableWidth() const { return logicalWidth() - contentLogicalWidth(); }
+
+ LayoutUnit trailingTrimmableWidth() const { return m_trimmableContent.width(); }
+
+ const LineBox& lineBox() const { return m_lineBox; }
+ void moveLogicalLeft(LayoutUnit);
+ void moveLogicalRight(LayoutUnit);
+ void setHasIntrusiveFloat() { m_hasIntrusiveFloat = true; }
+
+ struct Run {
+ Run(const InlineItemRun&);
+ Run(const InlineItemRun&, const Display::Rect&, const Display::Run::TextContext&, unsigned expansionOpportunityCount);
+ Run(Run&&) = default;
+ Run& operator=(Run&& other) = default;
+
+ bool isText() const { return m_type == InlineItem::Type::Text; }
+ bool isBox() const { return m_type == InlineItem::Type::Box; }
+ bool isForcedLineBreak() const { return m_type == InlineItem::Type::ForcedLineBreak; }
+ bool isContainerStart() const { return m_type == InlineItem::Type::ContainerStart; }
+ bool isContainerEnd() const { return m_type == InlineItem::Type::ContainerEnd; }
+
+ const Box& layoutBox() const { return *m_layoutBox; }
+ const Display::Rect& logicalRect() const { return m_logicalRect; }
+ Optional<Display::Run::TextContext> textContext() const { return m_textContext; }
+ bool isCollapsedToVisuallyEmpty() const { return m_isCollapsedToVisuallyEmpty; }
+
+ private:
+ friend class LineBuilder;
+
+ void adjustLogicalTop(LayoutUnit logicalTop) { m_logicalRect.setTop(logicalTop); }
+ void moveHorizontally(LayoutUnit offset) { m_logicalRect.moveHorizontally(offset); }
+ void moveVertically(LayoutUnit offset) { m_logicalRect.moveVertically(offset); }
+ void setLogicalHeight(LayoutUnit logicalHeight) { m_logicalRect.setHeight(logicalHeight); }
+
+ bool hasExpansionOpportunity() const { return m_expansionOpportunityCount; }
+ Optional<ExpansionBehavior> expansionBehavior() const;
+ unsigned expansionOpportunityCount() const { return m_expansionOpportunityCount; }
+ void setComputedHorizontalExpansion(LayoutUnit logicalExpansion);
+ void adjustExpansionBehavior(ExpansionBehavior);
+
+ const Box* m_layoutBox { nullptr };
+ InlineItem::Type m_type;
+ Display::Rect m_logicalRect;
+ Optional<Display::Run::TextContext> m_textContext;
+ unsigned m_expansionOpportunityCount { 0 };
+ bool m_isCollapsedToVisuallyEmpty { false };
+ };
+ using RunList = Vector<Run, 50>;
+ enum class IsLastLineWithInlineContent { No, Yes };
+ RunList close(IsLastLineWithInlineContent = IsLastLineWithInlineContent::No);
+
+ static LineBox::Baseline halfLeadingMetrics(const FontMetrics&, LayoutUnit lineLogicalHeight);
+
+private:
+ LayoutUnit logicalTop() const { return m_lineBox.logicalTop(); }
+ LayoutUnit logicalBottom() const { return m_lineBox.logicalBottom(); }
+
+ LayoutUnit logicalLeft() const { return m_lineBox.logicalLeft(); }
+ LayoutUnit logicalRight() const { return logicalLeft() + logicalWidth(); }
+
+ LayoutUnit logicalWidth() const { return m_lineLogicalWidth; }
+ LayoutUnit logicalHeight() const { return m_lineBox.logicalHeight(); }
+
+ LayoutUnit contentLogicalWidth() const { return m_lineBox.logicalWidth(); }
+ LayoutUnit contentLogicalRight() const { return m_lineBox.logicalRight(); }
+ LayoutUnit baselineOffset() const { return m_lineBox.baselineOffset(); }
+
+ void appendNonBreakableSpace(const InlineItem&, const Display::Rect& logicalRect);
+ void appendTextContent(const InlineTextItem&, LayoutUnit logicalWidth);
+ void appendNonReplacedInlineBox(const InlineItem&, LayoutUnit logicalWidth);
+ void appendReplacedInlineBox(const InlineItem&, LayoutUnit logicalWidth);
+ void appendInlineContainerStart(const InlineItem&, LayoutUnit logicalWidth);
+ void appendInlineContainerEnd(const InlineItem&, LayoutUnit logicalWidth);
+ void appendLineBreak(const InlineItem&);
+
+ void removeTrailingTrimmableContent();
+ void alignContentHorizontally(RunList&, IsLastLineWithInlineContent) const;
+ void alignContentVertically(RunList&);
+
+ void adjustBaselineAndLineHeight(const Run&);
+ LayoutUnit runContentHeight(const Run&) const;
+
+ bool isTextAlignJustify() const { return m_horizontalAlignment == TextAlignMode::Justify; };
+ void justifyRuns(RunList&) const;
+
+ LayoutState& layoutState() const;
+ const InlineFormattingContext& formattingContext() const;
+
+ const InlineFormattingContext& m_inlineFormattingContext;
+ Vector<std::unique_ptr<InlineItemRun>, 50> m_inlineItemRuns;
+ struct TrimmableContent {
+ void append(InlineItemRun&);
+ void clear();
+
+ LayoutUnit width() const { return m_width; }
+ using TrimmableList = Vector<InlineItemRun*, 5>;
+ TrimmableList& runs() { return m_inlineItemRuns; }
+ bool isEmpty() const { return m_inlineItemRuns.isEmpty(); }
+
+ private:
+ TrimmableList m_inlineItemRuns;
+ LayoutUnit m_width;
+ };
+ TrimmableContent m_trimmableContent;
+ Optional<LineBox::Baseline> m_initialStrut;
+ LayoutUnit m_lineLogicalWidth;
+ Optional<TextAlignMode> m_horizontalAlignment;
+ bool m_skipAlignment { false };
+ bool m_hasIntrusiveFloat { false };
+ LineBox m_lineBox;
+};
+
+inline void LineBuilder::TrimmableContent::clear()
+{
+ m_inlineItemRuns.clear();
+ m_width = { };
+}
+
+}
+}
+#endif
Modified: trunk/Source/WebCore/layout/inlineformatting/LineLayoutContext.cpp (252864 => 252865)
--- trunk/Source/WebCore/layout/inlineformatting/LineLayoutContext.cpp 2019-11-25 18:15:50 UTC (rev 252864)
+++ trunk/Source/WebCore/layout/inlineformatting/LineLayoutContext.cpp 2019-11-25 18:33:52 UTC (rev 252865)
@@ -85,7 +85,7 @@
{
}
-LineLayoutContext::LineContent LineLayoutContext::layoutLine(Line& line, unsigned leadingInlineItemIndex, Optional<PartialContent> leadingPartialContent)
+LineLayoutContext::LineContent LineLayoutContext::layoutLine(LineBuilder& line, unsigned leadingInlineItemIndex, Optional<PartialContent> leadingPartialContent)
{
auto initialize = [&] {
m_committedInlineItemCount = 0;
@@ -121,7 +121,7 @@
return close(line, leadingInlineItemIndex);
}
-void LineLayoutContext::commitPendingContent(Line& line)
+void LineLayoutContext::commitPendingContent(LineBuilder& line)
{
if (m_uncommittedContent.isEmpty())
return;
@@ -131,7 +131,7 @@
m_uncommittedContent.reset();
}
-LineLayoutContext::LineContent LineLayoutContext::close(Line& line, unsigned leadingInlineItemIndex)
+LineLayoutContext::LineContent LineLayoutContext::close(LineBuilder& line, unsigned leadingInlineItemIndex)
{
ASSERT(m_committedInlineItemCount || line.hasIntrusiveFloat());
m_uncommittedContent.reset();
@@ -145,21 +145,21 @@
auto isLastLineWithInlineContent = [&] {
if (overflowContent)
- return Line::IsLastLineWithInlineContent::No;
+ return LineBuilder::IsLastLineWithInlineContent::No;
// Skip floats backwards to see if this is going to be the last line with inline content.
for (auto i = m_inlineItems.size(); i--;) {
if (!m_inlineItems[i]->isFloat())
- return i == trailingInlineItemIndex ? Line::IsLastLineWithInlineContent::Yes : Line::IsLastLineWithInlineContent::No;
+ return i == trailingInlineItemIndex ? LineBuilder::IsLastLineWithInlineContent::Yes : LineBuilder::IsLastLineWithInlineContent::No;
}
// There has to be at least one non-float item.
ASSERT_NOT_REACHED();
- return Line::IsLastLineWithInlineContent::No;
+ return LineBuilder::IsLastLineWithInlineContent::No;
};
return LineContent { trailingInlineItemIndex, overflowContent, WTFMove(m_floats), line.close(isLastLineWithInlineContent()), line.lineBox() };
}
-LineLayoutContext::IsEndOfLine LineLayoutContext::placeInlineItem(Line& line, const InlineItem& inlineItem)
+LineLayoutContext::IsEndOfLine LineLayoutContext::placeInlineItem(LineBuilder& line, const InlineItem& inlineItem)
{
auto currentLogicalRight = line.lineBox().logicalRight();
auto itemLogicalWidth = inlineItemWidth(formattingContext(), inlineItem, currentLogicalRight);
@@ -207,7 +207,7 @@
return isEndOfLine;
}
-LineLayoutContext::IsEndOfLine LineLayoutContext::processUncommittedContent(Line& line)
+LineLayoutContext::IsEndOfLine LineLayoutContext::processUncommittedContent(LineBuilder& line)
{
// Check if the pending content fits.
auto lineIsConsideredEmpty = line.isVisuallyEmpty() && !line.hasIntrusiveFloat();
Modified: trunk/Source/WebCore/layout/inlineformatting/LineLayoutContext.h (252864 => 252865)
--- trunk/Source/WebCore/layout/inlineformatting/LineLayoutContext.h 2019-11-25 18:15:50 UTC (rev 252864)
+++ trunk/Source/WebCore/layout/inlineformatting/LineLayoutContext.h 2019-11-25 18:33:52 UTC (rev 252865)
@@ -27,7 +27,7 @@
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
-#include "InlineLine.h"
+#include "InlineLineBuilder.h"
namespace WebCore {
namespace Layout {
@@ -44,10 +44,10 @@
Optional<unsigned> trailingInlineItemIndex;
Optional<PartialContent> trailingPartialContent;
Vector<WeakPtr<InlineItem>> floats;
- const Line::RunList runList;
+ const LineBuilder::RunList runList;
const LineBox lineBox;
};
- LineContent layoutLine(Line&, unsigned leadingInlineItemIndex, Optional<PartialContent> leadingPartialContent);
+ LineContent layoutLine(LineBuilder&, unsigned leadingInlineItemIndex, Optional<PartialContent> leadingPartialContent);
struct Run {
const InlineItem& inlineItem;
@@ -59,11 +59,11 @@
private:
const InlineFormattingContext& formattingContext() const { return m_inlineFormattingContext; }
enum class IsEndOfLine { No, Yes };
- IsEndOfLine placeInlineItem(Line&, const InlineItem&);
- void commitPendingContent(Line&);
- LineContent close(Line&, unsigned leadingInlineItemIndex);
+ IsEndOfLine placeInlineItem(LineBuilder&, const InlineItem&);
+ void commitPendingContent(LineBuilder&);
+ LineContent close(LineBuilder&, unsigned leadingInlineItemIndex);
bool shouldProcessUncommittedContent(const InlineItem&) const;
- IsEndOfLine processUncommittedContent(Line&);
+ IsEndOfLine processUncommittedContent(LineBuilder&);
struct UncommittedContent {
void add(const InlineItem&, LayoutUnit logicalWidth);