Modified: trunk/Source/WebCore/ChangeLog (267433 => 267434)
--- trunk/Source/WebCore/ChangeLog 2020-09-22 19:31:47 UTC (rev 267433)
+++ trunk/Source/WebCore/ChangeLog 2020-09-22 20:07:19 UTC (rev 267434)
@@ -1,3 +1,28 @@
+2020-09-22 Zalan Bujtas <[email protected]>
+
+ [LFC][IFC] Add support for multiline inline box geometry.
+ https://bugs.webkit.org/show_bug.cgi?id=216835
+
+ Reviewed by Antti Koivisto.
+
+ This patch computes the geometry for inline boxes spanning multiple lines (e.g. <span>first line<br>next line</span).
+ However this is not the getBoundingClientRect() type of geometry where we provide geometry for each fragments. This is more like the
+ element.offset* geometry where we compute the enclosing rectangle for all the fragments.
+
+ * layout/inlineformatting/InlineFormattingContext.cpp:
+ (WebCore::Layout::InlineFormattingContext::layoutInFlowContent): Decouple the run construction and inline box geometry update logic,
+ where we loop through the runs and create "line runs" when needed and then we loop through the inline boxes on the current line and
+ update the box geometries.
+
+ (WebCore::Layout::InlineFormattingContext::computeGeometryForLineContent):
+ * layout/inlineformatting/InlineFormattingState.h:
+ (WebCore::Layout::InlineFormattingState::lineBoxes const):
+ (WebCore::Layout::InlineFormattingState::addLineBox):
+ (WebCore::Layout::InlineFormattingState::clearLineAndRuns):
+ (WebCore::Layout::InlineFormattingState::shrinkToFit):
+ * layout/inlineformatting/InlineLineBox.h:
+ (WebCore::Layout::LineBox::containsInlineLevelBox const):
+
2020-09-22 Chris Dumez <[email protected]>
AudioParams with automations must process timelines
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp (267433 => 267434)
--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp 2020-09-22 19:31:47 UTC (rev 267433)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp 2020-09-22 20:07:19 UTC (rev 267434)
@@ -113,7 +113,7 @@
computeHeightAndMargin(*layoutBox, constraints.horizontal);
}
} else if (layoutBox->isInlineBox()) {
- // Text wrapper boxes (anonymous inline level boxes) and <br>s don't generate display boxes (only display runs).
+ // Text wrapper boxes (anonymous inline level boxes) and <br>s don't have box geometries (they only generate runs).
if (!layoutBox->isInlineTextBox() && !layoutBox->isLineBreakBox()) {
// Inline boxes (<span>) can't get sized/positioned yet. At this point we can only compute their margins, borders and padding.
computeBorderAndPadding(*layoutBox, constraints.horizontal);
@@ -396,19 +396,17 @@
{
auto& formattingState = this->formattingState();
auto geometry = this->geometry();
- const auto lineBox = geometry.lineBoxForLineContent(lineContent);
+
+ formattingState.addLineBox(geometry.lineBoxForLineContent(lineContent));
+ const auto& lineBox = formattingState.lineBoxes().last();
+
auto lineRectAndLineBoxOffset = geometry.computedLineLogicalRect(lineBox, root().style(), lineContent);
auto lineLogicalRect = lineRectAndLineBoxOffset.logicalRect;
auto lineBoxVerticalOffset = lineRectAndLineBoxOffset.lineBoxVerticalOffset;
- auto lineIndex = formattingState.lines().size();
- auto constructLineGeometry = [&] {
- auto lineBoxLogicalRect = InlineRect { lineLogicalRect.top() + lineBoxVerticalOffset, lineLogicalRect.left(), lineBox.logicalWidth(), lineBox.logicalHeight() };
- formattingState.addLine({ lineLogicalRect, lineBoxLogicalRect, lineBoxVerticalOffset + lineBox.alignmentBaseline(), lineBox.horizontalAlignmentOffset().valueOr(InlineLayoutUnit { }) });
- };
- constructLineGeometry();
-
- if (!lineContent.floats.isEmpty()) {
+ auto updateFloatGeometry = [&] {
+ if (lineContent.floats.isEmpty())
+ return;
auto floatingContext = FloatingContext { root(), *this, formattingState.floatingState() };
// Move floats to their final position.
for (const auto& floatCandidate : lineContent.floats) {
@@ -421,36 +419,67 @@
boxGeometry.setLogicalTopLeft(floatingContext.positionForFloat(floatBox, horizontalConstraints));
floatingContext.append(floatBox);
}
- }
+ };
+ updateFloatGeometry();
- for (auto& lineRun : lineContent.runs) {
- auto& layoutBox = lineRun.layoutBox();
- // Inline level containers (<span>) don't generate display runs and neither do completely collapsed runs.
- auto initiatesInlineRun = lineRun.isText() || lineRun.isLineBreak() || lineRun.isBox();
- if (initiatesInlineRun) {
- auto logicalRect = lineRun.isBox() ? lineBox.inlineBoxForLayoutBox(layoutBox).logicalRect() : lineBox.logicalRectForTextRun(lineRun);
- formattingState.addLineRun({ lineIndex, layoutBox, logicalRect, lineRun.expansion(), lineRun.textContent() });
+ auto constructLineRuns = [&] {
+ auto lineIndex = formattingState.lines().size();
+ // Create the inline runs on the current line. This is mostly text and atomic inline runs.
+ for (auto& lineRun : lineContent.runs) {
+ if (lineRun.isText() || lineRun.isLineBreak())
+ formattingState.addLineRun({ lineIndex, lineRun.layoutBox(), lineBox.logicalRectForTextRun(lineRun), lineRun.expansion(), lineRun.textContent() });
+ else if (lineRun.isBox())
+ formattingState.addLineRun({ lineIndex, lineRun.layoutBox(), lineBox.inlineBoxForLayoutBox(lineRun.layoutBox()).logicalRect(), lineRun.expansion(), { } });
}
+ };
+ constructLineRuns();
- // FIXME: Since <br> and <wbr> runs have associated DOM elements, we might need to construct a display box here.
- auto needsBoxGeometry = lineRun.isBox() || lineRun.isContainerStart();
- if (needsBoxGeometry) {
+ auto updateBoxGeometry = [&] {
+ // Grab the inline boxes (even those that don't have associated layout boxes on the current line due to line wrapping)
+ // and update their geometries.
+ for (auto& inlineBox : lineBox.inlineBoxList()) {
+ auto& layoutBox = inlineBox->layoutBox();
+ if (&layoutBox == &root()) {
+ // Ignore root inline box.
+ continue;
+ }
auto& boxGeometry = formattingState.boxGeometry(layoutBox);
- auto& inlineBox = lineBox.inlineBoxForLayoutBox(layoutBox);
- auto topLeft = inlineBox.logicalRect().topLeft();
- topLeft.move({ }, lineBoxVerticalOffset);
+ auto logicalTopLeft = inlineBox->logicalRect().topLeft();
+
+ logicalTopLeft.move({ }, lineBoxVerticalOffset);
if (layoutBox.isInFlowPositioned())
- topLeft += geometry.inFlowPositionedPositionOffset(layoutBox, horizontalConstraints);
- boxGeometry.setLogicalTopLeft(toLayoutPoint(topLeft));
- if (lineRun.isContainerStart()) {
- auto marginBoxWidth = inlineBox.logicalWidth();
- auto contentBoxWidth = marginBoxWidth - (boxGeometry.marginStart() + boxGeometry.borderLeft() + boxGeometry.paddingLeft().valueOr(0));
- // FIXME: Fix it for multiline.
+ logicalTopLeft += geometry.inFlowPositionedPositionOffset(layoutBox, horizontalConstraints);
+
+ if (layoutBox.isAtomicInlineLevelBox()) {
+ // Atomic inline boxes are all set. Their margin/border/content box geometries are already computed. We just have to position them here.
+ boxGeometry.setLogicalTopLeft(toLayoutPoint(logicalTopLeft));
+ continue;
+ }
+ auto marginBoxWidth = inlineBox->logicalWidth();
+ auto contentBoxWidth = marginBoxWidth - (boxGeometry.marginStart() + boxGeometry.borderLeft() + boxGeometry.paddingLeft().valueOr(0));
+ // Non-atomic inline level boxes may or may not be wrapped and have geometries on multiple lines.
+ int previousLineIndex = formattingState.lineBoxes().size() - 2;
+ auto isSpanningInlineBox = previousLineIndex > 0 && formattingState.lineBoxes()[previousLineIndex].containsInlineLevelBox(layoutBox);
+ if (!isSpanningInlineBox) {
+ // This box showed up on this line the first time.
+ boxGeometry.setLogicalTopLeft(toLayoutPoint(logicalTopLeft));
boxGeometry.setContentBoxWidth(toLayoutUnit(contentBoxWidth));
- boxGeometry.setContentBoxHeight(toLayoutUnit(inlineBox.logicalHeight()));
+ boxGeometry.setContentBoxHeight(toLayoutUnit(inlineBox->logicalHeight()));
+ continue;
}
+ // This is a just a simple box geometry for the line spanning inline box. getBoundingClientRect looks into each line boxes (will turn into fragmented boxes).
+ boxGeometry.setLogicalLeft(std::min(boxGeometry.logicalLeft(), toLayoutUnit(logicalTopLeft.x())));
+ boxGeometry.setContentBoxWidth(std::max(toLayoutUnit(contentBoxWidth), boxGeometry.contentBoxWidth()));
+ boxGeometry.setContentBoxHeight(boxGeometry.contentBoxHeight() + toLayoutUnit(inlineBox->logicalHeight()));
}
- }
+ };
+ updateBoxGeometry();
+
+ auto constructLineGeometry = [&] {
+ auto lineBoxLogicalRect = InlineRect { lineLogicalRect.top() + lineBoxVerticalOffset, lineLogicalRect.left(), lineBox.logicalWidth(), lineBox.logicalHeight() };
+ formattingState.addLine({ lineLogicalRect, lineBoxLogicalRect, lineBoxVerticalOffset + lineBox.alignmentBaseline(), lineBox.horizontalAlignmentOffset().valueOr(InlineLayoutUnit { }) });
+ };
+ constructLineGeometry();
return lineLogicalRect;
}
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingState.h (267433 => 267434)
--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingState.h 2020-09-22 19:31:47 UTC (rev 267433)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingState.h 2020-09-22 20:07:19 UTC (rev 267434)
@@ -29,6 +29,7 @@
#include "FormattingState.h"
#include "InlineItem.h"
+#include "InlineLineBox.h"
#include "InlineLineGeometry.h"
#include "InlineLineRun.h"
#include <wtf/IsoMalloc.h>
@@ -38,6 +39,7 @@
using InlineItems = Vector<InlineItem>;
using InlineLines = Vector<InlineLineGeometry>;
+using InlineLineBoxes = Vector<LineBox>;
using InlineLineRuns = Vector<LineRun>;
// InlineFormattingState holds the state for a particular inline formatting context tree.
@@ -55,6 +57,9 @@
InlineLines& lines() { return m_lines; }
void addLine(const InlineLineGeometry& line) { m_lines.append(line); }
+ const InlineLineBoxes& lineBoxes() const { return m_lineBoxes; }
+ void addLineBox(LineBox&& lineBox) { m_lineBoxes.append(WTFMove(lineBox)); }
+
const InlineLineRuns& lineRuns() const { return m_lineRuns; }
InlineLineRuns& lineRuns() { return m_lineRuns; }
void addLineRun(const LineRun& run) { m_lineRuns.append(run); }
@@ -66,6 +71,7 @@
// Cacheable input to line layout.
InlineItems m_inlineItems;
InlineLines m_lines;
+ InlineLineBoxes m_lineBoxes;
InlineLineRuns m_lineRuns;
};
@@ -72,6 +78,7 @@
inline void InlineFormattingState::clearLineAndRuns()
{
m_lines.clear();
+ m_lineBoxes.clear();
m_lineRuns.clear();
}
@@ -78,6 +85,7 @@
inline void InlineFormattingState::shrinkToFit()
{
m_lines.shrinkToFit();
+ m_lineBoxes.shrinkToFit();
m_lineRuns.shrinkToFit();
}
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBox.h (267433 => 267434)
--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBox.h 2020-09-22 19:31:47 UTC (rev 267433)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBox.h 2020-09-22 20:07:19 UTC (rev 267434)
@@ -111,9 +111,8 @@
const InlineBox& inlineBoxForLayoutBox(const Box& layoutBox) const { return *m_inlineBoxRectMap.get(&layoutBox); }
InlineRect logicalRectForTextRun(const Line::Run&) const;
-
- using InlineBoxMap = HashMap<const Box*, InlineBox*>;
auto inlineBoxList() const { return m_inlineBoxRectMap.values(); }
+ bool containsInlineLevelBox(const Box& layoutBox) const { return m_inlineBoxRectMap.contains(&layoutBox); }
InlineLayoutUnit alignmentBaseline() const { return m_rootInlineBox->logicalTop() + m_rootInlineBox->baseline(); }
@@ -140,7 +139,7 @@
std::unique_ptr<InlineBox> m_rootInlineBox;
InlineBoxList m_nonRootInlineBoxList;
- InlineBoxMap m_inlineBoxRectMap;
+ HashMap<const Box*, InlineBox*> m_inlineBoxRectMap;
};
}