[LFC][IFC] Paint partial trailing run with hyphen when needed
https://bugs.webkit.org/show_bug.cgi?id=204953
<rdar://problem/57705169>
Reviewed by Antti Koivisto.
When LineBreaker comes back with a partial content that needs hyphen, we need to make sure this information
ends up in the final Display::Run so that the rendered content includes the hyphen string. Note that this only needs to
be done when the content does _not_ have the hyphen already (opportunity vs. oppor-tunity).
(This patch also renames trailingPartial to partialTrailing because the fact that it is partial run is more important than that it is trailing run.)
* layout/displaytree/DisplayRun.h:
(WebCore::Display::Run::TextContext::TextContext):
(WebCore::Display::Run::TextContext::needsHyphen const):
(WebCore::Display::Run::TextContext::setNeedsHyphen):
(WebCore::Display::Run::textContext):
* layout/inlineformatting/InlineFormattingContext.cpp:
(WebCore::Layout::InlineFormattingContext::lineLayout):
(WebCore::Layout::InlineFormattingContext::setDisplayBoxesForLine):
* layout/inlineformatting/InlineLineBreaker.cpp:
(WebCore::Layout::LineBreaker::breakingContextForInlineContent):
(WebCore::Layout::LineBreaker::wordBreakingBehavior const):
* layout/inlineformatting/InlineLineBreaker.h:
* layout/inlineformatting/LineLayoutContext.cpp:
(WebCore::Layout::LineLayoutContext::layoutLine):
(WebCore::Layout::LineLayoutContext::close):
(WebCore::Layout::LineLayoutContext::processUncommittedContent):
* layout/inlineformatting/LineLayoutContext.h:
* layout/integration/LayoutIntegrationLineLayout.cpp:
(WebCore::LayoutIntegration::LineLayout::paint):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@253214 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 3be3c29..513754a 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,36 @@
+2019-12-06 Zalan Bujtas <zalan@apple.com>
+
+ [LFC][IFC] Paint partial trailing run with hyphen when needed
+ https://bugs.webkit.org/show_bug.cgi?id=204953
+ <rdar://problem/57705169>
+
+ Reviewed by Antti Koivisto.
+
+ When LineBreaker comes back with a partial content that needs hyphen, we need to make sure this information
+ ends up in the final Display::Run so that the rendered content includes the hyphen string. Note that this only needs to
+ be done when the content does _not_ have the hyphen already (opportunity vs. oppor-tunity).
+ (This patch also renames trailingPartial to partialTrailing because the fact that it is partial run is more important than that it is trailing run.)
+
+ * layout/displaytree/DisplayRun.h:
+ (WebCore::Display::Run::TextContext::TextContext):
+ (WebCore::Display::Run::TextContext::needsHyphen const):
+ (WebCore::Display::Run::TextContext::setNeedsHyphen):
+ (WebCore::Display::Run::textContext):
+ * layout/inlineformatting/InlineFormattingContext.cpp:
+ (WebCore::Layout::InlineFormattingContext::lineLayout):
+ (WebCore::Layout::InlineFormattingContext::setDisplayBoxesForLine):
+ * layout/inlineformatting/InlineLineBreaker.cpp:
+ (WebCore::Layout::LineBreaker::breakingContextForInlineContent):
+ (WebCore::Layout::LineBreaker::wordBreakingBehavior const):
+ * layout/inlineformatting/InlineLineBreaker.h:
+ * layout/inlineformatting/LineLayoutContext.cpp:
+ (WebCore::Layout::LineLayoutContext::layoutLine):
+ (WebCore::Layout::LineLayoutContext::close):
+ (WebCore::Layout::LineLayoutContext::processUncommittedContent):
+ * layout/inlineformatting/LineLayoutContext.h:
+ * layout/integration/LayoutIntegrationLineLayout.cpp:
+ (WebCore::LayoutIntegration::LineLayout::paint):
+
2019-12-06 Chris Dumez <cdumez@apple.com>
Prevent synchronous XHR in beforeunload / unload event handlers
diff --git a/Source/WebCore/layout/displaytree/DisplayRun.h b/Source/WebCore/layout/displaytree/DisplayRun.h
index 4488118..41db175 100644
--- a/Source/WebCore/layout/displaytree/DisplayRun.h
+++ b/Source/WebCore/layout/displaytree/DisplayRun.h
@@ -45,7 +45,7 @@
WTF_MAKE_STRUCT_FAST_ALLOCATED;
public:
struct ExpansionContext;
- TextContext(unsigned position, unsigned length, const String&, Optional<ExpansionContext> = { });
+ TextContext(unsigned position, unsigned length, const String&);
unsigned start() const { return m_start; }
unsigned end() const { return start() + length(); }
@@ -59,11 +59,15 @@
void setExpansion(ExpansionContext expansionContext) { m_expansionContext = expansionContext; }
Optional<ExpansionContext> expansion() const { return m_expansionContext; }
+ bool needsHyphen() const { return m_needsHyphen; }
+
void expand(unsigned expandedLength);
+ void setNeedsHyphen() { m_needsHyphen = true; }
private:
unsigned m_start { 0 };
unsigned m_length { 0 };
+ bool m_needsHyphen { false };
String m_contentString;
Optional<ExpansionContext> m_expansionContext;
};
@@ -94,6 +98,7 @@
void setTextContext(const TextContext&& textContext) { m_textContext.emplace(textContext); }
const Optional<TextContext>& textContext() const { return m_textContext; }
+ Optional<TextContext>& textContext() { return m_textContext; }
void setImage(CachedImage& image) { m_cachedImage = ℑ }
CachedImage* image() const { return m_cachedImage; }
@@ -120,11 +125,10 @@
{
}
-inline Run::TextContext::TextContext(unsigned start, unsigned length, const String& contentString, Optional<ExpansionContext> expansionContext)
+inline Run::TextContext::TextContext(unsigned start, unsigned length, const String& contentString)
: m_start(start)
, m_length(length)
, m_contentString(contentString)
- , m_expansionContext(expansionContext)
{
}
diff --git a/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp b/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp
index 621fb8b..b723484 100644
--- a/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp
+++ b/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp
@@ -94,24 +94,24 @@
auto& inlineItems = formattingState().inlineItems();
auto lineLogicalTop = geometryForBox(root()).contentBoxTop();
unsigned leadingInlineItemIndex = 0;
- Optional<LineLayoutContext::PartialContent> leadingPartialContent;
+ Optional<unsigned> partialLeadingContentLength;
auto lineBuilder = LineBuilder { *this, root().style().textAlign(), LineBuilder::SkipAlignment::No };
auto lineLayoutContext = LineLayoutContext { *this, root(), inlineItems };
while (leadingInlineItemIndex < inlineItems.size()) {
lineBuilder.initialize(constraintsForLine(usedHorizontalValues, lineLogicalTop));
- auto lineContent = lineLayoutContext.layoutLine(lineBuilder, leadingInlineItemIndex, leadingPartialContent);
+ auto lineContent = lineLayoutContext.layoutLine(lineBuilder, leadingInlineItemIndex, partialLeadingContentLength);
setDisplayBoxesForLine(lineContent, usedHorizontalValues);
- leadingPartialContent = { };
+ partialLeadingContentLength = { };
if (lineContent.trailingInlineItemIndex) {
lineLogicalTop = lineContent.lineBox.logicalBottom();
// When the trailing content is partial, we need to reuse the last InlinItem.
- if (lineContent.overflowPartialContent) {
+ if (lineContent.partialContent) {
leadingInlineItemIndex = *lineContent.trailingInlineItemIndex;
// Turn previous line's overflow content length into the next line's leading content partial length.
// "sp<->litcontent" -> overflow length: 10 -> leading partial content length: 10.
- leadingPartialContent = LineLayoutContext::PartialContent { lineContent.overflowPartialContent->length };
+ partialLeadingContentLength = lineContent.partialContent->overlfowContentLength;
} else
leadingInlineItemIndex = *lineContent.trailingInlineItemIndex + 1;
} else {
@@ -427,10 +427,9 @@
}
auto& inlineContent = formattingState.ensureDisplayInlineContent();
-
auto lineIndex = inlineContent.lineBoxes.size();
inlineContent.lineBoxes.append(LineBox { lineContent.lineBox });
-
+ Optional<unsigned> lastTextItemIndex;
// Compute box final geometry.
auto& lineRuns = lineContent.runList;
for (unsigned index = 0; index < lineRuns.size(); ++index) {
@@ -483,6 +482,7 @@
}
if (lineRun.isText()) {
+ lastTextItemIndex = inlineContent.runs.size() - 1;
auto firstRunForLayoutBox = !index || &lineRuns[index - 1].layoutBox() != &layoutBox;
if (firstRunForLayoutBox) {
// Setup display box for the associated layout box.
@@ -497,6 +497,9 @@
}
ASSERT_NOT_REACHED();
}
+ // Make sure the trailing text run gets a hyphen when it needs one.
+ if (lineContent.partialContent && lineContent.partialContent->trailingContentNeedsHyphen)
+ inlineContent.runs[*lastTextItemIndex].textContext()->setNeedsHyphen();
}
void InlineFormattingContext::invalidateFormattingState(const InvalidationState&)
diff --git a/Source/WebCore/layout/inlineformatting/InlineLineBreaker.cpp b/Source/WebCore/layout/inlineformatting/InlineLineBreaker.cpp
index 57ebafb..c84d1c1 100644
--- a/Source/WebCore/layout/inlineformatting/InlineLineBreaker.cpp
+++ b/Source/WebCore/layout/inlineformatting/InlineLineBreaker.cpp
@@ -72,8 +72,8 @@
if (candidateRuns.hasTextContentOnly()) {
auto& runs = candidateRuns.runs();
- if (auto trailingPartialContent = wordBreakingBehavior(runs, lineStatus.availableWidth))
- return { BreakingContext::ContentBreak::Split, trailingPartialContent };
+ if (auto partialTrailingContent = wordBreakingBehavior(runs, lineStatus.availableWidth))
+ return { BreakingContext::ContentBreak::Split, partialTrailingContent };
// If we did not manage to break this content, we still need to decide whether keep it or wrap it to the next line.
// FIXME: Keep tracking the last breaking opportunity where we can wrap the content:
// <span style="white-space: pre;">this fits</span> <span style="white-space: pre;">this does not fit but does not wrap either</span>
@@ -95,7 +95,7 @@
return !lineIsEmpty && floatLogicalWidth > availableWidth;
}
-Optional<LineBreaker::BreakingContext::TrailingPartialContent> LineBreaker::wordBreakingBehavior(const Content::RunList& runs, LayoutUnit availableWidth) const
+Optional<LineBreaker::BreakingContext::PartialTrailingContent> LineBreaker::wordBreakingBehavior(const Content::RunList& runs, LayoutUnit availableWidth) const
{
// Check where the overflow occurs and use the corresponding style to figure out the breaking behaviour.
// <span style="word-break: normal">first</span><span style="word-break: break-all">second</span><span style="word-break: normal">third</span>
@@ -122,7 +122,7 @@
// When the first span computes longer than the available space, by the time we get to the second span, the adjusted available space becomes negative.
auto adjustedAvailableWidth = std::max(LayoutUnit { }, availableWidth - runsWidth + run.logicalWidth);
if (auto leftSide = tryBreakingTextRun(run, adjustedAvailableWidth))
- return BreakingContext::TrailingPartialContent { i, leftSide->length, leftSide->logicalWidth, leftSide->hasHyphen };
+ return BreakingContext::PartialTrailingContent { i, leftSide->length, leftSide->logicalWidth, leftSide->needsHyphen };
return { };
}
// We did not manage to break in this sequence of runs.
diff --git a/Source/WebCore/layout/inlineformatting/InlineLineBreaker.h b/Source/WebCore/layout/inlineformatting/InlineLineBreaker.h
index 852ac0c..0d523ae 100644
--- a/Source/WebCore/layout/inlineformatting/InlineLineBreaker.h
+++ b/Source/WebCore/layout/inlineformatting/InlineLineBreaker.h
@@ -40,13 +40,13 @@
struct BreakingContext {
enum class ContentBreak { Keep, Split, Wrap };
ContentBreak contentBreak;
- struct TrailingPartialContent {
+ struct PartialTrailingContent {
unsigned runIndex { 0 };
unsigned length { 0 };
LayoutUnit logicalWidth;
- bool hasHyphen { false };
+ bool needsHyphen { false };
};
- Optional<TrailingPartialContent> trailingPartialContent;
+ Optional<PartialTrailingContent> partialTrailingContent;
};
// This struct represents the amount of content committed to line breaking at a time e.g.
@@ -107,11 +107,11 @@
void setHyphenationDisabled() { n_hyphenationIsDisabled = true; }
private:
- Optional<BreakingContext::TrailingPartialContent> wordBreakingBehavior(const Content::RunList&, LayoutUnit availableWidth) const;
+ Optional<BreakingContext::PartialTrailingContent> wordBreakingBehavior(const Content::RunList&, LayoutUnit availableWidth) const;
struct LeftSide {
unsigned length { 0 };
LayoutUnit logicalWidth;
- bool hasHyphen { false };
+ bool needsHyphen { false };
};
Optional<LeftSide> tryBreakingTextRun(const Content::Run& overflowRun, LayoutUnit availableWidth) const;
diff --git a/Source/WebCore/layout/inlineformatting/LineLayoutContext.cpp b/Source/WebCore/layout/inlineformatting/LineLayoutContext.cpp
index 22ad074..3d451ab 100644
--- a/Source/WebCore/layout/inlineformatting/LineLayoutContext.cpp
+++ b/Source/WebCore/layout/inlineformatting/LineLayoutContext.cpp
@@ -77,27 +77,27 @@
{
}
-LineLayoutContext::LineContent LineLayoutContext::layoutLine(LineBuilder& line, unsigned leadingInlineItemIndex, Optional<PartialContent> leadingPartialContent)
+LineLayoutContext::LineContent LineLayoutContext::layoutLine(LineBuilder& line, unsigned leadingInlineItemIndex, Optional<unsigned> partialLeadingContentLength)
{
auto initialize = [&] {
m_committedInlineItemCount = 0;
m_uncommittedContent.reset();
- m_leadingPartialTextItem = { };
- m_trailingPartialTextItem = { };
- m_overflowPartialContent = { };
+ m_partialLeadingTextItem = { };
+ m_partialTrailingTextItem = { };
+ m_partialContent = { };
};
initialize();
// Iterate through the inline content and place the inline boxes on the current line.
// Start with the partial leading text from the previous line.
auto firstNonPartialInlineItemIndex = leadingInlineItemIndex;
- if (leadingPartialContent) {
+ if (partialLeadingContentLength) {
// Handle partial inline item (split text from the previous line).
auto& leadingTextItem = m_inlineItems[leadingInlineItemIndex];
RELEASE_ASSERT(leadingTextItem->isText());
// Construct a partial leading inline item.
- ASSERT(!m_leadingPartialTextItem);
- m_leadingPartialTextItem = downcast<InlineTextItem>(*leadingTextItem).right(leadingPartialContent->length);
- if (placeInlineItem(line, *m_leadingPartialTextItem) == IsEndOfLine::Yes)
+ ASSERT(!m_partialLeadingTextItem);
+ m_partialLeadingTextItem = downcast<InlineTextItem>(*leadingTextItem).right(*partialLeadingContentLength);
+ if (placeInlineItem(line, *m_partialLeadingTextItem) == IsEndOfLine::Yes)
return close(line, leadingInlineItemIndex);
++firstNonPartialInlineItemIndex;
}
@@ -132,7 +132,7 @@
auto trailingInlineItemIndex = leadingInlineItemIndex + m_committedInlineItemCount - 1;
auto isLastLineWithInlineContent = [&] {
- if (m_overflowPartialContent)
+ if (m_partialContent)
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--;) {
@@ -144,7 +144,7 @@
return LineBuilder::IsLastLineWithInlineContent::No;
};
- return LineContent { trailingInlineItemIndex, m_overflowPartialContent, WTFMove(m_floats), line.close(isLastLineWithInlineContent()), line.lineBox() };
+ return LineContent { trailingInlineItemIndex, m_partialContent, WTFMove(m_floats), line.close(isLastLineWithInlineContent()), line.lineBox() };
}
LineLayoutContext::IsEndOfLine LineLayoutContext::placeInlineItem(LineBuilder& line, const InlineItem& inlineItem)
@@ -215,27 +215,27 @@
if (breakingContext.contentBreak == LineBreaker::BreakingContext::ContentBreak::Keep)
commitPendingContent(line);
else if (breakingContext.contentBreak == LineBreaker::BreakingContext::ContentBreak::Split) {
- ASSERT(breakingContext.trailingPartialContent);
- ASSERT(m_uncommittedContent.runs()[breakingContext.trailingPartialContent->runIndex].inlineItem.isText());
+ ASSERT(breakingContext.partialTrailingContent);
+ ASSERT(m_uncommittedContent.runs()[breakingContext.partialTrailingContent->runIndex].inlineItem.isText());
// Turn the uncommitted trailing run into a partial trailing run.
- auto overflowInlineTextItemIndex = breakingContext.trailingPartialContent->runIndex;
+ auto overflowInlineTextItemIndex = breakingContext.partialTrailingContent->runIndex;
auto& overflowInlineTextItem = downcast<InlineTextItem>(m_uncommittedContent.runs()[overflowInlineTextItemIndex].inlineItem);
// Construct a partial trailing inline run.
- ASSERT(!m_trailingPartialTextItem);
- auto trailingContentLength = breakingContext.trailingPartialContent->length;
- m_trailingPartialTextItem = overflowInlineTextItem.left(trailingContentLength);
- m_overflowPartialContent = PartialContent { overflowInlineTextItem.length() - trailingContentLength };
+ ASSERT(!m_partialTrailingTextItem);
+ auto trailingContentLength = breakingContext.partialTrailingContent->length;
+ m_partialTrailingTextItem = overflowInlineTextItem.left(trailingContentLength);
+ m_partialContent = LineContent::PartialContent { breakingContext.partialTrailingContent->needsHyphen, overflowInlineTextItem.length() - trailingContentLength };
// Keep the non-overflow part of the uncommitted runs and add the trailing partial content.
m_uncommittedContent.trim(overflowInlineTextItemIndex);
- m_uncommittedContent.append(*m_trailingPartialTextItem, breakingContext.trailingPartialContent->logicalWidth);
+ m_uncommittedContent.append(*m_partialTrailingTextItem, breakingContext.partialTrailingContent->logicalWidth);
commitPendingContent(line);
} else if (breakingContext.contentBreak == LineBreaker::BreakingContext::ContentBreak::Wrap)
m_uncommittedContent.reset();
else
ASSERT_NOT_REACHED();
// Adjust hyphenated line count
- m_successiveHyphenatedLineCount = breakingContext.trailingPartialContent && breakingContext.trailingPartialContent->hasHyphen ? m_successiveHyphenatedLineCount + 1 : 0;
+ m_successiveHyphenatedLineCount = breakingContext.partialTrailingContent && breakingContext.partialTrailingContent->needsHyphen ? m_successiveHyphenatedLineCount + 1 : 0;
return breakingContext.contentBreak == LineBreaker::BreakingContext::ContentBreak::Keep ? IsEndOfLine::No :IsEndOfLine::Yes;
}
diff --git a/Source/WebCore/layout/inlineformatting/LineLayoutContext.h b/Source/WebCore/layout/inlineformatting/LineLayoutContext.h
index 62a5139..0fb80b6 100644
--- a/Source/WebCore/layout/inlineformatting/LineLayoutContext.h
+++ b/Source/WebCore/layout/inlineformatting/LineLayoutContext.h
@@ -37,18 +37,18 @@
public:
LineLayoutContext(const InlineFormattingContext&, const Container& formattingContextRoot, const InlineItems&);
- struct PartialContent {
- // This will potentially gain some more members.
- unsigned length { 0 };
- };
struct LineContent {
+ struct PartialContent {
+ bool trailingContentNeedsHyphen { false };
+ unsigned overlfowContentLength { 0 };
+ };
Optional<unsigned> trailingInlineItemIndex;
- Optional<PartialContent> overflowPartialContent;
+ Optional<PartialContent> partialContent;
Vector<WeakPtr<InlineItem>> floats;
const LineBuilder::RunList runList;
const LineBox lineBox;
};
- LineContent layoutLine(LineBuilder&, unsigned leadingInlineItemIndex, Optional<PartialContent> leadingPartialContent);
+ LineContent layoutLine(LineBuilder&, unsigned leadingInlineItemIndex, Optional<unsigned> partialLeadingContentLength);
private:
const InlineFormattingContext& formattingContext() const { return m_inlineFormattingContext; }
@@ -65,9 +65,9 @@
LineBreaker::Content m_uncommittedContent;
unsigned m_committedInlineItemCount { 0 };
Vector<WeakPtr<InlineItem>> m_floats;
- std::unique_ptr<InlineTextItem> m_leadingPartialTextItem;
- std::unique_ptr<InlineTextItem> m_trailingPartialTextItem;
- Optional<PartialContent> m_overflowPartialContent;
+ std::unique_ptr<InlineTextItem> m_partialLeadingTextItem;
+ std::unique_ptr<InlineTextItem> m_partialTrailingTextItem;
+ Optional<LineContent::PartialContent> m_partialContent;
unsigned m_successiveHyphenatedLineCount { 0 };
};
diff --git a/Source/WebCore/layout/integration/LayoutIntegrationLineLayout.cpp b/Source/WebCore/layout/integration/LayoutIntegrationLineLayout.cpp
index 90d098d..3b5f380 100644
--- a/Source/WebCore/layout/integration/LayoutIntegrationLineLayout.cpp
+++ b/Source/WebCore/layout/integration/LayoutIntegrationLineLayout.cpp
@@ -212,8 +212,6 @@
continue;
}
- // FIXME: Hyphens.
-
auto& lineBox = inlineContent.lineBoxForRun(run);
auto baselineOffset = paintOffset.y() + lineBox.logicalTop() + lineBox.baselineOffset();
@@ -221,7 +219,10 @@
auto horizontalExpansion = textContext.expansion() ? textContext.expansion()->horizontalExpansion : 0_lu;
auto logicalLeft = paintOffset.x() + run.logicalLeft();
- TextRun textRun { textContext.content(), logicalLeft, horizontalExpansion, behavior };
+ String textWithHyphen;
+ if (textContext.needsHyphen())
+ textWithHyphen = makeString(textContext.content(), style.hyphenString());
+ TextRun textRun { !textWithHyphen.isEmpty() ? textWithHyphen : textContext.content(), logicalLeft, horizontalExpansion, behavior };
textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
FloatPoint textOrigin { rect.x() + paintOffset.x(), roundToDevicePixel(baselineOffset, deviceScaleFactor) };