Diff
Modified: trunk/LayoutTests/ChangeLog (267636 => 267637)
--- trunk/LayoutTests/ChangeLog 2020-09-26 21:08:55 UTC (rev 267636)
+++ trunk/LayoutTests/ChangeLog 2020-09-26 21:31:49 UTC (rev 267637)
@@ -1,5 +1,15 @@
2020-09-26 Zalan Bujtas <[email protected]>
+ [LFC][IFC] Add support for <wbr>
+ https://bugs.webkit.org/show_bug.cgi?id=217014
+
+ Reviewed by Antti Koivisto.
+
+ * fast/layoutformattingcontext/wbr-simple-expected.html: Added.
+ * fast/layoutformattingcontext/wbr-simple.html: Added.
+
+2020-09-26 Zalan Bujtas <[email protected]>
+
[LFC][IFC] Line content logical width expands with run expansions (text-align: justify)
https://bugs.webkit.org/show_bug.cgi?id=217011
Added: trunk/LayoutTests/fast/layoutformattingcontext/wbr-simple-expected.html (0 => 267637)
--- trunk/LayoutTests/fast/layoutformattingcontext/wbr-simple-expected.html (rev 0)
+++ trunk/LayoutTests/fast/layoutformattingcontext/wbr-simple-expected.html 2020-09-26 21:31:49 UTC (rev 267637)
@@ -0,0 +1,8 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ internal:LayoutFormattingContextEnabled=true internal:LayoutFormattingContextIntegrationEnabled=false ] -->
+<style>
+div {
+ background-color: green;
+}
+</style>
+<div style="width: 300px; height: 10px;"></div>
+<div style="width: 100px; height: 20px;"></div>
Added: trunk/LayoutTests/fast/layoutformattingcontext/wbr-simple.html (0 => 267637)
--- trunk/LayoutTests/fast/layoutformattingcontext/wbr-simple.html (rev 0)
+++ trunk/LayoutTests/fast/layoutformattingcontext/wbr-simple.html 2020-09-26 21:31:49 UTC (rev 267637)
@@ -0,0 +1,11 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ internal:LayoutFormattingContextEnabled=true internal:LayoutFormattingContextIntegrationEnabled=false ] -->
+<style>
+div {
+ background-color: green;
+ color: green;
+ font-family: Ahem;
+ font-size: 10px;
+}
+</style>
+<div style="width: 300px;">only_one_line<wbr>here</div>
+<div style="width: 100px">first_line<wbr>second_line</div>
Modified: trunk/Source/WebCore/ChangeLog (267636 => 267637)
--- trunk/Source/WebCore/ChangeLog 2020-09-26 21:08:55 UTC (rev 267636)
+++ trunk/Source/WebCore/ChangeLog 2020-09-26 21:31:49 UTC (rev 267637)
@@ -1,3 +1,31 @@
+2020-09-26 Zalan Bujtas <[email protected]>
+
+ [LFC][IFC] Add support for <wbr>
+ https://bugs.webkit.org/show_bug.cgi?id=217014
+
+ Reviewed by Antti Koivisto.
+
+ Introduce the WordBreakOpportunity type of InlineItem and take it into account while searching for word break opportunities.
+ It generates an empty line run but it does not yet have geometry information.
+
+ Test: fast/layoutformattingcontext/wbr-simple.html
+
+ * layout/inlineformatting/InlineFormattingContext.cpp:
+ (WebCore::Layout::InlineFormattingContext::collectInlineContentIfNeeded):
+ * layout/inlineformatting/InlineItem.h:
+ (WebCore::Layout::InlineItem::isWordBreakOpportunity const):
+ * layout/inlineformatting/InlineLine.cpp:
+ (WebCore::Layout::Line::appendWith):
+ (WebCore::Layout::Line::appendTextContent):
+ (WebCore::Layout::Line::appendWordBreakOpportunity):
+ * layout/inlineformatting/InlineLine.h:
+ (WebCore::Layout::Line::Run::isWordBreakOpportunity const):
+ * layout/inlineformatting/InlineLineBuilder.cpp:
+ (WebCore::Layout::nextWrapOpportunity):
+ (WebCore::Layout::LineCandidate::InlineContent::appendInlineItem):
+ (WebCore::Layout::LineBuilder::inlineItemWidth const):
+ (WebCore::Layout::LineBuilder::nextContentForLine):
+
2020-09-26 Alex Christensen <[email protected]>
Fix Big Sur clean build after r267613
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp (267636 => 267637)
--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp 2020-09-26 21:08:55 UTC (rev 267636)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp 2020-09-26 21:31:49 UTC (rev 267637)
@@ -40,6 +40,7 @@
#include "LayoutContext.h"
#include "LayoutInitialContainingBlock.h"
#include "LayoutInlineTextBox.h"
+#include "LayoutLineBreakBox.h"
#include "LayoutReplacedBox.h"
#include "LayoutState.h"
#include "Logging.h"
@@ -370,9 +371,9 @@
while (!layoutQueue.isEmpty()) {
auto& layoutBox = *layoutQueue.takeLast();
- if (layoutBox.isLineBreakBox()) {
- // FIXME: Treat <wbr> as a word break opportunity instead.
- formattingState.addInlineItem({ layoutBox, InlineItem::Type::HardLineBreak });
+ if (is<LineBreakBox>(layoutBox)) {
+ auto& lineBreakBox = downcast<LineBreakBox>(layoutBox);
+ formattingState.addInlineItem({ layoutBox, lineBreakBox.isOptional() ? InlineItem::Type::WordBreakOpportunity : InlineItem::Type::HardLineBreak });
} else if (layoutBox.isFloatingPositioned())
formattingState.addInlineItem({ layoutBox, InlineItem::Type::Float });
else if (layoutBox.isAtomicInlineLevelBox())
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineItem.h (267636 => 267637)
--- trunk/Source/WebCore/layout/inlineformatting/InlineItem.h 2020-09-26 21:08:55 UTC (rev 267636)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineItem.h 2020-09-26 21:31:49 UTC (rev 267637)
@@ -35,7 +35,7 @@
class InlineItem {
public:
- enum class Type : uint8_t { Text, HardLineBreak, SoftLineBreak, Box, Float, ContainerStart, ContainerEnd };
+ enum class Type : uint8_t { Text, HardLineBreak, SoftLineBreak, WordBreakOpportunity, Box, Float, ContainerStart, ContainerEnd };
InlineItem(const Box& layoutBox, Type);
Type type() const { return m_type; }
@@ -46,6 +46,7 @@
bool isBox() const { return type() == Type::Box; }
bool isFloat() const { return type() == Type::Float; }
bool isLineBreak() const { return isSoftLineBreak() || isHardLineBreak(); }
+ bool isWordBreakOpportunity() const { return type() == Type::WordBreakOpportunity; }
bool isSoftLineBreak() const { return type() == Type::SoftLineBreak; }
bool isHardLineBreak() const { return type() == Type::HardLineBreak; }
bool isContainerStart() const { return type() == Type::ContainerStart; }
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp (267636 => 267637)
--- trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp 2020-09-26 21:08:55 UTC (rev 267636)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp 2020-09-26 21:31:49 UTC (rev 267637)
@@ -224,6 +224,8 @@
appendTextContent(downcast<InlineTextItem>(inlineItem), inlineRunDetails.logicalWidth, inlineRunDetails.needsHyphen);
else if (inlineItem.isLineBreak())
appendLineBreak(inlineItem);
+ else if (inlineItem.isWordBreakOpportunity())
+ appendWordBreakOpportunity(inlineItem);
else if (inlineItem.isContainerStart())
appendInlineContainerStart(inlineItem, inlineRunDetails.logicalWidth);
else if (inlineItem.isContainerEnd())
@@ -284,7 +286,7 @@
// 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.hasCollapsibleTrailingWhitespace();
- ASSERT(run.isContainerStart() || run.isContainerEnd());
+ ASSERT(run.isContainerStart() || run.isContainerEnd() || run.isWordBreakOpportunity());
}
// Leading whitespace.
return !isWhitespacePreserved(inlineTextItem.style());
@@ -348,6 +350,11 @@
m_runs.append({ downcast<InlineSoftLineBreakItem>(inlineItem), contentLogicalWidth() });
}
+void Line::appendWordBreakOpportunity(const InlineItem& inlineItem)
+{
+ m_runs.append({ inlineItem, contentLogicalWidth(), 0_lu });
+}
+
bool Line::isRunVisuallyNonEmpty(const Run& run) const
{
if (run.isText())
@@ -374,6 +381,9 @@
return run.logicalWidth();
}
+ if (run.isWordBreakOpportunity())
+ return false;
+
ASSERT_NOT_REACHED();
return false;
}
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLine.h (267636 => 267637)
--- trunk/Source/WebCore/layout/inlineformatting/InlineLine.h 2020-09-26 21:08:55 UTC (rev 267636)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLine.h 2020-09-26 21:31:49 UTC (rev 267637)
@@ -66,6 +66,7 @@
bool isText() const { return m_type == InlineItem::Type::Text; }
bool isBox() const { return m_type == InlineItem::Type::Box; }
bool isLineBreak() const { return m_type == InlineItem::Type::HardLineBreak || m_type == InlineItem::Type::SoftLineBreak; }
+ bool isWordBreakOpportunity() const { return m_type == InlineItem::Type::WordBreakOpportunity; }
bool isContainerStart() const { return m_type == InlineItem::Type::ContainerStart; }
bool isContainerEnd() const { return m_type == InlineItem::Type::ContainerEnd; }
@@ -143,6 +144,7 @@
void appendInlineContainerStart(const InlineItem&, InlineLayoutUnit logicalWidth);
void appendInlineContainerEnd(const InlineItem&, InlineLayoutUnit logicalWidth);
void appendLineBreak(const InlineItem&);
+ void appendWordBreakOpportunity(const InlineItem&);
void removeTrailingTrimmableContent();
void visuallyCollapsePreWrapOverflowContent();
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp (267636 => 267637)
--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp 2020-09-26 21:08:55 UTC (rev 267636)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp 2020-09-26 21:31:49 UTC (rev 267637)
@@ -110,55 +110,41 @@
static inline size_t nextWrapOpportunity(const InlineItems& inlineContent, size_t startIndex, const LineBuilder::InlineItemRange layoutRange)
{
- // 1. Find the start candidate by skipping leading non-content items e.g <span><span>start : skip "<span><span>"
- // 2. Find the end candidate by skipping non-content items inbetween e.g. <span><span>start</span>end: skip "</span>"
+ // 1. Find the start candidate by skipping leading non-content items e.g "<span><span>start". Opportunity is after "<span><span>".
+ // 2. Find the end candidate by skipping non-content items inbetween e.g. "<span><span>start</span>end". Opportunity is after "</span>".
// 3. Check if there's a soft wrap opportunity between the 2 candidate inline items and repeat.
- // 4. Any force line break inbetween is considered as a wrap opportunity.
+ // 4. Any force line break/explicit wrap content inbetween is considered as wrap opportunity.
- // [ex-][container start][container end][float][ample] (ex-<span></span><div style="float:left"></div>ample) : wrap index is at [ex-].
- // [ex][container start][amp-][container start][le] (ex<span>amp-<span>ample) : wrap index is at [amp-].
- // [ex-][container start][line break][ample] (ex-<span><br>ample) : wrap index is after [br].
- auto isAtLineBreak = false;
-
- auto inlineItemIndexWithContent = [&] (auto index) {
- // Note that floats are not part of the inline content. We should treat them as if they were not here as far as wrap opportunities are concerned.
- // [text][float box][text] is essentially just [text][text]
- for (; index < layoutRange.end; ++index) {
- auto& inlineItem = inlineContent[index];
- if (inlineItem.isText() || inlineItem.isBox())
- return index;
- if (inlineItem.isLineBreak()) {
- isAtLineBreak = true;
- return index;
- }
+ // [ex-][container start][container end][float][ample] (ex-<span></span><div style="float:left"></div>ample). Wrap index is at [ex-].
+ // [ex][container start][amp-][container start][le] (ex<span>amp-<span>ample). Wrap index is at [amp-].
+ // [ex-][container start][line break][ample] (ex-<span><br>ample). Wrap index is after [br].
+ auto previousInlineItemIndex = Optional<size_t> { };
+ for (auto index = startIndex; index < layoutRange.end; ++index) {
+ auto& inlineItem = inlineContent[index];
+ if (inlineItem.isLineBreak() || inlineItem.isWordBreakOpportunity()) {
+ // We always stop at explicit wrapping opportunities e.g. <br>. The wrap position is after the opportunity position.
+ return ++index;
}
- return layoutRange.end;
- };
-
- // Start at the first inline item with content.
- // [container start][ex-] : start at [ex-]
- auto startContentIndex = inlineItemIndexWithContent(startIndex);
- if (isAtLineBreak) {
- // Content starts with a line break. The wrap position is after the line break.
- return startContentIndex + 1;
- }
-
- while (startContentIndex < layoutRange.end) {
- // 1. Find the next inline item with content.
- // 2. Check if there's a soft wrap opportunity between the start and the next inline item.
- auto nextContentIndex = inlineItemIndexWithContent(startContentIndex + 1);
- if (nextContentIndex == layoutRange.end)
- return nextContentIndex;
- if (isAtLineBreak) {
- // We always stop at line breaks. The wrap position is after the line break.
- return nextContentIndex + 1;
+ if (inlineItem.isFloat()) {
+ // Floats are not part of the inline content. We ignore them as far as wrap opportunities are concerned.
+ // [text][float box][text] is essentially just [text][text]
+ continue;
}
- if (isAtSoftWrapOpportunity(inlineContent[startContentIndex], inlineContent[nextContentIndex])) {
- // There's a soft wrap opportunity between the start and the nextContent.
+ if (inlineItem.isContainerStart() || inlineItem.isContainerEnd()) {
+ // There's no wrapping opportunity between <span>text, <span></span> or </span>text.
+ continue;
+ }
+ ASSERT(inlineItem.isText() || inlineItem.isBox());
+ if (!previousInlineItemIndex) {
+ previousInlineItemIndex = index;
+ continue;
+ }
+ if (isAtSoftWrapOpportunity(inlineContent[*previousInlineItemIndex], inlineContent[index])) {
+ // There's a soft wrap opportunity between 'previousInlineItemIndex' and 'index'.
// Now forward-find from the start position to see where we can actually wrap.
// [ex-][ample] vs. [ex-][container start][container end][ample]
// where [ex-] is startContent and [ample] is the nextContent.
- for (auto candidateIndex = startContentIndex + 1; candidateIndex < nextContentIndex; ++candidateIndex) {
+ for (auto candidateIndex = *previousInlineItemIndex + 1; candidateIndex < index; ++candidateIndex) {
if (inlineContent[candidateIndex].isContainerStart()) {
// inline content and [container start] and [container end] form unbreakable content.
// ex-<span></span>ample : wrap opportunity is after "ex-".
@@ -168,9 +154,9 @@
return candidateIndex;
}
}
- return nextContentIndex;
+ return index;
}
- startContentIndex = nextContentIndex;
+ previousInlineItemIndex = index;
}
return layoutRange.end;
}
@@ -247,7 +233,7 @@
auto& inlineTextItem = downcast<InlineTextItem>(inlineItem);
return inlineTextItem.isWhitespace() && !TextUtil::shouldPreserveTrailingWhitespace(inlineTextItem.style());
}
- if (inlineItem.isContainerStart() || inlineItem.isContainerEnd())
+ if (inlineItem.isContainerStart() || inlineItem.isContainerEnd() || inlineItem.isWordBreakOpportunity())
return false;
ASSERT_NOT_REACHED();
return true;
@@ -310,8 +296,8 @@
return TextUtil::width(inlineTextItem, inlineTextItem.start(), end, contentLogicalLeft);
}
- if (inlineItem.isLineBreak())
- return 0;
+ if (inlineItem.isLineBreak() || inlineItem.isWordBreakOpportunity())
+ return { };
auto& layoutBox = inlineItem.layoutBox();
auto& boxGeometry = m_inlineFormattingContext.geometryForBox(layoutBox);
@@ -537,7 +523,7 @@
accumulatedWidth += floatWidth;
continue;
}
- if (inlineItem.isText() || inlineItem.isContainerStart() || inlineItem.isContainerEnd() || inlineItem.isBox()) {
+ if (inlineItem.isText() || inlineItem.isContainerStart() || inlineItem.isContainerEnd() || inlineItem.isBox() || inlineItem.isWordBreakOpportunity()) {
auto inlineItenmWidth = inlineItemWidth(inlineItem, currentLogicalRight);
lineCandidate.inlineContent.appendInlineItem(inlineItem, inlineItenmWidth);
currentLogicalRight += inlineItenmWidth;