Diff
Modified: trunk/Source/WebCore/ChangeLog (251916 => 251917)
--- trunk/Source/WebCore/ChangeLog 2019-11-01 15:20:19 UTC (rev 251916)
+++ trunk/Source/WebCore/ChangeLog 2019-11-01 15:35:43 UTC (rev 251917)
@@ -1,3 +1,33 @@
+2019-11-01 Zalan Bujtas <za...@apple.com>
+
+ [LFC][IFC] Add support for text-align: justify
+ https://bugs.webkit.org/show_bug.cgi?id=203715
+ <rdar://problem/56804607>
+
+ Reviewed by Antti Koivisto.
+
+ Add basic support for text-align: justify. Register expansion opportunities as the text content is being
+ appended to the line (Line::appendTextContent) and distribute the extra space when the line is being closed (Line::close).
+ Now LFC rendering of OpenSource/PerformanceTests/Layout/line-layout-simple.html visually matches trunk rendering.
+
+ * layout/displaytree/DisplayPainter.cpp:
+ (WebCore::Display::paintInlineContent):
+ * layout/displaytree/DisplayRun.h:
+ (WebCore::Display::Run::TextContext::resetExpansion):
+ * layout/inlineformatting/InlineLine.cpp:
+ (WebCore::Layout::Line::Run::expand):
+ (WebCore::Layout::Line::close):
+ (WebCore::Layout::Line::justifyRuns):
+ (WebCore::Layout::Line::alignContentHorizontally):
+ (WebCore::Layout::Line::appendTextContent):
+ * layout/inlineformatting/InlineLine.h:
+ (WebCore::Layout::Line::isTextAlignJustify const):
+ (WebCore::Layout::Line::Run::setCollapsesToZeroAdvanceWidth):
+ (WebCore::Layout::Line::Run::adjustExpansionBehavior):
+ (WebCore::Layout::Line::Run::expand): Deleted.
+ * layout/inlineformatting/InlineLineLayout.cpp:
+ (WebCore::Layout::LineLayout::close):
+
2019-11-01 Antti Koivisto <an...@apple.com>
Style::Builder should not depend on StyleResolver
Modified: trunk/Source/WebCore/layout/displaytree/DisplayPainter.cpp (251916 => 251917)
--- trunk/Source/WebCore/layout/displaytree/DisplayPainter.cpp 2019-11-01 15:20:19 UTC (rev 251916)
+++ trunk/Source/WebCore/layout/displaytree/DisplayPainter.cpp 2019-11-01 15:35:43 UTC (rev 251917)
@@ -127,7 +127,7 @@
auto& lineBox = formattingState.lineBoxForRun(*run);
auto baselineOffset = rootAbsoluteDisplayBox.top() + lineBox.logicalTop() + lineBox.baselineOffset();
if (auto expansionContext = textContext->expansion())
- context.drawText(style.fontCascade(), TextRun { textContext->content(), logicalLeft, expansionContext->horiztontalExpansion, expansionContext->behavior }, { logicalLeft, baselineOffset });
+ context.drawText(style.fontCascade(), TextRun { textContext->content(), logicalLeft, expansionContext->horizontalExpansion, expansionContext->behavior }, { logicalLeft, baselineOffset });
else
context.drawText(style.fontCascade(), TextRun { textContext->content(), logicalLeft }, { logicalLeft, baselineOffset });
} else if (auto* cachedImage = run->image()) {
Modified: trunk/Source/WebCore/layout/displaytree/DisplayRun.h (251916 => 251917)
--- trunk/Source/WebCore/layout/displaytree/DisplayRun.h 2019-11-01 15:20:19 UTC (rev 251916)
+++ trunk/Source/WebCore/layout/displaytree/DisplayRun.h 2019-11-01 15:35:43 UTC (rev 251917)
@@ -53,9 +53,10 @@
struct ExpansionContext {
ExpansionBehavior behavior;
- LayoutUnit horiztontalExpansion;
+ LayoutUnit horizontalExpansion;
};
void setExpansion(ExpansionContext expansionContext) { m_expansionContext = expansionContext; }
+ void resetExpansion() { m_expansionContext = WTF::nullopt; }
Optional<ExpansionContext> expansion() const { return m_expansionContext; }
void expand(const TextContext& other);
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp (251916 => 251917)
--- trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp 2019-11-01 15:20:19 UTC (rev 251916)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp 2019-11-01 15:35:43 UTC (rev 251917)
@@ -46,6 +46,35 @@
{
}
+void Line::Run::expand(const Run& other)
+{
+ ASSERT(isText());
+ ASSERT(other.isText());
+ ASSERT(!isCollapsedToZeroAdvanceWidth());
+ ASSERT(!hasTrailingCollapsedContent());
+
+ auto& otherDisplayRun = other.displayRun();
+ m_displayRun.expandHorizontally(otherDisplayRun.logicalWidth());
+ m_displayRun.textContext()->expand(*otherDisplayRun.textContext());
+ m_hasTrailingCollapsedContent = other.isCollapsed();
+ m_isWhitespace &= other.isWhitespace();
+ m_isCollapsible = false;
+
+ // FIXME: This is a very simple expansion merge. We should eventually switch over to FontCascade::expansionOpportunityCount.
+ if (hasExpansionOpportunity() && other.hasExpansionOpportunity()) {
+ // Run is expanded with a whitespace content.
+ adjustExpansionBehavior(ForbidLeadingExpansion | AllowTrailingExpansion);
+ m_expansionOpportunityCount = *m_expansionOpportunityCount + *other.expansionOpportunityCount();
+ } else if (!hasExpansionOpportunity() && other.hasExpansionOpportunity()) {
+ // Nonwhitespace runs is expanded with whitespace.
+ setHasExpansionOpportunity(ForbidLeadingExpansion | AllowTrailingExpansion);
+ m_expansionOpportunityCount = other.expansionOpportunityCount();
+ } else if (hasExpansionOpportunity() && !other.hasExpansionOpportunity()) {
+ // Run is expanded with a nonwhitespace content.
+ adjustExpansionBehavior(AllowLeadingExpansion | AllowTrailingExpansion);
+ }
+}
+
Line::Line(const InlineFormattingContext& inlineFormattingContext, const InitialConstraints& initialConstraints, Optional<TextAlignMode> horizontalAlignment, SkipAlignment skipAlignment)
: m_inlineFormattingContext(inlineFormattingContext)
, m_initialStrut(initialConstraints.heightAndBaseline ? initialConstraints.heightAndBaseline->strut : WTF::nullopt)
@@ -112,7 +141,7 @@
return true;
}
-Line::RunList Line::close()
+Line::RunList Line::close(IsLastLineWithInlineContent isLastLineWithInlineContent)
{
removeTrailingTrimmableContent();
// Join text runs together when possible.
@@ -145,7 +174,7 @@
if (!m_skipAlignment) {
alignContentVertically();
- alignContentHorizontally();
+ alignContentHorizontally(isLastLineWithInlineContent);
}
return WTFMove(m_runList);
@@ -223,10 +252,53 @@
}
}
-void Line::alignContentHorizontally()
+void Line::justifyRuns()
{
+ ASSERT(!m_runList.isEmpty());
+ ASSERT(availableWidth() > 0);
+ // Need to fix up the last run first.
+ auto& lastRun = m_runList.last();
+ if (lastRun->hasExpansionOpportunity())
+ lastRun->adjustExpansionBehavior(*lastRun->expansionBehavior() | ForbidTrailingExpansion);
+ // Collect the expansion opportunity numbers.
+ auto expansionOpportunityCount = 0;
+ for (auto& run : m_runList) {
+ if (!run->hasExpansionOpportunity())
+ continue;
+ expansionOpportunityCount += *run->expansionOpportunityCount();
+ }
+ // Nothing to distribute?
+ if (!expansionOpportunityCount)
+ return;
+ // Distribute the extra space.
+ auto expansionToDistribute = availableWidth() / expansionOpportunityCount;
+ LayoutUnit accumulatedExpansion;
+ for (auto& run : m_runList) {
+ // Expand and moves runs by the accumulated expansion.
+ if (!run->hasExpansionOpportunity()) {
+ run->moveHorizontally(accumulatedExpansion);
+ continue;
+ }
+ auto computedExpansion = expansionToDistribute * *run->expansionOpportunityCount();
+ run->setComputedHorizontalExpansion(computedExpansion);
+ run->moveHorizontally(accumulatedExpansion);
+ accumulatedExpansion += computedExpansion;
+ }
+}
+
+void Line::alignContentHorizontally(IsLastLineWithInlineContent lastLine)
+{
ASSERT(!m_skipAlignment);
+ if (m_runList.isEmpty() || availableWidth() <= 0)
+ return;
+ if (isTextAlignJustify()) {
+ // Do not justify align the last line.
+ if (lastLine == IsLastLineWithInlineContent::No)
+ justifyRuns();
+ return;
+ }
+
auto adjustmentForAlignment = [&]() -> Optional<LayoutUnit> {
switch (*m_horizontalAlignment) {
case TextAlignMode::Left:
@@ -382,6 +454,12 @@
auto textContent = inlineItem.layoutBox().textContent().substring(contentStart, contentLength);
auto lineRun = makeUnique<Run>(inlineItem, Display::Run { inlineItem.style(), logicalRect, Display::Run::TextContext { contentStart, contentLength, textContent } });
+ if (isTextAlignJustify()) {
+ // Register expansion opportunity for whitespace runs.
+ if (inlineItem.isWhitespace())
+ lineRun->setHasExpansionOpportunity(DefaultExpansion);
+ }
+
auto collapsesToZeroAdvanceWidth = willCollapseCompletely();
if (collapsesToZeroAdvanceWidth)
lineRun->setCollapsesToZeroAdvanceWidth();
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLine.h (251916 => 251917)
--- trunk/Source/WebCore/layout/inlineformatting/InlineLine.h 2019-11-01 15:20:19 UTC (rev 251916)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLine.h 2019-11-01 15:35:43 UTC (rev 251917)
@@ -97,6 +97,7 @@
bool hasExpansionOpportunity() const { return m_expansionOpportunityCount.hasValue(); }
Optional<ExpansionBehavior> expansionBehavior() const;
Optional<unsigned> expansionOpportunityCount() const { return m_expansionOpportunityCount; }
+ void adjustExpansionBehavior(ExpansionBehavior);
void setComputedHorizontalExpansion(LayoutUnit logicalExpansion);
bool isCollapsible() const { return m_isCollapsible; }
@@ -114,7 +115,8 @@
Optional<unsigned> m_expansionOpportunityCount;
};
using RunList = Vector<std::unique_ptr<Run>>;
- RunList close();
+ enum class IsLastLineWithInlineContent { No, Yes };
+ RunList close(IsLastLineWithInlineContent = IsLastLineWithInlineContent::No);
static LineBox::Baseline halfLeadingMetrics(const FontMetrics&, LayoutUnit lineLogicalHeight);
@@ -141,7 +143,7 @@
void appendLineBreak(const InlineItem&);
void removeTrailingTrimmableContent();
- void alignContentHorizontally();
+ void alignContentHorizontally(IsLastLineWithInlineContent);
void alignContentVertically();
void adjustBaselineAndLineHeight(const InlineItem&);
@@ -148,6 +150,9 @@
LayoutUnit inlineItemContentHeight(const InlineItem&) const;
bool isVisuallyEmpty() const;
+ bool isTextAlignJustify() const { return m_horizontalAlignment == TextAlignMode::Justify; };
+ void justifyRuns();
+
LayoutState& layoutState() const;
const InlineFormattingContext& formattingContext() const;
@@ -162,21 +167,6 @@
LineBox m_lineBox;
};
-inline void Line::Run::expand(const Run& other)
-{
- ASSERT(isText());
- ASSERT(other.isText());
- ASSERT(!isCollapsedToZeroAdvanceWidth());
- ASSERT(!hasTrailingCollapsedContent());
-
- auto& otherDisplayRun = other.displayRun();
- m_displayRun.expandHorizontally(otherDisplayRun.logicalWidth());
- m_displayRun.textContext()->expand(*otherDisplayRun.textContext());
- m_hasTrailingCollapsedContent = other.isCollapsed();
- m_isWhitespace &= other.isWhitespace();
- m_isCollapsible = false;
-}
-
inline void Line::Run::setIsCollapsed()
{
ASSERT(isWhitespace());
@@ -195,6 +185,8 @@
setIsCollapsed();
m_collapsedToZeroAdvanceWidth = true;
m_displayRun.setLogicalWidth({ });
+ m_expansionOpportunityCount = { };
+ m_displayRun.textContext()->resetExpansion();
}
inline Optional<ExpansionBehavior> Line::Run::expansionBehavior() const
@@ -213,6 +205,13 @@
m_displayRun.textContext()->setExpansion({ expansionBehavior, { } });
}
+inline void Line::Run::adjustExpansionBehavior(ExpansionBehavior expansionBehavior)
+{
+ ASSERT(isText());
+ ASSERT(hasExpansionOpportunity());
+ m_displayRun.textContext()->setExpansion({ expansionBehavior, m_displayRun.textContext()->expansion()->horizontalExpansion });
+}
+
inline void Line::Run::setComputedHorizontalExpansion(LayoutUnit logicalExpansion)
{
ASSERT(isText());
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineLayout.cpp (251916 => 251917)
--- trunk/Source/WebCore/layout/inlineformatting/InlineLineLayout.cpp 2019-11-01 15:20:19 UTC (rev 251916)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineLayout.cpp 2019-11-01 15:35:43 UTC (rev 251917)
@@ -143,7 +143,21 @@
if (m_overflowTextLength)
overflowContent = PartialContent { *m_overflowTextLength };
auto trailingInlineItemIndex = m_lineInput.leadingInlineItemIndex + m_committedInlineItemCount - 1;
- return LineContent { trailingInlineItemIndex, overflowContent, WTFMove(m_floats), m_line.close(), m_line.lineBox() };
+
+ auto isLastLineWithInlineContent = [&] {
+ if (overflowContent)
+ return Line::IsLastLineWithInlineContent::No;
+ // Skip floats backwards to see if this is going to be the last line with inline content.
+ for (auto i = m_lineInput.inlineItems.size(); i--;) {
+ if (!m_lineInput.inlineItems[i]->isFloat())
+ return i == trailingInlineItemIndex ? Line::IsLastLineWithInlineContent::Yes : Line::IsLastLineWithInlineContent::No;
+ }
+ // There has to be at least one non-float item.
+ ASSERT_NOT_REACHED();
+ return Line::IsLastLineWithInlineContent::No;
+ };
+
+ return LineContent { trailingInlineItemIndex, overflowContent, WTFMove(m_floats), m_line.close(isLastLineWithInlineContent()), m_line.lineBox() };
}
LineLayout::IsEndOfLine LineLayout::placeInlineItem(const InlineItem& inlineItem)