<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[276886] trunk/Source/WebCore</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/276886">276886</a></dd>
<dt>Author</dt> <dd>zalan@apple.com</dd>
<dt>Date</dt> <dd>2021-05-02 09:59:56 -0700 (Sun, 02 May 2021)</dd>
</dl>

<h3>Log Message</h3>
<pre>[LFC] Rename block(inline/table/flex)formattingContext directories to block(inline/table/flex)
https://bugs.webkit.org/show_bug.cgi?id=225275

Reviewed by Antti Koivisto.

The formattingContext postfix is redundant as they are all under the /formattingContexts directory.

* CMakeLists.txt:
* Headers.cmake:
* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreCMakeListstxt">trunk/Source/WebCore/CMakeLists.txt</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreHeaderscmake">trunk/Source/WebCore/Headers.cmake</a></li>
<li><a href="#trunkSourceWebCoreSourcestxt">trunk/Source/WebCore/Sources.txt</a></li>
<li><a href="#trunkSourceWebCoreWebCorexcodeprojprojectpbxproj">trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li>trunk/Source/WebCore/layout/formattingContexts/block/</li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsblockBlockFormattingContextcpp">trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingContext.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsblockBlockFormattingContexth">trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingContext.h</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsblockBlockFormattingContextGeometrycpp">trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingContextGeometry.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsblockBlockFormattingContextQuirkscpp">trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingContextQuirks.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsblockBlockFormattingStatecpp">trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingState.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsblockBlockFormattingStateh">trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingState.h</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsblockBlockMarginCollapsecpp">trunk/Source/WebCore/layout/formattingContexts/block/BlockMarginCollapse.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsblockPrecomputedBlockMarginCollapsecpp">trunk/Source/WebCore/layout/formattingContexts/block/PrecomputedBlockMarginCollapse.cpp</a></li>
<li>trunk/Source/WebCore/layout/formattingContexts/block/tablewrapper/</li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsblocktablewrapperTableWrapperBlockFormattingContextcpp">trunk/Source/WebCore/layout/formattingContexts/block/tablewrapper/TableWrapperBlockFormattingContext.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsblocktablewrapperTableWrapperBlockFormattingContexth">trunk/Source/WebCore/layout/formattingContexts/block/tablewrapper/TableWrapperBlockFormattingContext.h</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsblocktablewrapperTableWrapperBlockFormattingContextQuirkscpp">trunk/Source/WebCore/layout/formattingContexts/block/tablewrapper/TableWrapperBlockFormattingContextQuirks.cpp</a></li>
<li>trunk/Source/WebCore/layout/formattingContexts/flex/</li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsflexFlexFormattingContextcpp">trunk/Source/WebCore/layout/formattingContexts/flex/FlexFormattingContext.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsflexFlexFormattingContexth">trunk/Source/WebCore/layout/formattingContexts/flex/FlexFormattingContext.h</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsflexFlexFormattingContextGeometrycpp">trunk/Source/WebCore/layout/formattingContexts/flex/FlexFormattingContextGeometry.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsflexFlexFormattingStatecpp">trunk/Source/WebCore/layout/formattingContexts/flex/FlexFormattingState.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsflexFlexFormattingStateh">trunk/Source/WebCore/layout/formattingContexts/flex/FlexFormattingState.h</a></li>
<li>trunk/Source/WebCore/layout/formattingContexts/inline/</li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineContentBreakercpp">trunk/Source/WebCore/layout/formattingContexts/inline/InlineContentBreaker.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineContentBreakerh">trunk/Source/WebCore/layout/formattingContexts/inline/InlineContentBreaker.h</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineFormattingContextcpp">trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContext.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineFormattingContexth">trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContext.h</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineFormattingContextGeometrycpp">trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContextGeometry.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineFormattingContextQuirkscpp">trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContextQuirks.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineFormattingStatecpp">trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingState.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineFormattingStateh">trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingState.h</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineItemcpp">trunk/Source/WebCore/layout/formattingContexts/inline/InlineItem.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineItemh">trunk/Source/WebCore/layout/formattingContexts/inline/InlineItem.h</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineLinecpp">trunk/Source/WebCore/layout/formattingContexts/inline/InlineLine.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineLineh">trunk/Source/WebCore/layout/formattingContexts/inline/InlineLine.h</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineLineBoxcpp">trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBox.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineLineBoxh">trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBox.h</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineLineBuildercpp">trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBuilder.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineLineBuilderh">trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBuilder.h</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineLineGeometryh">trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineGeometry.h</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineLineRunh">trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineRun.h</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineRecth">trunk/Source/WebCore/layout/formattingContexts/inline/InlineRect.h</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineSoftLineBreakItemh">trunk/Source/WebCore/layout/formattingContexts/inline/InlineSoftLineBreakItem.h</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineTextItemcpp">trunk/Source/WebCore/layout/formattingContexts/inline/InlineTextItem.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineTextItemh">trunk/Source/WebCore/layout/formattingContexts/inline/InlineTextItem.h</a></li>
<li>trunk/Source/WebCore/layout/formattingContexts/inline/text/</li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlinetextTextUtilcpp">trunk/Source/WebCore/layout/formattingContexts/inline/text/TextUtil.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlinetextTextUtilh">trunk/Source/WebCore/layout/formattingContexts/inline/text/TextUtil.h</a></li>
<li>trunk/Source/WebCore/layout/formattingContexts/table/</li>
<li><a href="#trunkSourceWebCorelayoutformattingContextstableTableFormattingContextcpp">trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingContext.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextstableTableFormattingContexth">trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingContext.h</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextstableTableFormattingContextGeometrycpp">trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingContextGeometry.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextstableTableFormattingContextQuirkscpp">trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingContextQuirks.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextstableTableFormattingStatecpp">trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingState.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextstableTableFormattingStateh">trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingState.h</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextstableTableGridcpp">trunk/Source/WebCore/layout/formattingContexts/table/TableGrid.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextstableTableGridh">trunk/Source/WebCore/layout/formattingContexts/table/TableGrid.h</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextstableTableLayoutcpp">trunk/Source/WebCore/layout/formattingContexts/table/TableLayout.cpp</a></li>
</ul>

<h3>Removed Paths</h3>
<ul>
<li>trunk/Source/WebCore/layout/formattingContexts/blockformatting/</li>
<li>trunk/Source/WebCore/layout/formattingContexts/flexformatting/</li>
<li>trunk/Source/WebCore/layout/formattingContexts/inlineformatting/</li>
<li>trunk/Source/WebCore/layout/formattingContexts/tableformatting/</li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreCMakeListstxt"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/CMakeLists.txt (276885 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/CMakeLists.txt      2021-05-02 16:45:29 UTC (rev 276885)
+++ trunk/Source/WebCore/CMakeLists.txt 2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -102,16 +102,16 @@
</span><span class="cx">     "${WEBCORE_DIR}/inspector/agents/worker"
</span><span class="cx">     "${WEBCORE_DIR}/layout"
</span><span class="cx">     "${WEBCORE_DIR}/layout/formattingContexts"
</span><del>-    "${WEBCORE_DIR}/layout/formattingContexts/blockformatting"
-    "${WEBCORE_DIR}/layout/formattingContexts/blockformatting/tablewrapper"
-    "${WEBCORE_DIR}/layout/formattingContexts/flexformatting"
</del><ins>+    "${WEBCORE_DIR}/layout/formattingContexts/block"
+    "${WEBCORE_DIR}/layout/formattingContexts/block/tablewrapper"
+    "${WEBCORE_DIR}/layout/formattingContexts/flex"
</ins><span class="cx">     "${WEBCORE_DIR}/layout/floats"
</span><del>-    "${WEBCORE_DIR}/layout/formattingContexts/inlineformatting"
-    "${WEBCORE_DIR}/layout/formattingContexts/inlineformatting/text"
</del><ins>+    "${WEBCORE_DIR}/layout/formattingContexts/inline"
+    "${WEBCORE_DIR}/layout/formattingContexts/inline/text"
</ins><span class="cx">     "${WEBCORE_DIR}/layout/integration"
</span><span class="cx">     "${WEBCORE_DIR}/layout/invalidation"
</span><span class="cx">     "${WEBCORE_DIR}/layout/layouttree"
</span><del>-    "${WEBCORE_DIR}/layout/formattingContexts/tableformatting"
</del><ins>+    "${WEBCORE_DIR}/layout/formattingContexts/table"
</ins><span class="cx">     "${WEBCORE_DIR}/loader"
</span><span class="cx">     "${WEBCORE_DIR}/loader/appcache"
</span><span class="cx">     "${WEBCORE_DIR}/loader/archive"
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (276885 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog   2021-05-02 16:45:29 UTC (rev 276885)
+++ trunk/Source/WebCore/ChangeLog      2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -1,3 +1,17 @@
</span><ins>+2021-05-02  Zalan Bujtas  <zalan@apple.com>
+
+        [LFC] Rename block(inline/table/flex)formattingContext directories to block(inline/table/flex)
+        https://bugs.webkit.org/show_bug.cgi?id=225275
+
+        Reviewed by Antti Koivisto.
+
+        The formattingContext postfix is redundant as they are all under the /formattingContexts directory.
+
+        * CMakeLists.txt:
+        * Headers.cmake:
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+
</ins><span class="cx"> 2021-05-02  Jer Noble  <jer.noble@apple.com>
</span><span class="cx"> 
</span><span class="cx">         [Perf] Creation of many video elements slows over time
</span></span></pre></div>
<a id="trunkSourceWebCoreHeaderscmake"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Headers.cmake (276885 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Headers.cmake       2021-05-02 16:45:29 UTC (rev 276885)
+++ trunk/Source/WebCore/Headers.cmake  2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -754,7 +754,7 @@
</span><span class="cx">     layout/LayoutUnits.h
</span><span class="cx">     layout/MarginTypes.h
</span><span class="cx"> 
</span><del>-    layout/formattingContexts/inlineformatting/InlineRect.h
</del><ins>+    layout/formattingContexts/inline/InlineRect.h
</ins><span class="cx"> 
</span><span class="cx">     layout/integration/LayoutIntegrationInlineContent.h
</span><span class="cx">     layout/integration/LayoutIntegrationLine.h
</span></span></pre></div>
<a id="trunkSourceWebCoreSourcestxt"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Sources.txt (276885 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Sources.txt 2021-05-02 16:45:29 UTC (rev 276885)
+++ trunk/Source/WebCore/Sources.txt    2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -1517,31 +1517,31 @@
</span><span class="cx"> layout/LayoutState.cpp
</span><span class="cx"> layout/LayoutUnits.cpp
</span><span class="cx"> layout/Verification.cpp
</span><del>-layout/formattingContexts/blockformatting/BlockFormattingContext.cpp
-layout/formattingContexts/blockformatting/BlockFormattingContextGeometry.cpp
-layout/formattingContexts/blockformatting/BlockFormattingContextQuirks.cpp
-layout/formattingContexts/blockformatting/BlockFormattingState.cpp
-layout/formattingContexts/blockformatting/BlockMarginCollapse.cpp
-layout/formattingContexts/blockformatting/PrecomputedBlockMarginCollapse.cpp
-layout/formattingContexts/blockformatting/tablewrapper/TableWrapperBlockFormattingContext.cpp
-layout/formattingContexts/blockformatting/tablewrapper/TableWrapperBlockFormattingContextQuirks.cpp
-layout/formattingContexts/flexformatting/FlexFormattingContext.cpp
-layout/formattingContexts/flexformatting/FlexFormattingContextGeometry.cpp
-layout/formattingContexts/flexformatting/FlexFormattingState.cpp
</del><ins>+layout/formattingContexts/block/BlockFormattingContext.cpp
+layout/formattingContexts/block/BlockFormattingContextGeometry.cpp
+layout/formattingContexts/block/BlockFormattingContextQuirks.cpp
+layout/formattingContexts/block/BlockFormattingState.cpp
+layout/formattingContexts/block/BlockMarginCollapse.cpp
+layout/formattingContexts/block/PrecomputedBlockMarginCollapse.cpp
+layout/formattingContexts/block/tablewrapper/TableWrapperBlockFormattingContext.cpp
+layout/formattingContexts/block/tablewrapper/TableWrapperBlockFormattingContextQuirks.cpp
+layout/formattingContexts/flex/FlexFormattingContext.cpp
+layout/formattingContexts/flex/FlexFormattingContextGeometry.cpp
+layout/formattingContexts/flex/FlexFormattingState.cpp
</ins><span class="cx"> layout/floats/FloatAvoider.cpp
</span><span class="cx"> layout/floats/FloatingContext.cpp
</span><span class="cx"> layout/floats/FloatingState.cpp
</span><del>-layout/formattingContexts/inlineformatting/InlineContentBreaker.cpp
-layout/formattingContexts/inlineformatting/InlineFormattingContext.cpp
-layout/formattingContexts/inlineformatting/InlineFormattingContextGeometry.cpp
-layout/formattingContexts/inlineformatting/InlineFormattingContextQuirks.cpp
-layout/formattingContexts/inlineformatting/InlineFormattingState.cpp
-layout/formattingContexts/inlineformatting/InlineItem.cpp
-layout/formattingContexts/inlineformatting/InlineLine.cpp
-layout/formattingContexts/inlineformatting/InlineLineBox.cpp
-layout/formattingContexts/inlineformatting/InlineLineBuilder.cpp
-layout/formattingContexts/inlineformatting/InlineTextItem.cpp
-layout/formattingContexts/inlineformatting/text/TextUtil.cpp
</del><ins>+layout/formattingContexts/inline/InlineContentBreaker.cpp
+layout/formattingContexts/inline/InlineFormattingContext.cpp
+layout/formattingContexts/inline/InlineFormattingContextGeometry.cpp
+layout/formattingContexts/inline/InlineFormattingContextQuirks.cpp
+layout/formattingContexts/inline/InlineFormattingState.cpp
+layout/formattingContexts/inline/InlineItem.cpp
+layout/formattingContexts/inline/InlineLine.cpp
+layout/formattingContexts/inline/InlineLineBox.cpp
+layout/formattingContexts/inline/InlineLineBuilder.cpp
+layout/formattingContexts/inline/InlineTextItem.cpp
+layout/formattingContexts/inline/text/TextUtil.cpp
</ins><span class="cx"> layout/integration/LayoutIntegrationBoxTree.cpp
</span><span class="cx"> layout/integration/LayoutIntegrationCoverage.cpp
</span><span class="cx"> layout/integration/LayoutIntegrationInlineContentBuilder.cpp
</span><span class="lines">@@ -1560,12 +1560,12 @@
</span><span class="cx"> layout/layouttree/LayoutLineBreakBox.cpp
</span><span class="cx"> layout/layouttree/LayoutReplacedBox.cpp
</span><span class="cx"> layout/layouttree/LayoutTreeBuilder.cpp
</span><del>-layout/formattingContexts/tableformatting/TableFormattingContext.cpp
-layout/formattingContexts/tableformatting/TableFormattingContextGeometry.cpp
-layout/formattingContexts/tableformatting/TableFormattingContextQuirks.cpp
-layout/formattingContexts/tableformatting/TableFormattingState.cpp
-layout/formattingContexts/tableformatting/TableGrid.cpp
-layout/formattingContexts/tableformatting/TableLayout.cpp
</del><ins>+layout/formattingContexts/table/TableFormattingContext.cpp
+layout/formattingContexts/table/TableFormattingContextGeometry.cpp
+layout/formattingContexts/table/TableFormattingContextQuirks.cpp
+layout/formattingContexts/table/TableFormattingState.cpp
+layout/formattingContexts/table/TableGrid.cpp
+layout/formattingContexts/table/TableLayout.cpp
</ins><span class="cx"> loader/PrivateClickMeasurement.cpp
</span><span class="cx"> loader/ApplicationManifestLoader.cpp
</span><span class="cx"> loader/CanvasActivityRecord.cpp
</span></span></pre></div>
<a id="trunkSourceWebCoreWebCorexcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (276885 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj   2021-05-02 16:45:29 UTC (rev 276885)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj      2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -18229,7 +18229,7 @@
</span><span class="cx">                  path = layouttree;
</span><span class="cx">                  sourceTree = "<group>";
</span><span class="cx">          };
</span><del>-               115CFA99208BC09A001E6991 /* blockformatting */ = {
</del><ins>+                115CFA99208BC09A001E6991 /* block */ = {
</ins><span class="cx">                   isa = PBXGroup;
</span><span class="cx">                  children = (
</span><span class="cx">                          6FD4FE3E2470B3AD007374CC /* tablewrapper */,
</span><span class="lines">@@ -18242,10 +18242,10 @@
</span><span class="cx">                          115CFA89208B921A001E6991 /* BlockMarginCollapse.cpp */,
</span><span class="cx">                          6FAE16BA2406DE7E00A48414 /* PrecomputedBlockMarginCollapse.cpp */,
</span><span class="cx">                  );
</span><del>-                       path = blockformatting;
</del><ins>+                        path = block;
</ins><span class="cx">                   sourceTree = "<group>";
</span><span class="cx">          };
</span><del>-               115CFA9A208BC140001E6991 /* inlineformatting */ = {
</del><ins>+                115CFA9A208BC140001E6991 /* inline */ = {
</ins><span class="cx">                   isa = PBXGroup;
</span><span class="cx">                  children = (
</span><span class="cx">                          6FE7DDDD20EC6E8B008B5B4E /* text */,
</span><span class="lines">@@ -18272,7 +18272,7 @@
</span><span class="cx">                          6F1CC1DC225F8B4100720AD2 /* InlineTextItem.cpp */,
</span><span class="cx">                          6F1CC1DD225F8B4200720AD2 /* InlineTextItem.h */,
</span><span class="cx">                  );
</span><del>-                       path = inlineformatting;
</del><ins>+                        path = inline;
</ins><span class="cx">                   sourceTree = "<group>";
</span><span class="cx">          };
</span><span class="cx">          1221E0581C02B409006A1A00 /* Animation */ = {
</span><span class="lines">@@ -22599,10 +22599,10 @@
</span><span class="cx">          6F844F7F263D916100F91370 /* formattingContexts */ = {
</span><span class="cx">                  isa = PBXGroup;
</span><span class="cx">                  children = (
</span><del>-                               115CFA99208BC09A001E6991 /* blockformatting */,
-                               6FB7D2D5250FD7B5000207AA /* flexformatting */,
-                               115CFA9A208BC140001E6991 /* inlineformatting */,
-                               6FC5CA9122E3593300B13E11 /* tableformatting */,
</del><ins>+                                115CFA99208BC09A001E6991 /* block */,
+                               6FB7D2D5250FD7B5000207AA /* flex */,
+                               115CFA9A208BC140001E6991 /* inline */,
+                               6FC5CA9122E3593300B13E11 /* table */,
</ins><span class="cx">                           115CFA69208AF7D0001E6991 /* FormattingContext.cpp */,
</span><span class="cx">                          115CFA68208AF7D0001E6991 /* FormattingContext.h */,
</span><span class="cx">                          6FBB860520B464B600DAD938 /* FormattingContextGeometry.cpp */,
</span><span class="lines">@@ -22622,7 +22622,7 @@
</span><span class="cx">                  path = invalidation;
</span><span class="cx">                  sourceTree = "<group>";
</span><span class="cx">          };
</span><del>-               6FB7D2D5250FD7B5000207AA /* flexformatting */ = {
</del><ins>+                6FB7D2D5250FD7B5000207AA /* flex */ = {
</ins><span class="cx">                   isa = PBXGroup;
</span><span class="cx">                  children = (
</span><span class="cx">                          6FB7D2D7250FD7E5000207AA /* FlexFormattingContext.cpp */,
</span><span class="lines">@@ -22631,10 +22631,10 @@
</span><span class="cx">                          6FB7D2D8250FD7EF000207AA /* FlexFormattingState.cpp */,
</span><span class="cx">                          6FB7D2DA250FD7FC000207AA /* FlexFormattingState.h */,
</span><span class="cx">                  );
</span><del>-                       path = flexformatting;
</del><ins>+                        path = flex;
</ins><span class="cx">                   sourceTree = "<group>";
</span><span class="cx">          };
</span><del>-               6FC5CA9122E3593300B13E11 /* tableformatting */ = {
</del><ins>+                6FC5CA9122E3593300B13E11 /* table */ = {
</ins><span class="cx">                   isa = PBXGroup;
</span><span class="cx">                  children = (
</span><span class="cx">                          6FC5CA9422E3599400B13E11 /* TableFormattingContext.cpp */,
</span><span class="lines">@@ -22647,7 +22647,7 @@
</span><span class="cx">                          6FB22E30230097E300C20866 /* TableGrid.h */,
</span><span class="cx">                          6FF9F1BE246D966C00435083 /* TableLayout.cpp */,
</span><span class="cx">                  );
</span><del>-                       path = tableformatting;
</del><ins>+                        path = table;
</ins><span class="cx">                   sourceTree = "<group>";
</span><span class="cx">          };
</span><span class="cx">          6FCFC055212DACC2007695D2 /* floats */ = {
</span></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsblockBlockFormattingContextcppfromrev276885trunkSourceWebCorelayoutformattingContextsblockformattingBlockFormattingContextcpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingContext.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/blockformatting/BlockFormattingContext.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingContext.cpp                          (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingContext.cpp     2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,563 @@
</span><ins>+/*
+ * Copyright (C) 2018 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 "BlockFormattingContext.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "BlockFormattingState.h"
+#include "FloatingContext.h"
+#include "FloatingState.h"
+#include "InvalidationState.h"
+#include "LayoutBox.h"
+#include "LayoutChildIterator.h"
+#include "LayoutContainerBox.h"
+#include "LayoutContext.h"
+#include "LayoutInitialContainingBlock.h"
+#include "LayoutState.h"
+#include "Logging.h"
+#include "TableWrapperBlockFormattingContext.h"
+#include <wtf/IsoMallocInlines.h>
+#include <wtf/text/TextStream.h>
+
+namespace WebCore {
+namespace Layout {
+
+WTF_MAKE_ISO_ALLOCATED_IMPL(BlockFormattingContext);
+
+BlockFormattingContext::BlockFormattingContext(const ContainerBox& formattingContextRoot, BlockFormattingState& formattingState)
+    : FormattingContext(formattingContextRoot, formattingState)
+{
+}
+
+void BlockFormattingContext::layoutInFlowContent(InvalidationState& invalidationState, const ConstraintsForInFlowContent& constraints)
+{
+    // 9.4.1 Block formatting contexts
+    // In a block formatting context, boxes are laid out one after the other, vertically, beginning at the top of a containing block.
+    // The vertical distance between two sibling boxes is determined by the 'margin' properties.
+    // Vertical margins between adjacent block-level boxes in a block formatting context collapse.
+    LOG_WITH_STREAM(FormattingContextLayout, stream << "[Start] -> block formatting context -> formatting root(" << &root() << ")");
+    auto& formattingRoot = root();
+    ASSERT(formattingRoot.hasInFlowOrFloatingChild());
+    auto& floatingState = formattingState().floatingState();
+    auto floatingContext = FloatingContext { *this, floatingState };
+
+    LayoutQueue layoutQueue;
+    enum class LayoutDirection { Child, Sibling };
+    auto appendNextToLayoutQueue = [&] (const auto& layoutBox, auto direction) {
+        if (direction == LayoutDirection::Child) {
+            if (!is<ContainerBox>(layoutBox))
+                return false;
+            for (auto* child = downcast<ContainerBox>(layoutBox).firstInFlowOrFloatingChild(); child; child = child->nextInFlowOrFloatingSibling()) {
+                if (!invalidationState.needsLayout(*child))
+                    continue;
+                layoutQueue.append(child);
+                return true;
+            }
+            return false;
+        }
+
+        if (direction == LayoutDirection::Sibling) {
+            for (auto* nextSibling = layoutBox.nextInFlowOrFloatingSibling(); nextSibling; nextSibling = nextSibling->nextInFlowOrFloatingSibling()) {
+                if (!invalidationState.needsLayout(*nextSibling))
+                    continue;
+                layoutQueue.append(nextSibling);
+                return true;
+            }
+            return false;
+        }
+        ASSERT_NOT_REACHED();
+        return false;
+    };
+
+    auto constraintsForLayoutBox = [&] (const auto& layoutBox) {
+        auto& containingBlock = layoutBox.containingBlock();
+        return &containingBlock == &formattingRoot ? constraints : geometry().constraintsForInFlowContent(containingBlock);
+    };
+
+    // This is a post-order tree traversal layout.
+    // The root container layout is done in the formatting context it lives in, not that one it creates, so let's start with the first child.
+    appendNextToLayoutQueue(formattingRoot, LayoutDirection::Child);
+    // 1. Go all the way down to the leaf node
+    // 2. Compute static position and width as we traverse down
+    // 3. As we climb back on the tree, compute height and finialize position
+    // (Any subtrees with new formatting contexts need to layout synchronously)
+    while (!layoutQueue.isEmpty()) {
+        // Traverse down on the descendants and compute width/static position until we find a leaf node.
+        while (true) {
+            auto& layoutBox = *layoutQueue.last();
+            auto containingBlockConstraints = constraintsForLayoutBox(layoutBox);
+
+            computeBorderAndPadding(layoutBox, containingBlockConstraints.horizontal);
+            computeStaticVerticalPosition(layoutBox, containingBlockConstraints.vertical);
+            computeWidthAndMargin(floatingContext, layoutBox, { constraints, containingBlockConstraints });
+            computeStaticHorizontalPosition(layoutBox, containingBlockConstraints.horizontal);
+            computePositionToAvoidFloats(floatingContext, layoutBox, { constraints, containingBlockConstraints });
+
+            if (layoutBox.establishesFormattingContext()) {
+                if (is<ContainerBox>(layoutBox) && downcast<ContainerBox>(layoutBox).hasInFlowOrFloatingChild()) {
+                    auto& containerBox = downcast<ContainerBox>(layoutBox);
+                    if (containerBox.establishesInlineFormattingContext()) {
+                        // IFCs inherit floats from parent FCs. We need final vertical position to find intruding floats.
+                        precomputeVerticalPositionForBoxAndAncestors(containerBox, { constraints, containingBlockConstraints });
+                    }
+                    // Layout the inflow descendants of this formatting context root.
+                    auto formattingContext = LayoutContext::createFormattingContext(containerBox, layoutState());
+                    if (containerBox.isTableWrapperBox())
+                        downcast<TableWrapperBlockFormattingContext>(*formattingContext).setHorizontalConstraintsIgnoringFloats(containingBlockConstraints.horizontal);
+                    formattingContext->layoutInFlowContent(invalidationState, geometry().constraintsForInFlowContent(containerBox));
+                }
+                break;
+            }
+            if (!appendNextToLayoutQueue(layoutBox, LayoutDirection::Child))
+                break;
+        }
+
+        // Climb back on the ancestors and compute height/final position.
+        while (!layoutQueue.isEmpty()) {
+            auto& layoutBox = *layoutQueue.takeLast();
+            auto containingBlockConstraints = constraintsForLayoutBox(layoutBox);
+
+            // All inflow descendants (if there are any) are laid out by now. Let's compute the box's height and vertical margin.
+            computeHeightAndMargin(layoutBox, containingBlockConstraints);
+            if (layoutBox.isFloatingPositioned())
+                floatingState.append(floatingContext.toFloatItem(layoutBox));
+            else {
+                // Adjust the vertical position now that we've got final margin values for non-float avoider boxes.
+                // Float avoiders have pre-computed vertical positions when floats are present.
+                if (!layoutBox.isFloatAvoider() || floatingContext.isEmpty()) {
+                    auto& formattingState = this->formattingState();
+                    auto& boxGeometry = formattingState.boxGeometry(layoutBox);
+                    boxGeometry.setLogicalTop(verticalPositionWithMargin(layoutBox, formattingState.usedVerticalMargin(layoutBox), containingBlockConstraints.vertical));
+                }
+            }
+            auto establishesFormattingContext = layoutBox.establishesFormattingContext(); 
+            if (establishesFormattingContext) {
+                // Now that we computed the box's height, we can layout the out-of-flow descendants.
+                if (is<ContainerBox>(layoutBox) && downcast<ContainerBox>(layoutBox).hasChild()) {
+                    auto& containerBox = downcast<ContainerBox>(layoutBox);
+                    LayoutContext::createFormattingContext(containerBox, layoutState())->layoutOutOfFlowContent(invalidationState, geometry().constraintsForOutOfFlowContent(containerBox));
+                }
+            }
+            if (!establishesFormattingContext && is<ContainerBox>(layoutBox))
+                placeInFlowPositionedChildren(downcast<ContainerBox>(layoutBox), containingBlockConstraints.horizontal);
+
+            if (appendNextToLayoutQueue(layoutBox, LayoutDirection::Sibling))
+                break;
+        }
+    }
+    // Place the inflow positioned children.
+    placeInFlowPositionedChildren(formattingRoot, constraints.horizontal);
+    LOG_WITH_STREAM(FormattingContextLayout, stream << "[End] -> block formatting context -> formatting root(" << &root() << ")");
+}
+
+LayoutUnit BlockFormattingContext::usedContentHeight() const
+{
+    // 10.6.7 'Auto' heights for block formatting context roots
+
+    // If it has block-level children, the height is the distance between the top margin-edge of the topmost block-level
+    // child box and the bottom margin-edge of the bottommost block-level child box.
+
+    // In addition, if the element has any floating descendants whose bottom margin edge is below the element's bottom content edge,
+    // then the height is increased to include those edges. Only floats that participate in this block formatting context are taken
+    // into account, e.g., floats inside absolutely positioned descendants or other floats are not.
+    auto top = Optional<LayoutUnit> { };
+    auto bottom = Optional<LayoutUnit> { };
+    if (root().hasInFlowChild()) {
+        top = BoxGeometry::marginBoxRect(geometryForBox(*root().firstInFlowChild())).top();
+        bottom = BoxGeometry::marginBoxRect(geometryForBox(*root().lastInFlowChild())).bottom();
+    }
+
+    auto floatingContext = FloatingContext { *this, formattingState().floatingState() };
+    if (auto floatTop = floatingContext.top()) {
+        top = std::min(*floatTop, top.valueOr(*floatTop));
+        auto floatBottom = *floatingContext.bottom();
+        bottom = std::max(floatBottom, bottom.valueOr(floatBottom));
+    }
+    return *bottom - *top;
+}
+
+Optional<LayoutUnit> BlockFormattingContext::usedAvailableWidthForFloatAvoider(const FloatingContext& floatingContext, const Box& layoutBox, const ConstraintsPair& constraintsPair)
+{
+    // Normally the available width for an in-flow block level box is the width of the containing block's content box.
+    // However (and can't find it anywhere in the spec) non-floating positioned float avoider block level boxes are constrained by existing floats.
+    ASSERT(layoutBox.isFloatAvoider());
+    if (floatingContext.isEmpty())
+        return { };
+    // Float clear pushes the block level box either below the floats, or just one side below but the other side could overlap.
+    // What it means is that the used available width always matches the containing block's constraint.
+    if (layoutBox.hasFloatClear())
+        return { };
+
+    ASSERT(layoutBox.establishesFormattingContext());
+    // Vertical static position is not computed yet for this formatting context root, so let's just pre-compute it for now.
+    precomputeVerticalPositionForBoxAndAncestors(layoutBox, constraintsPair);
+
+    auto logicalTopInFormattingContextRootCoordinate = [&] (auto& floatAvoider) {
+        auto top = BoxGeometry::borderBoxTop(geometryForBox(floatAvoider));
+        for (auto* ancestor = &floatAvoider.containingBlock(); ancestor != &root(); ancestor = &ancestor->containingBlock())
+            top += BoxGeometry::borderBoxTop(geometryForBox(*ancestor));
+        return top;
+    };
+
+    auto floatConstraintsInContainingBlockCoordinate = [&] (auto floatConstraints) {
+        if (!floatConstraints.left && !floatConstraints.right)
+            return FloatingContext::Constraints { };
+        auto offset = LayoutSize { };
+        for (auto* ancestor = &layoutBox.containingBlock(); ancestor != &root(); ancestor = &ancestor->containingBlock())
+            offset += toLayoutSize(BoxGeometry::borderBoxTopLeft(geometryForBox(*ancestor)));
+        if (floatConstraints.left)
+            floatConstraints.left = PointInContextRoot { *floatConstraints.left - offset };
+        if (floatConstraints.right)
+            floatConstraints.right = PointInContextRoot { *floatConstraints.right - offset };
+        return floatConstraints;
+    };
+
+    // FIXME: Check if the non-yet-computed height affects this computation - and whether we have to resolve it at a later point.
+    auto logicalTop = logicalTopInFormattingContextRootCoordinate(layoutBox);
+    auto constraints = floatConstraintsInContainingBlockCoordinate(floatingContext.constraints(logicalTop, logicalTop));
+    if (!constraints.left && !constraints.right)
+        return { };
+    // Shrink the available space if the floats are actually intruding at this vertical position.
+    auto availableWidth = constraintsPair.containingBlock.horizontal.logicalWidth;
+    if (constraints.left)
+        availableWidth -= constraints.left->x;
+    if (constraints.right)
+        availableWidth -= std::max(0_lu, constraintsPair.containingBlock.horizontal.logicalRight() - constraints.right->x);
+    return availableWidth;
+}
+
+void BlockFormattingContext::placeInFlowPositionedChildren(const ContainerBox& containerBox, const HorizontalConstraints& horizontalConstraints)
+{
+    LOG_WITH_STREAM(FormattingContextLayout, stream << "Start: move in-flow positioned children -> parent: " << &containerBox);
+    for (auto& childBox : childrenOfType<Box>(containerBox)) {
+        if (!childBox.isInFlowPositioned())
+            continue;
+        auto positionOffset = geometry().inFlowPositionedPositionOffset(childBox, horizontalConstraints);
+        formattingState().boxGeometry(childBox).move(positionOffset);
+    }
+    LOG_WITH_STREAM(FormattingContextLayout, stream << "End: move in-flow positioned children -> parent: " << &containerBox);
+}
+
+void BlockFormattingContext::computeStaticVerticalPosition(const Box& layoutBox, const VerticalConstraints& verticalConstraints)
+{
+    formattingState().boxGeometry(layoutBox).setLogicalTop(geometry().staticVerticalPosition(layoutBox, verticalConstraints));
+}
+
+void BlockFormattingContext::computeStaticHorizontalPosition(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints)
+{
+    formattingState().boxGeometry(layoutBox).setLogicalLeft(geometry().staticHorizontalPosition(layoutBox, horizontalConstraints));
+}
+
+void BlockFormattingContext::precomputeVerticalPositionForBoxAndAncestors(const Box& layoutBox, const ConstraintsPair& constraintsPair)
+{
+    // In order to figure out whether a box should avoid a float, we need to know the final positions of both (ignore relative positioning for now).
+    // In block formatting context the final position for a normal flow box includes
+    // 1. the static position and
+    // 2. the corresponding (non)collapsed margins.
+    // Now the vertical margins are computed when all the descendants are finalized, because the margin values might be depending on the height of the box
+    // (and the height might be based on the content).
+    // So when we get to the point where we intersect the box with the float to decide if the box needs to move, we don't yet have the final vertical position.
+    //
+    // The idea here is that as long as we don't cross the block formatting context boundary, we should be able to pre-compute the final top position.
+    // FIXME: we currently don't account for the "clear" property when computing the final position for an ancestor.
+    for (auto* ancestor = &layoutBox; ancestor && ancestor != &root(); ancestor = &ancestor->containingBlock()) {
+        auto constraintsForAncestor = [&] {
+            auto& containingBlock = ancestor->containingBlock();
+            return &containingBlock == &root() ? constraintsPair.formattingContextRoot : geometry().constraintsForInFlowContent(containingBlock);
+        }();
+
+        auto computedVerticalMargin = geometry().computedVerticalMargin(*ancestor, constraintsForAncestor.horizontal);
+        auto usedNonCollapsedMargin = UsedVerticalMargin::NonCollapsedValues { computedVerticalMargin.before.valueOr(0), computedVerticalMargin.after.valueOr(0) };
+        auto precomputedMarginBefore = marginCollapse().precomputedMarginBefore(*ancestor, usedNonCollapsedMargin);
+
+        auto& boxGeometry = formattingState().boxGeometry(*ancestor);
+        auto nonCollapsedValues = UsedVerticalMargin::NonCollapsedValues { precomputedMarginBefore.nonCollapsedValue, { } };
+        auto collapsedValues = UsedVerticalMargin::CollapsedValues { precomputedMarginBefore.collapsedValue, { }, false };
+        auto verticalMargin = UsedVerticalMargin { nonCollapsedValues, collapsedValues, { precomputedMarginBefore.positiveAndNegativeMarginBefore, { } } };
+
+        formattingState().setUsedVerticalMargin(*ancestor, verticalMargin);
+        boxGeometry.setVerticalMargin({ marginBefore(verticalMargin), marginAfter(verticalMargin) });
+        boxGeometry.setLogicalTop(verticalPositionWithMargin(*ancestor, verticalMargin, constraintsForAncestor.vertical));
+#if ASSERT_ENABLED
+        setPrecomputedMarginBefore(*ancestor, precomputedMarginBefore);
+        boxGeometry.setHasPrecomputedMarginBefore();
+#endif
+    }
+}
+
+void BlockFormattingContext::computePositionToAvoidFloats(const FloatingContext& floatingContext, const Box& layoutBox, const ConstraintsPair& constraintsPair)
+{
+    if (!layoutBox.isFloatAvoider())
+        return;
+    // In order to position a float avoider we need to know its vertical position relative to its formatting context root (and not just its containing block),
+    // because all the already-placed floats (floats that we are trying to avoid here) in this BFC might belong
+    // to a different set of containing blocks (but they all descendants of the BFC root).
+    // However according to the BFC rules, at this point of the layout flow we don't yet have computed vertical positions for the ancestors.
+    if (layoutBox.isFloatingPositioned()) {
+        precomputeVerticalPositionForBoxAndAncestors(layoutBox, constraintsPair);
+        formattingState().boxGeometry(layoutBox).setLogicalTopLeft(floatingContext.positionForFloat(layoutBox, constraintsPair.containingBlock.horizontal));
+        return;
+    }
+    // Non-float positioned float avoiders (formatting context roots and clear boxes) should be fine unless there are floats in this context.
+    if (floatingContext.isEmpty())
+        return;
+    precomputeVerticalPositionForBoxAndAncestors(layoutBox, constraintsPair);
+    if (layoutBox.hasFloatClear())
+        return computeVerticalPositionForFloatClear(floatingContext, layoutBox);
+
+    ASSERT(layoutBox.establishesFormattingContext());
+    formattingState().boxGeometry(layoutBox).setLogicalTopLeft(floatingContext.positionForNonFloatingFloatAvoider(layoutBox, constraintsPair.containingBlock.horizontal));
+}
+
+void BlockFormattingContext::computeVerticalPositionForFloatClear(const FloatingContext& floatingContext, const Box& layoutBox)
+{
+    ASSERT(layoutBox.hasFloatClear());
+    if (floatingContext.isEmpty())
+        return;
+    auto verticalPositionAndClearance = floatingContext.verticalPositionWithClearance(layoutBox);
+    if (!verticalPositionAndClearance)
+        return;
+
+    auto& boxGeometry = formattingState().boxGeometry(layoutBox);
+    ASSERT(verticalPositionAndClearance->position >= BoxGeometry::borderBoxTop(boxGeometry));
+    boxGeometry.setLogicalTop(verticalPositionAndClearance->position);
+    if (verticalPositionAndClearance->clearance)
+        formattingState().setHasClearance(layoutBox);
+    // FIXME: Reset the margin values on the ancestors/previous siblings now that the float avoider with clearance does not margin collapse anymore.
+}
+
+void BlockFormattingContext::computeWidthAndMargin(const FloatingContext& floatingContext, const Box& layoutBox, const ConstraintsPair& constraintsPair)
+{
+    auto availableWidthFloatAvoider = Optional<LayoutUnit> { };
+    if (layoutBox.isFloatAvoider()) {
+        // Float avoiders' available width might be shrunk by existing floats in the context.
+        availableWidthFloatAvoider = usedAvailableWidthForFloatAvoider(floatingContext, layoutBox, constraintsPair);
+    }
+    auto contentWidthAndMargin = geometry().computedContentWidthAndMargin(layoutBox, constraintsPair.containingBlock.horizontal, availableWidthFloatAvoider);
+    auto& boxGeometry = formattingState().boxGeometry(layoutBox);
+    boxGeometry.setContentBoxWidth(contentWidthAndMargin.contentWidth);
+    boxGeometry.setHorizontalMargin({ contentWidthAndMargin.usedMargin.start, contentWidthAndMargin.usedMargin.end });
+}
+
+void BlockFormattingContext::computeHeightAndMargin(const Box& layoutBox, const ConstraintsForInFlowContent& constraints)
+{
+    auto compute = [&](Optional<LayoutUnit> usedHeight) -> ContentHeightAndMargin {
+        if (layoutBox.isInFlow())
+            return geometry().inFlowContentHeightAndMargin(layoutBox, constraints.horizontal, { usedHeight });
+
+        if (layoutBox.isFloatingPositioned())
+            return geometry().floatingContentHeightAndMargin(layoutBox, constraints.horizontal, { usedHeight });
+
+        ASSERT_NOT_REACHED();
+        return { };
+    };
+
+    auto contentHeightAndMargin = compute({ });
+    if (auto maxHeight = geometry().computedMaxHeight(layoutBox)) {
+        if (contentHeightAndMargin.contentHeight > *maxHeight) {
+            auto maxHeightAndMargin = compute(maxHeight);
+            // Used height should remain the same.
+            ASSERT((layoutState().inQuirksMode() && (layoutBox.isBodyBox() || layoutBox.isDocumentBox())) || maxHeightAndMargin.contentHeight == *maxHeight);
+            contentHeightAndMargin = { *maxHeight, maxHeightAndMargin.nonCollapsedMargin };
+        }
+    }
+
+    if (auto minHeight = geometry().computedMinHeight(layoutBox)) {
+        if (contentHeightAndMargin.contentHeight < *minHeight) {
+            auto minHeightAndMargin = compute(minHeight);
+            // Used height should remain the same.
+            ASSERT((layoutState().inQuirksMode() && (layoutBox.isBodyBox() || layoutBox.isDocumentBox())) || minHeightAndMargin.contentHeight == *minHeight);
+            contentHeightAndMargin = { *minHeight, minHeightAndMargin.nonCollapsedMargin };
+        }
+    }
+
+    // 1. Compute collapsed margins.
+    // 2. Adjust vertical position using the collapsed values.
+    // 3. Adjust previous in-flow sibling margin after using this margin.
+    auto marginCollapse = this->marginCollapse();
+    auto verticalMargin = marginCollapse.collapsedVerticalValues(layoutBox, contentHeightAndMargin.nonCollapsedMargin);
+    // Cache the computed positive and negative margin value pair.
+    formattingState().setUsedVerticalMargin(layoutBox, verticalMargin);
+
+#if ASSERT_ENABLED
+    if (hasPrecomputedMarginBefore(layoutBox) && precomputedMarginBefore(layoutBox).usedValue() != marginBefore(verticalMargin)) {
+        // When the pre-computed margin turns out to be incorrect, we need to re-layout this subtree with the correct margin values.
+        // <div style="float: left"></div>
+        // <div>
+        //   <div style="margin-bottom: 200px"></div>
+        // </div>
+        // The float box triggers margin before computation on the ancestor chain to be able to intersect with other floats in the same floating context.
+        // However in some cases the parent margin-top collapses with some next siblings (nephews) and there's no way to be able to properly
+        // account for that without laying out every node in the FC (in the example, the margin-bottom pushes down the float).
+        ASSERT_NOT_IMPLEMENTED_YET();
+    }
+#endif
+    auto& boxGeometry = formattingState().boxGeometry(layoutBox);
+    boxGeometry.setContentBoxHeight(contentHeightAndMargin.contentHeight);
+    boxGeometry.setVerticalMargin({ marginBefore(verticalMargin), marginAfter(verticalMargin) });
+    // Adjust the previous sibling's margin bottom now that this box's vertical margin is computed.
+    MarginCollapse::updateMarginAfterForPreviousSibling(*this, marginCollapse, layoutBox);
+}
+
+FormattingContext::IntrinsicWidthConstraints BlockFormattingContext::computedIntrinsicWidthConstraints()
+{
+    auto& formattingState = this->formattingState();
+    ASSERT(!formattingState.intrinsicWidthConstraints());
+    // Visit the in-flow descendants and compute their min/max intrinsic width if needed.
+    // 1. Go all the way down to the leaf node
+    // 2. Check if actually need to visit all the boxes as we traverse down (already computed, container's min/max does not depend on descendants etc)
+    // 3. As we climb back on the tree, compute min/max intrinsic width
+    // (Any subtrees with new formatting contexts need to layout synchronously)
+    Vector<const Box*> queue;
+    if (root().hasInFlowOrFloatingChild())
+        queue.append(root().firstInFlowOrFloatingChild());
+
+    IntrinsicWidthConstraints constraints;
+    auto maximumHorizontalStackingWidth = LayoutUnit { };
+    auto currentHorizontalStackingWidth = LayoutUnit { };
+    while (!queue.isEmpty()) {
+        while (true) {
+            // Check if we have to deal with descendant content.
+            auto& layoutBox = *queue.last();
+            // Float avoiders are all establish a new formatting context. No need to look inside them.
+            if (layoutBox.isFloatAvoider() && !layoutBox.hasFloatClear())
+                break;
+            // Non-floating block level boxes reset the current horizontal float stacking.
+            // SPEC: This is a bit odd as floating positioning is a formatting context level concept:
+            // e.g.
+            // <div style="float: left; width: 10px;"></div>
+            // <div></div>
+            // <div style="float: left; width: 40px;"></div>
+            // ...will produce a max width of 40px which makes the floats vertically stacked.
+            // Vertically stacked floats makes me think we haven't managed to provide the maximum preferred width for the content.
+            maximumHorizontalStackingWidth = std::max(currentHorizontalStackingWidth, maximumHorizontalStackingWidth);
+            currentHorizontalStackingWidth = { };
+            // Already has computed intrinsic constraints.
+            if (formattingState.intrinsicWidthConstraintsForBox(layoutBox))
+                break;
+            // Box with fixed width defines their descendant content intrinsic width.
+            if (layoutBox.style().width().isFixed())
+                break;
+            // Non-float avoider formatting context roots are opaque to intrinsic width computation.
+            if (layoutBox.establishesFormattingContext())
+                break;
+            // No relevant child content.
+            if (!is<ContainerBox>(layoutBox) || !downcast<ContainerBox>(layoutBox).hasInFlowOrFloatingChild())
+                break;
+            queue.append(downcast<ContainerBox>(layoutBox).firstInFlowOrFloatingChild());
+        }
+        // Compute min/max intrinsic width bottom up if needed.
+        while (!queue.isEmpty()) {
+            auto& layoutBox = *queue.takeLast();
+            auto desdendantConstraints = formattingState.intrinsicWidthConstraintsForBox(layoutBox);
+            if (!desdendantConstraints) {
+                desdendantConstraints = geometry().intrinsicWidthConstraints(layoutBox);
+                formattingState.setIntrinsicWidthConstraintsForBox(layoutBox, *desdendantConstraints);
+            }
+            constraints.minimum = std::max(constraints.minimum, desdendantConstraints->minimum);
+            auto willStackHorizontally = layoutBox.isFloatAvoider() && !layoutBox.hasFloatClear();
+            if (willStackHorizontally)
+                currentHorizontalStackingWidth += desdendantConstraints->maximum;
+            else
+                constraints.maximum = std::max(constraints.maximum, desdendantConstraints->maximum);
+            // Move over to the next sibling or take the next box in the queue.
+            if (auto* nextSibling = layoutBox.nextInFlowOrFloatingSibling()) {
+                queue.append(nextSibling);
+                break;
+            }
+        }
+    }
+    maximumHorizontalStackingWidth = std::max(currentHorizontalStackingWidth, maximumHorizontalStackingWidth);
+    constraints.maximum = std::max(constraints.maximum, maximumHorizontalStackingWidth);
+    formattingState.setIntrinsicWidthConstraints(constraints);
+    return constraints;
+}
+
+LayoutUnit BlockFormattingContext::verticalPositionWithMargin(const Box& layoutBox, const UsedVerticalMargin& verticalMargin,  const VerticalConstraints& verticalConstraints) const
+{
+    ASSERT(!layoutBox.isOutOfFlowPositioned());
+    // Now that we've computed the final margin before, let's shift the box's vertical position if needed.
+    // 1. Check if the box has clearance. If so, we've already precomputed/finalized the top value and vertical margin does not impact it anymore.
+    // 2. Check if the margin before collapses with the previous box's margin after. if not -> return previous box's bottom including margin after + marginBefore
+    // 3. Check if the previous box's margins collapse through. If not -> return previous box' bottom excluding margin after + marginBefore (they are supposed to be equal)
+    // 4. Go to previous box and start from step #1 until we hit the parent box.
+    auto& boxGeometry = geometryForBox(layoutBox);
+    if (formattingState().hasClearance(layoutBox))
+        return BoxGeometry::borderBoxTop(boxGeometry);
+
+    auto* currentLayoutBox = &layoutBox;
+    while (currentLayoutBox) {
+        if (!currentLayoutBox->previousInFlowSibling())
+            break;
+        auto& previousInFlowSibling = *currentLayoutBox->previousInFlowSibling();
+        if (!marginCollapse().marginBeforeCollapsesWithPreviousSiblingMarginAfter(*currentLayoutBox)) {
+            auto& previousBoxGeometry = geometryForBox(previousInFlowSibling);
+            return BoxGeometry::marginBoxRect(previousBoxGeometry).bottom() + marginBefore(verticalMargin);
+        }
+
+        if (!marginCollapse().marginsCollapseThrough(previousInFlowSibling)) {
+            auto& previousBoxGeometry = geometryForBox(previousInFlowSibling);
+            return BoxGeometry::borderBoxRect(previousBoxGeometry).bottom() + marginBefore(verticalMargin);
+        }
+        currentLayoutBox = &previousInFlowSibling;
+    }
+
+    auto containingBlockContentBoxTop = verticalConstraints.logicalTop;
+    // Adjust vertical position depending whether this box directly or indirectly adjoins with its parent.
+    auto directlyAdjoinsParent = !layoutBox.previousInFlowSibling();
+    if (directlyAdjoinsParent) {
+        // If the top and bottom margins of a box are adjoining, then it is possible for margins to collapse through it.
+        // In this case, the position of the element depends on its relationship with the other elements whose margins are being collapsed.
+        if (verticalMargin.collapsedValues.isCollapsedThrough) {
+            // If the element's margins are collapsed with its parent's top margin, the top border edge of the box is defined to be the same as the parent's.
+            if (marginCollapse().marginBeforeCollapsesWithParentMarginBefore(layoutBox))
+                return containingBlockContentBoxTop;
+            // Otherwise, either the element's parent is not taking part in the margin collapsing, or only the parent's bottom margin is involved.
+            // The position of the element's top border edge is the same as it would have been if the element had a non-zero bottom border.
+            auto beforeMarginWithBottomBorder = marginCollapse().marginBeforeIgnoringCollapsingThrough(layoutBox, verticalMargin.nonCollapsedValues);
+            return containingBlockContentBoxTop + beforeMarginWithBottomBorder;
+        }
+        // Non-collapsed through box vertical position depending whether the margin collapses.
+        if (marginCollapse().marginBeforeCollapsesWithParentMarginBefore(layoutBox))
+            return containingBlockContentBoxTop;
+
+        return containingBlockContentBoxTop + marginBefore(verticalMargin);
+    }
+    // At this point this box indirectly (via collapsed through previous in-flow siblings) adjoins the parent. Let's check if it margin collapses with the parent.
+    auto& containingBlock = layoutBox.containingBlock();
+    ASSERT(containingBlock.firstInFlowChild());
+    ASSERT(containingBlock.firstInFlowChild() != &layoutBox);
+    if (marginCollapse().marginBeforeCollapsesWithParentMarginBefore(*containingBlock.firstInFlowChild()))
+        return containingBlockContentBoxTop;
+
+    return containingBlockContentBoxTop + marginBefore(verticalMargin);
+}
+
+}
+}
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsblockBlockFormattingContexthfromrev276885trunkSourceWebCorelayoutformattingContextsblockformattingBlockFormattingContexth"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingContext.h (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/blockformatting/BlockFormattingContext.h) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingContext.h                            (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingContext.h       2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,198 @@
</span><ins>+/*
+ * Copyright (C) 2018 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 "BlockFormattingState.h"
+#include "FormattingContext.h"
+#include <wtf/HashMap.h>
+#include <wtf/IsoMalloc.h>
+
+namespace WebCore {
+
+class LayoutUnit;
+
+namespace Layout {
+
+class Box;
+class FloatingContext;
+
+// This class implements the layout logic for block formatting contexts.
+// https://www.w3.org/TR/CSS22/visuren.html#block-formatting
+class BlockFormattingContext : public FormattingContext {
+    WTF_MAKE_ISO_ALLOCATED(BlockFormattingContext);
+public:
+    BlockFormattingContext(const ContainerBox& formattingContextRoot, BlockFormattingState&);
+
+    void layoutInFlowContent(InvalidationState&, const ConstraintsForInFlowContent&) override;
+    LayoutUnit usedContentHeight() const override;
+
+protected:
+    struct ConstraintsPair {
+        ConstraintsForInFlowContent formattingContextRoot;
+        ConstraintsForInFlowContent containingBlock;
+    };
+    void placeInFlowPositionedChildren(const ContainerBox&, const HorizontalConstraints&);
+
+    void computeWidthAndMargin(const FloatingContext&, const Box&, const ConstraintsPair&);
+    void computeHeightAndMargin(const Box&, const ConstraintsForInFlowContent&);
+
+    void computeStaticHorizontalPosition(const Box&, const HorizontalConstraints&);
+    void computeStaticVerticalPosition(const Box&, const VerticalConstraints&);
+    void computePositionToAvoidFloats(const FloatingContext&, const Box&, const ConstraintsPair&);
+    void computeVerticalPositionForFloatClear(const FloatingContext&, const Box&);
+
+    void precomputeVerticalPositionForBoxAndAncestors(const Box&, const ConstraintsPair&);
+
+    IntrinsicWidthConstraints computedIntrinsicWidthConstraints() override;
+
+    LayoutUnit verticalPositionWithMargin(const Box&, const UsedVerticalMargin&, const VerticalConstraints&) const;
+
+    // This class implements positioning and sizing for boxes participating in a block formatting context.
+    class Geometry : public FormattingContext::Geometry {
+    public:
+        Geometry(const BlockFormattingContext&);
+
+        ContentHeightAndMargin inFlowContentHeightAndMargin(const Box&, const HorizontalConstraints&, const OverriddenVerticalValues&);
+        ContentWidthAndMargin inFlowContentWidthAndMargin(const Box&, const HorizontalConstraints&, const OverriddenHorizontalValues&);
+
+        Point staticPosition(const Box&, const HorizontalConstraints&, const VerticalConstraints&) const;
+        LayoutUnit staticVerticalPosition(const Box&, const VerticalConstraints&) const;
+        LayoutUnit staticHorizontalPosition(const Box&, const HorizontalConstraints&) const;
+
+        IntrinsicWidthConstraints intrinsicWidthConstraints(const Box&);
+
+        ContentWidthAndMargin computedContentWidthAndMargin(const Box&, const HorizontalConstraints&, Optional<LayoutUnit> availableWidthFloatAvoider);
+
+    private:
+        ContentHeightAndMargin inFlowNonReplacedContentHeightAndMargin(const Box&, const HorizontalConstraints&, const OverriddenVerticalValues&);
+        ContentWidthAndMargin inFlowNonReplacedContentWidthAndMargin(const Box&, const HorizontalConstraints&, const OverriddenHorizontalValues&);
+        ContentWidthAndMargin inFlowReplacedContentWidthAndMargin(const ReplacedBox&, const HorizontalConstraints&, const OverriddenHorizontalValues&);
+
+        const BlockFormattingContext& formattingContext() const { return downcast<BlockFormattingContext>(FormattingContext::Geometry::formattingContext()); }
+    };
+    BlockFormattingContext::Geometry geometry() const { return Geometry(*this); }
+
+    // This class implements margin collapsing for block formatting context.
+    class MarginCollapse {
+    public:
+        UsedVerticalMargin collapsedVerticalValues(const Box&, UsedVerticalMargin::NonCollapsedValues);
+
+        PrecomputedMarginBefore precomputedMarginBefore(const Box&, UsedVerticalMargin::NonCollapsedValues);
+        LayoutUnit marginBeforeIgnoringCollapsingThrough(const Box&, UsedVerticalMargin::NonCollapsedValues);
+        static void updateMarginAfterForPreviousSibling(BlockFormattingContext&, const MarginCollapse&, const Box&);
+
+        bool marginBeforeCollapsesWithParentMarginBefore(const Box&) const;
+        bool marginBeforeCollapsesWithFirstInFlowChildMarginBefore(const Box&) const;
+        bool marginBeforeCollapsesWithParentMarginAfter(const Box&) const;
+        bool marginBeforeCollapsesWithPreviousSiblingMarginAfter(const Box&) const;
+
+        bool marginAfterCollapsesWithParentMarginAfter(const Box&) const;
+        bool marginAfterCollapsesWithLastInFlowChildMarginAfter(const Box&) const;
+        bool marginAfterCollapsesWithParentMarginBefore(const Box&) const;
+        bool marginAfterCollapsesWithNextSiblingMarginBefore(const Box&) const;
+        bool marginAfterCollapsesWithSiblingMarginBeforeWithClearance(const Box&) const;
+
+        bool marginsCollapseThrough(const Box&) const;
+
+    private:
+        friend class BlockFormattingContext;
+        MarginCollapse(const BlockFormattingContext&);
+
+        enum class MarginType { Before, After };
+        UsedVerticalMargin::PositiveAndNegativePair::Values positiveNegativeValues(const Box&, MarginType) const;
+        UsedVerticalMargin::PositiveAndNegativePair::Values positiveNegativeMarginBefore(const Box&, UsedVerticalMargin::NonCollapsedValues) const;
+        UsedVerticalMargin::PositiveAndNegativePair::Values positiveNegativeMarginAfter(const Box&, UsedVerticalMargin::NonCollapsedValues) const;
+
+        UsedVerticalMargin::PositiveAndNegativePair::Values precomputedPositiveNegativeMarginBefore(const Box&, UsedVerticalMargin::NonCollapsedValues) const;
+        UsedVerticalMargin::PositiveAndNegativePair::Values precomputedPositiveNegativeValues(const Box&) const;
+
+        UsedVerticalMargin::PositiveAndNegativePair::Values computedPositiveAndNegativeMargin(UsedVerticalMargin::PositiveAndNegativePair::Values, UsedVerticalMargin::PositiveAndNegativePair::Values) const;
+        Optional<LayoutUnit> marginValue(UsedVerticalMargin::PositiveAndNegativePair::Values) const;
+
+        bool hasClearance(const Box&) const;
+
+        LayoutState& layoutState() { return m_blockFormattingContext.layoutState(); }
+        const LayoutState& layoutState() const { return m_blockFormattingContext.layoutState(); }
+        const BlockFormattingContext& formattingContext() const { return m_blockFormattingContext; }
+
+        const BlockFormattingContext& m_blockFormattingContext;
+    };
+    MarginCollapse marginCollapse() const { return MarginCollapse(*this); }
+
+    class Quirks : public FormattingContext::Quirks {
+    public:
+        Quirks(const BlockFormattingContext&);
+
+        bool needsStretching(const Box&) const;
+        LayoutUnit stretchedInFlowHeight(const Box&, ContentHeightAndMargin);
+
+        bool shouldIgnoreCollapsedQuirkMargin(const Box&) const;
+        bool shouldCollapseMarginBeforeWithParentMarginBefore(const Box&) const;
+        bool shouldCollapseMarginAfterWithParentMarginAfter(const Box&) const;
+
+        const BlockFormattingContext& formattingContext() const { return downcast<BlockFormattingContext>(FormattingContext::Quirks::formattingContext()); }
+        BlockFormattingContext::Geometry geometry() const { return formattingContext().geometry(); }
+
+    };
+    BlockFormattingContext::Quirks quirks() const { return Quirks(*this); }
+
+    Optional<LayoutUnit> usedAvailableWidthForFloatAvoider(const FloatingContext&, const Box&, const ConstraintsPair&);
+
+    const BlockFormattingState& formattingState() const { return downcast<BlockFormattingState>(FormattingContext::formattingState()); }
+    BlockFormattingState& formattingState() { return downcast<BlockFormattingState>(FormattingContext::formattingState()); }
+
+#if ASSERT_ENABLED
+    void setPrecomputedMarginBefore(const Box& layoutBox, const PrecomputedMarginBefore& precomputedMarginBefore) { m_precomputedMarginBeforeList.set(&layoutBox, precomputedMarginBefore); }
+    PrecomputedMarginBefore precomputedMarginBefore(const Box& layoutBox) const { return m_precomputedMarginBeforeList.get(&layoutBox); }
+    bool hasPrecomputedMarginBefore(const Box& layoutBox) const { return m_precomputedMarginBeforeList.contains(&layoutBox); }
+#endif
+private:
+    HashMap<const Box*, PrecomputedMarginBefore> m_precomputedMarginBeforeList;
+};
+
+inline BlockFormattingContext::Geometry::Geometry(const BlockFormattingContext& blockFormattingContext)
+    : FormattingContext::Geometry(blockFormattingContext)
+{
+}
+
+inline BlockFormattingContext::Quirks::Quirks(const BlockFormattingContext& blockFormattingContext)
+    : FormattingContext::Quirks(blockFormattingContext)
+{
+}
+
+inline BlockFormattingContext::MarginCollapse::MarginCollapse(const BlockFormattingContext& blockFormattingContext)
+    : m_blockFormattingContext(blockFormattingContext)
+{
+}
+
+}
+}
+
+SPECIALIZE_TYPE_TRAITS_LAYOUT_FORMATTING_CONTEXT(BlockFormattingContext, isBlockFormattingContext())
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsblockBlockFormattingContextGeometrycppfromrev276885trunkSourceWebCorelayoutformattingContextsblockformattingBlockFormattingContextGeometrycpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingContextGeometry.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/blockformatting/BlockFormattingContextGeometry.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingContextGeometry.cpp                          (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingContextGeometry.cpp     2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,401 @@
</span><ins>+/*
+ * Copyright (C) 2018 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 "BlockFormattingContext.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "BlockFormattingState.h"
+#include "FormattingContext.h"
+#include "InlineFormattingState.h"
+#include "LayoutBoxGeometry.h"
+#include "LayoutChildIterator.h"
+#include "LayoutContext.h"
+#include "LayoutInitialContainingBlock.h"
+#include "LayoutReplacedBox.h"
+#include "Logging.h"
+#include <wtf/text/TextStream.h>
+
+namespace WebCore {
+namespace Layout {
+
+ContentHeightAndMargin BlockFormattingContext::Geometry::inFlowNonReplacedContentHeightAndMargin(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints, const OverriddenVerticalValues& overriddenVerticalValues)
+{
+    ASSERT(layoutBox.isInFlow() && !layoutBox.isReplacedBox());
+    ASSERT(layoutBox.isOverflowVisible());
+
+    auto compute = [&](const auto& overriddenVerticalValues) -> ContentHeightAndMargin {
+
+        // 10.6.3 Block-level non-replaced elements in normal flow when 'overflow' computes to 'visible'
+        //
+        // If 'margin-top', or 'margin-bottom' are 'auto', their used value is 0.
+        // If 'height' is 'auto', the height depends on whether the element has any block-level children and whether it has padding or borders:
+        // The element's height is the distance from its top content edge to the first applicable of the following:
+        // 1. the bottom edge of the last line box, if the box establishes a inline formatting context with one or more lines
+        // 2. the bottom edge of the bottom (possibly collapsed) margin of its last in-flow child, if the child's bottom margin
+        //    does not collapse with the element's bottom margin
+        // 3. the bottom border edge of the last in-flow child whose top margin doesn't collapse with the element's bottom margin
+        // 4. zero, otherwise
+        // Only children in the normal flow are taken into account (i.e., floating boxes and absolutely positioned boxes are ignored,
+        // and relatively positioned boxes are considered without their offset). Note that the child box may be an anonymous block box.
+
+        auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
+        auto computedVerticalMargin = Geometry::computedVerticalMargin(layoutBox, horizontalConstraints);
+        auto nonCollapsedMargin = UsedVerticalMargin::NonCollapsedValues { computedVerticalMargin.before.valueOr(0), computedVerticalMargin.after.valueOr(0) }; 
+        auto borderAndPaddingTop = boxGeometry.borderTop() + boxGeometry.paddingTop().valueOr(0);
+        auto height = overriddenVerticalValues.height ? overriddenVerticalValues.height.value() : computedHeight(layoutBox);
+
+        if (height)
+            return { *height, nonCollapsedMargin };
+
+        if (!is<ContainerBox>(layoutBox) || !downcast<ContainerBox>(layoutBox).hasInFlowChild())
+            return { 0, nonCollapsedMargin };
+
+        // 1. the bottom edge of the last line box, if the box establishes a inline formatting context with one or more lines
+        auto& layoutContainer = downcast<ContainerBox>(layoutBox);
+        if (layoutContainer.establishesInlineFormattingContext()) {
+            auto& inlineFormattingState = layoutState().establishedInlineFormattingState(layoutContainer);
+            auto& lines = inlineFormattingState.lines();
+            // Even empty containers generate one line. 
+            ASSERT(!lines.isEmpty());
+            return { toLayoutUnit(lines.last().lineBoxLogicalRect().bottom() + inlineFormattingState.clearGapAfterLastLine()) - borderAndPaddingTop, nonCollapsedMargin };
+        }
+
+        // 2. the bottom edge of the bottom (possibly collapsed) margin of its last in-flow child, if the child's bottom margin...
+        auto& lastInFlowChild = *layoutContainer.lastInFlowChild();
+        if (!formattingContext().marginCollapse().marginAfterCollapsesWithParentMarginAfter(lastInFlowChild)) {
+            auto& lastInFlowBoxGeometry = formattingContext().geometryForBox(lastInFlowChild);
+            auto bottomEdgeOfBottomMargin = BoxGeometry::borderBoxRect(lastInFlowBoxGeometry).bottom() + lastInFlowBoxGeometry.marginAfter();
+            return { bottomEdgeOfBottomMargin - borderAndPaddingTop, nonCollapsedMargin };
+        }
+
+        // 3. the bottom border edge of the last in-flow child whose top margin doesn't collapse with the element's bottom margin
+        auto* inFlowChild = &lastInFlowChild;
+        while (inFlowChild && formattingContext().marginCollapse().marginBeforeCollapsesWithParentMarginAfter(*inFlowChild))
+            inFlowChild = inFlowChild->previousInFlowSibling();
+        if (inFlowChild) {
+            auto& inFlowBoxGeometry = formattingContext().geometryForBox(*inFlowChild);
+            return { BoxGeometry::borderBoxTop(inFlowBoxGeometry) + inFlowBoxGeometry.borderBox().height() - borderAndPaddingTop, nonCollapsedMargin };
+        }
+
+        // 4. zero, otherwise
+        return { 0, nonCollapsedMargin };
+    };
+
+    // 10.6.7 'Auto' heights for block-level formatting context boxes.
+    auto isAutoHeight = !overriddenVerticalValues.height && !computedHeight(layoutBox);
+    if (isAutoHeight && (layoutBox.establishesFormattingContext() && !layoutBox.establishesInlineFormattingContext()))
+        return compute( OverriddenVerticalValues { contentHeightForFormattingContextRoot(downcast<ContainerBox>(layoutBox)) });
+    return compute(overriddenVerticalValues);
+}
+
+ContentWidthAndMargin BlockFormattingContext::Geometry::inFlowNonReplacedContentWidthAndMargin(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints, const OverriddenHorizontalValues& overriddenHorizontalValues)
+{
+    ASSERT(layoutBox.isInFlow());
+
+    auto compute = [&]() {
+
+        // 10.3.3 Block-level, non-replaced elements in normal flow
+        //
+        // The following constraints must hold among the used values of the other properties:
+        // 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' = width of containing block
+        //
+        // 1. If 'width' is not 'auto' and 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' 
+        //    (plus any of 'margin-left' or 'margin-right' that are not 'auto') is larger than the width of the containing block, then
+        //    any 'auto' values for 'margin-left' or 'margin-right' are, for the following rules, treated as zero.
+        //
+        // 2. If all of the above have a computed value other than 'auto', the values are said to be "over-constrained" and one of the used values will
+        //    have to be different from its computed value. If the 'direction' property of the containing block has the value 'ltr', the specified value
+        //    of 'margin-right' is ignored and the value is calculated so as to make the equality true. If the value of 'direction' is 'rtl',
+        //    this happens to 'margin-left' instead.
+        //
+        // 3. If there is exactly one value specified as 'auto', its used value follows from the equality.
+        //
+        // 4. If 'width' is set to 'auto', any other 'auto' values become '0' and 'width' follows from the resulting equality.
+        //
+        // 5. If both 'margin-left' and 'margin-right' are 'auto', their used values are equal. This horizontally centers the element with respect to the
+        //    edges of the containing block.
+
+        auto containingBlockWidth = horizontalConstraints.logicalWidth;
+        auto& containingBlockStyle = layoutBox.containingBlock().style();
+        auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
+
+        auto width = overriddenHorizontalValues.width ? overriddenHorizontalValues.width : computedWidth(layoutBox, containingBlockWidth);
+        auto computedHorizontalMargin = Geometry::computedHorizontalMargin(layoutBox, horizontalConstraints);
+        UsedHorizontalMargin usedHorizontalMargin;
+        auto borderLeft = boxGeometry.borderLeft();
+        auto borderRight = boxGeometry.borderRight();
+        auto paddingLeft = boxGeometry.paddingLeft().valueOr(0);
+        auto paddingRight = boxGeometry.paddingRight().valueOr(0);
+
+        // #1
+        if (width) {
+            auto horizontalSpaceForMargin = containingBlockWidth - (computedHorizontalMargin.start.valueOr(0) + borderLeft + paddingLeft + *width + paddingRight + borderRight + computedHorizontalMargin.end.valueOr(0));
+            if (horizontalSpaceForMargin < 0)
+                usedHorizontalMargin = { computedHorizontalMargin.start.valueOr(0), computedHorizontalMargin.end.valueOr(0) };
+        }
+
+        // #2
+        if (width && computedHorizontalMargin.start && computedHorizontalMargin.end) {
+            if (containingBlockStyle.isLeftToRightDirection()) {
+                usedHorizontalMargin.start = *computedHorizontalMargin.start;
+                usedHorizontalMargin.end = containingBlockWidth - (usedHorizontalMargin.start + borderLeft + paddingLeft + *width + paddingRight + borderRight);
+            } else {
+                usedHorizontalMargin.end = *computedHorizontalMargin.end;
+                usedHorizontalMargin.start = containingBlockWidth - (borderLeft + paddingLeft + *width + paddingRight + borderRight + usedHorizontalMargin.end);
+            }
+        }
+
+        // #3
+        if (!computedHorizontalMargin.start && width && computedHorizontalMargin.end) {
+            usedHorizontalMargin.end = *computedHorizontalMargin.end;
+            usedHorizontalMargin.start = containingBlockWidth - (borderLeft + paddingLeft  + *width + paddingRight + borderRight + usedHorizontalMargin.end);
+        } else if (computedHorizontalMargin.start && !width && computedHorizontalMargin.end) {
+            usedHorizontalMargin = { *computedHorizontalMargin.start, *computedHorizontalMargin.end };
+            width = containingBlockWidth - (usedHorizontalMargin.start + borderLeft + paddingLeft + paddingRight + borderRight + usedHorizontalMargin.end);
+        } else if (computedHorizontalMargin.start && width && !computedHorizontalMargin.end) {
+            usedHorizontalMargin.start = *computedHorizontalMargin.start;
+            usedHorizontalMargin.end = containingBlockWidth - (usedHorizontalMargin.start + borderLeft + paddingLeft + *width + paddingRight + borderRight);
+        }
+
+        // #4
+        if (!width) {
+            usedHorizontalMargin = { computedHorizontalMargin.start.valueOr(0), computedHorizontalMargin.end.valueOr(0) };
+            width = containingBlockWidth - (usedHorizontalMargin.start + borderLeft + paddingLeft + paddingRight + borderRight + usedHorizontalMargin.end);
+        }
+
+        // #5
+        if (!computedHorizontalMargin.start && !computedHorizontalMargin.end) {
+            auto horizontalSpaceForMargin = containingBlockWidth - (borderLeft + paddingLeft  + *width + paddingRight + borderRight);
+            usedHorizontalMargin = { horizontalSpaceForMargin / 2, horizontalSpaceForMargin / 2 };
+        }
+
+        auto shouldApplyCenterAlignForBlockContent = containingBlockStyle.textAlign() == TextAlignMode::WebKitCenter && (computedHorizontalMargin.start || computedHorizontalMargin.end);
+        if (shouldApplyCenterAlignForBlockContent) {
+            auto borderBoxWidth = (borderLeft + paddingLeft  + *width + paddingRight + borderRight);
+            auto marginStart = computedHorizontalMargin.start.valueOr(0);
+            auto marginEnd = computedHorizontalMargin.end.valueOr(0);
+            auto centeredLogicalLeftForMarginBox = std::max((containingBlockWidth - borderBoxWidth - marginStart - marginEnd) / 2, 0_lu);
+            usedHorizontalMargin.start = centeredLogicalLeftForMarginBox + marginStart;
+            usedHorizontalMargin.end = containingBlockWidth - borderBoxWidth - marginStart + marginEnd;
+        }
+        ASSERT(width);
+
+        return ContentWidthAndMargin { *width, usedHorizontalMargin };
+    };
+
+    auto contentWidthAndMargin = compute();
+    LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width][Margin] -> inflow non-replaced -> width(" << contentWidthAndMargin.contentWidth << "px) margin(" << contentWidthAndMargin.usedMargin.start << "px, " << contentWidthAndMargin.usedMargin.end << "px) -> layoutBox(" << &layoutBox << ")");
+    return contentWidthAndMargin;
+}
+
+ContentWidthAndMargin BlockFormattingContext::Geometry::inFlowReplacedContentWidthAndMargin(const ReplacedBox& replacedBox, const HorizontalConstraints& horizontalConstraints, const OverriddenHorizontalValues& overriddenHorizontalValues)
+{
+    ASSERT(replacedBox.isInFlow());
+
+    // 10.3.4 Block-level, replaced elements in normal flow
+    //
+    // 1. The used value of 'width' is determined as for inline replaced elements.
+    // 2. Then the rules for non-replaced block-level elements are applied to determine the margins.
+
+    // #1
+    auto usedWidth = inlineReplacedContentWidthAndMargin(replacedBox, horizontalConstraints, { }, overriddenHorizontalValues).contentWidth;
+    // #2
+    auto nonReplacedWidthAndMargin = inFlowNonReplacedContentWidthAndMargin(replacedBox, horizontalConstraints, OverriddenHorizontalValues { usedWidth, overriddenHorizontalValues.margin });
+
+    LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width][Margin] -> inflow replaced -> width(" << usedWidth  << "px) margin(" << nonReplacedWidthAndMargin.usedMargin.start << "px, " << nonReplacedWidthAndMargin.usedMargin.end << "px) -> layoutBox(" << &replacedBox << ")");
+    return { usedWidth, nonReplacedWidthAndMargin.usedMargin };
+}
+
+LayoutUnit BlockFormattingContext::Geometry::staticVerticalPosition(const Box& layoutBox, const VerticalConstraints& verticalConstraints) const
+{
+    // https://www.w3.org/TR/CSS22/visuren.html#block-formatting
+    // In a block formatting context, boxes are laid out one after the other, vertically, beginning at the top of a containing block.
+    // The vertical distance between two sibling boxes is determined by the 'margin' properties.
+    // Vertical margins between adjacent block-level boxes in a block formatting context collapse.
+    if (auto* previousInFlowSibling = layoutBox.previousInFlowSibling()) {
+        auto& previousInFlowBoxGeometry = formattingContext().geometryForBox(*previousInFlowSibling);
+        return BoxGeometry::borderBoxRect(previousInFlowBoxGeometry).bottom() + previousInFlowBoxGeometry.marginAfter();
+    }
+    return verticalConstraints.logicalTop;
+}
+
+LayoutUnit BlockFormattingContext::Geometry::staticHorizontalPosition(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints) const
+{
+    // https://www.w3.org/TR/CSS22/visuren.html#block-formatting
+    // In a block formatting context, each box's left outer edge touches the left edge of the containing block (for right-to-left formatting, right edges touch).
+    return horizontalConstraints.logicalLeft + formattingContext().geometryForBox(layoutBox).marginStart();
+}
+
+Point BlockFormattingContext::Geometry::staticPosition(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints, const VerticalConstraints& verticalConstraints) const
+{
+    return { staticHorizontalPosition(layoutBox, horizontalConstraints), staticVerticalPosition(layoutBox, verticalConstraints) };
+}
+
+ContentHeightAndMargin BlockFormattingContext::Geometry::inFlowContentHeightAndMargin(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints, const OverriddenVerticalValues& overriddenVerticalValues)
+{
+    ASSERT(layoutBox.isInFlow());
+
+    // 10.6.2 Inline replaced elements, block-level replaced elements in normal flow, 'inline-block'
+    // replaced elements in normal flow and floating replaced elements
+    if (layoutBox.isReplacedBox())
+        return inlineReplacedContentHeightAndMargin(downcast<ReplacedBox>(layoutBox), horizontalConstraints, { }, overriddenVerticalValues);
+
+    ContentHeightAndMargin contentHeightAndMargin;
+    if (layoutBox.isOverflowVisible() && !layoutBox.isDocumentBox()) {
+        // TODO: Figure out the case for the document element. Let's just complicated-case it for now.
+        contentHeightAndMargin = inFlowNonReplacedContentHeightAndMargin(layoutBox, horizontalConstraints, overriddenVerticalValues);
+    } else {
+        // 10.6.6 Complicated cases
+        // Block-level, non-replaced elements in normal flow when 'overflow' does not compute to 'visible' (except if the 'overflow' property's value has been propagated to the viewport).
+        contentHeightAndMargin = complicatedCases(layoutBox, horizontalConstraints, overriddenVerticalValues);
+    }
+
+    auto quirks = formattingContext().quirks();
+    if (!quirks.needsStretching(layoutBox))
+        return contentHeightAndMargin;
+
+    contentHeightAndMargin.contentHeight = quirks.stretchedInFlowHeight(layoutBox, contentHeightAndMargin);
+
+    LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> inflow non-replaced -> streched to viewport -> height(" << contentHeightAndMargin.contentHeight << "px) margin(" << contentHeightAndMargin.nonCollapsedMargin.before << "px, " << contentHeightAndMargin.nonCollapsedMargin.after << "px) -> layoutBox(" << &layoutBox << ")");
+    return contentHeightAndMargin;
+}
+
+ContentWidthAndMargin BlockFormattingContext::Geometry::inFlowContentWidthAndMargin(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints, const OverriddenHorizontalValues& overriddenHorizontalValues)
+{
+    ASSERT(layoutBox.isInFlow());
+
+    if (!layoutBox.isReplacedBox())
+        return inFlowNonReplacedContentWidthAndMargin(layoutBox, horizontalConstraints, overriddenHorizontalValues);
+    return inFlowReplacedContentWidthAndMargin(downcast<ReplacedBox>(layoutBox), horizontalConstraints, overriddenHorizontalValues);
+}
+
+ContentWidthAndMargin BlockFormattingContext::Geometry::computedContentWidthAndMargin(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints, Optional<LayoutUnit> availableWidthFloatAvoider)
+{
+    auto compute = [&] (auto constraintsForWidth, Optional<LayoutUnit> usedWidth) {
+        if (layoutBox.isFloatingPositioned())
+            return floatingContentWidthAndMargin(layoutBox, constraintsForWidth, { usedWidth, { } });
+
+        if (layoutBox.isInFlow())
+            return inFlowContentWidthAndMargin(layoutBox, constraintsForWidth, { usedWidth, { } });
+
+        ASSERT_NOT_REACHED();
+        return ContentWidthAndMargin { };
+    };
+
+    auto horizontalConstraintsForWidth = horizontalConstraints;
+    if (layoutBox.style().logicalWidth().isAuto() && availableWidthFloatAvoider) {
+        // While the non-auto width values should all be resolved against the containing block's width, when
+        // the width is auto the available horizontal space is shrunk by neighboring floats.
+        horizontalConstraintsForWidth.logicalWidth = *availableWidthFloatAvoider;
+    }
+    auto contentWidthAndMargin = compute(horizontalConstraintsForWidth, { });
+    auto availableWidth = horizontalConstraints.logicalWidth;
+    if (auto maxWidth = computedMaxWidth(layoutBox, availableWidth)) {
+        auto maxWidthAndMargin = compute(horizontalConstraints, maxWidth);
+        if (contentWidthAndMargin.contentWidth > maxWidthAndMargin.contentWidth)
+            contentWidthAndMargin = maxWidthAndMargin;
+    }
+
+    auto minWidth = computedMinWidth(layoutBox, availableWidth).valueOr(0);
+    auto minWidthAndMargin = compute(horizontalConstraints, minWidth);
+    if (contentWidthAndMargin.contentWidth < minWidthAndMargin.contentWidth)
+        contentWidthAndMargin = minWidthAndMargin;
+    return contentWidthAndMargin;
+}
+
+FormattingContext::IntrinsicWidthConstraints BlockFormattingContext::Geometry::intrinsicWidthConstraints(const Box& layoutBox)
+{
+    auto fixedMarginBorderAndPadding = [&](auto& layoutBox) {
+        auto& style = layoutBox.style();
+        return fixedValue(style.marginStart()).valueOr(0)
+            + LayoutUnit { style.borderLeftWidth() }
+            + fixedValue(style.paddingLeft()).valueOr(0)
+            + fixedValue(style.paddingRight()).valueOr(0)
+            + LayoutUnit { style.borderRightWidth() }
+            + fixedValue(style.marginEnd()).valueOr(0);
+    };
+
+    auto computedIntrinsicWidthConstraints = [&]() -> IntrinsicWidthConstraints {
+        auto logicalWidth = layoutBox.style().logicalWidth();
+        // Minimum/maximum width can't be depending on the containing block's width.
+        auto needsResolvedContainingBlockWidth = logicalWidth.isCalculated() || logicalWidth.isPercent() || logicalWidth.isRelative();
+        if (needsResolvedContainingBlockWidth)
+            return { };
+
+        if (auto width = fixedValue(logicalWidth))
+            return { *width, *width };
+
+        if (layoutBox.isReplacedBox()) {
+            auto& replacedBox = downcast<ReplacedBox>(layoutBox);
+            if (replacedBox.hasIntrinsicWidth()) {
+                auto replacedWidth = replacedBox.intrinsicWidth();
+                return { replacedWidth, replacedWidth };
+            }
+            return { };
+        }
+
+        if (!is<ContainerBox>(layoutBox) || !downcast<ContainerBox>(layoutBox).hasInFlowOrFloatingChild())
+            return { };
+
+        if (layoutBox.isSizeContainmentBox()) {
+            // The intrinsic sizes of the size containment box are determined as if the element had no content,
+            // following the same logic as when sizing as if empty.
+            return { };
+        }
+
+        if (layoutBox.establishesFormattingContext()) {
+            auto intrinsicWidthConstraints = LayoutContext::createFormattingContext(downcast<ContainerBox>(layoutBox), layoutState())->computedIntrinsicWidthConstraints();
+            if (logicalWidth.isMinContent())
+                return { intrinsicWidthConstraints.minimum, intrinsicWidthConstraints.minimum };
+            if (logicalWidth.isMaxContent())
+                return { intrinsicWidthConstraints.maximum, intrinsicWidthConstraints.maximum };
+            return intrinsicWidthConstraints;
+        }
+
+        auto intrinsicWidthConstraints = IntrinsicWidthConstraints { };
+        auto& formattingState = layoutState().formattingStateForBox(layoutBox);
+        for (auto& child : childrenOfType<Box>(downcast<ContainerBox>(layoutBox))) {
+            if (child.isOutOfFlowPositioned() || (child.isFloatAvoider() && !child.hasFloatClear()))
+                continue;
+            auto childIntrinsicWidthConstraints = formattingState.intrinsicWidthConstraintsForBox(child);
+            ASSERT(childIntrinsicWidthConstraints);
+
+            intrinsicWidthConstraints.minimum = std::max(intrinsicWidthConstraints.minimum, childIntrinsicWidthConstraints->minimum);
+            intrinsicWidthConstraints.maximum = std::max(intrinsicWidthConstraints.maximum, childIntrinsicWidthConstraints->maximum);
+        }
+        return intrinsicWidthConstraints;
+    };
+    // FIXME Check for box-sizing: border-box;
+    auto intrinsicWidthConstraints = constrainByMinMaxWidth(layoutBox, computedIntrinsicWidthConstraints());
+    intrinsicWidthConstraints.expand(fixedMarginBorderAndPadding(layoutBox));
+    return intrinsicWidthConstraints;
+}
+
+}
+}
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsblockBlockFormattingContextQuirkscppfromrev276885trunkSourceWebCorelayoutformattingContextsblockformattingBlockFormattingContextQuirkscpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingContextQuirks.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/blockformatting/BlockFormattingContextQuirks.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingContextQuirks.cpp                            (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingContextQuirks.cpp       2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,129 @@
</span><ins>+/*
+ * Copyright (C) 2018 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 "BlockFormattingContext.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "BlockFormattingState.h"
+#include "LayoutBox.h"
+#include "LayoutBoxGeometry.h"
+#include "LayoutContainerBox.h"
+#include "LayoutInitialContainingBlock.h"
+#include "LayoutState.h"
+
+namespace WebCore {
+namespace Layout {
+
+static bool isQuirkContainer(const Box& layoutBox)
+{
+    return layoutBox.isBodyBox() || layoutBox.isDocumentBox() || layoutBox.isTableCell();
+}
+
+bool BlockFormattingContext::Quirks::needsStretching(const Box& layoutBox) const
+{
+    ASSERT(layoutBox.isInFlow());
+    // In quirks mode, in-flow body and html stretch to the initial containing block (height: auto only).
+    if (!layoutState().inQuirksMode())
+        return false;
+
+    if (!layoutBox.isDocumentBox() && !layoutBox.isBodyBox())
+        return false;
+
+    return layoutBox.style().logicalHeight().isAuto();
+}
+
+LayoutUnit BlockFormattingContext::Quirks::stretchedInFlowHeight(const Box& layoutBox, ContentHeightAndMargin contentHeightAndMargin)
+{
+    ASSERT(needsStretching(layoutBox));
+    auto& formattingContext = this->formattingContext();
+    auto nonCollapsedVerticalMargin = contentHeightAndMargin.nonCollapsedMargin.before + contentHeightAndMargin.nonCollapsedMargin.after;
+
+    if (layoutBox.isDocumentBox()) {
+        // Let's stretch the inflow document box(<html>) to the height of the initial containing block (view).
+        auto documentBoxContentHeight = formattingContext.geometryForBox(layoutBox.initialContainingBlock(), EscapeReason::DocumentBoxStretchesToViewportQuirk).contentBoxHeight();
+        // Document box's own vertical margin/border/padding values always shrink the content height.
+        auto& documentBoxGeometry = formattingContext.geometryForBox(layoutBox);
+        documentBoxContentHeight -= nonCollapsedVerticalMargin + documentBoxGeometry.verticalBorder() + documentBoxGeometry.verticalPadding().valueOr(0);
+        return std::max(contentHeightAndMargin.contentHeight,  documentBoxContentHeight);
+    }
+
+    // Here is the quirky part for body box when it stretches all the way to the ICB even when the document box does not (e.g. out-of-flow positioned).
+    ASSERT(layoutBox.isBodyBox());
+    auto& initialContainingBlock = layoutBox.initialContainingBlock();
+    auto& initialContainingBlockGeometry = formattingContext.geometryForBox(initialContainingBlock, EscapeReason::BodyStretchesToViewportQuirk);
+    // Start the content height with the ICB.
+    auto bodyBoxContentHeight = initialContainingBlockGeometry.contentBoxHeight();
+    // Body box's own border and padding shrink the content height.
+    auto& bodyBoxGeometry = formattingContext.geometryForBox(layoutBox);
+    bodyBoxContentHeight -= bodyBoxGeometry.verticalBorder() + bodyBoxGeometry.verticalPadding().valueOr(0);
+    // Body box never collapses its vertical margins with the document box but it might collapse its margin with its descendants.
+    auto nonCollapsedMargin = contentHeightAndMargin.nonCollapsedMargin;
+    auto collapsedMargin = formattingContext.marginCollapse().collapsedVerticalValues(layoutBox, nonCollapsedMargin).collapsedValues;
+    auto usedVerticalMargin = collapsedMargin.before.valueOr(nonCollapsedMargin.before);
+    usedVerticalMargin += collapsedMargin.isCollapsedThrough ? nonCollapsedMargin.after : collapsedMargin.after.valueOr(nonCollapsedMargin.after);
+    bodyBoxContentHeight -= usedVerticalMargin;
+    // Document box's padding and border also shrink the body box's content height.
+    auto& documentBox = layoutBox.parent();
+    auto& documentBoxGeometry = formattingContext.geometryForBox(documentBox, EscapeReason::BodyStretchesToViewportQuirk);
+    bodyBoxContentHeight -= documentBoxGeometry.verticalBorder() + documentBoxGeometry.verticalPadding().valueOr(0);
+    // However the non-in-flow document box's vertical margins are ignored. They don't affect the body box's content height.
+    if (documentBox.isInFlow()) {
+        auto geometry = this->geometry();
+        auto precomputeDocumentBoxVerticalMargin = geometry.computedVerticalMargin(documentBox, geometry.constraintsForInFlowContent(initialContainingBlock, EscapeReason::BodyStretchesToViewportQuirk).horizontal);
+        bodyBoxContentHeight -= precomputeDocumentBoxVerticalMargin.before.valueOr(0) + precomputeDocumentBoxVerticalMargin.after.valueOr(0);
+    }
+    return std::max(contentHeightAndMargin.contentHeight,  bodyBoxContentHeight);
+}
+
+bool BlockFormattingContext::Quirks::shouldIgnoreCollapsedQuirkMargin(const Box& layoutBox) const
+{
+    return layoutState().inQuirksMode() && isQuirkContainer(layoutBox);
+}
+
+enum class VerticalMargin { Before, After };
+static inline bool hasQuirkMarginToCollapse(const Box& layoutBox, VerticalMargin verticalMargin)
+{
+    if (!layoutBox.isInFlow())
+        return false;
+    auto& style = layoutBox.style();
+    return (verticalMargin == VerticalMargin::Before && style.hasMarginBeforeQuirk()) || (verticalMargin == VerticalMargin::After && style.hasMarginAfterQuirk());
+}
+
+bool BlockFormattingContext::Quirks::shouldCollapseMarginBeforeWithParentMarginBefore(const Box& layoutBox) const
+{
+    return layoutState().inQuirksMode() && hasQuirkMarginToCollapse(layoutBox, VerticalMargin::Before) && isQuirkContainer(layoutBox.containingBlock());
+}
+
+bool BlockFormattingContext::Quirks::shouldCollapseMarginAfterWithParentMarginAfter(const Box& layoutBox) const
+{
+    return layoutState().inQuirksMode() && hasQuirkMarginToCollapse(layoutBox, VerticalMargin::After) && isQuirkContainer(layoutBox.containingBlock());
+}
+
+}
+}
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsblockBlockFormattingStatecppfromrev276885trunkSourceWebCorelayoutformattingContextsblockformattingBlockFormattingStatecpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingState.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/blockformatting/BlockFormattingState.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingState.cpp                            (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingState.cpp       2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,49 @@
</span><ins>+/*
+ * Copyright (C) 2018 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 "BlockFormattingState.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include <wtf/IsoMallocInlines.h>
+
+namespace WebCore {
+namespace Layout {
+
+WTF_MAKE_ISO_ALLOCATED_IMPL(BlockFormattingState);
+
+BlockFormattingState::BlockFormattingState(Ref<FloatingState>&& floatingState, LayoutState& layoutState)
+    : FormattingState(WTFMove(floatingState), Type::Block, layoutState)
+{
+}
+
+BlockFormattingState::~BlockFormattingState()
+{
+}
+
+}
+}
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsblockBlockFormattingStatehfromrev276885trunkSourceWebCorelayoutformattingContextsblockformattingBlockFormattingStateh"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingState.h (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/blockformatting/BlockFormattingState.h) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingState.h                              (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/block/BlockFormattingState.h 2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,62 @@
</span><ins>+/*
+ * Copyright (C) 2018 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 "FormattingState.h"
+#include <wtf/IsoMalloc.h>
+#include <wtf/WeakHashSet.h>
+
+namespace WebCore {
+namespace Layout {
+
+// BlockFormattingState holds the state for a particular block formatting context tree.
+class BlockFormattingState : public FormattingState {
+    WTF_MAKE_ISO_ALLOCATED(BlockFormattingState);
+public:
+    BlockFormattingState(Ref<FloatingState>&&, LayoutState&);
+    ~BlockFormattingState();
+
+    void setUsedVerticalMargin(const Box& layoutBox, const UsedVerticalMargin& usedVerticalMargin) { m_usedVerticalMargins.set(&layoutBox, usedVerticalMargin); }
+    UsedVerticalMargin usedVerticalMargin(const Box& layoutBox) const { return m_usedVerticalMargins.get(&layoutBox); }
+    bool hasUsedVerticalMargin(const Box& layoutBox) const { return m_usedVerticalMargins.contains(&layoutBox); }
+
+    void setHasClearance(const Box& layoutBox) { m_clearanceSet.add(layoutBox); }
+    void clearHasClearance(const Box& layoutBox) { m_clearanceSet.remove(layoutBox); }
+    bool hasClearance(const Box& layoutBox) const { return m_clearanceSet.contains(layoutBox); }
+
+private:
+    HashMap<const Box*, UsedVerticalMargin> m_usedVerticalMargins;
+    WeakHashSet<const Box> m_clearanceSet;
+};
+
+}
+}
+
+SPECIALIZE_TYPE_TRAITS_LAYOUT_FORMATTING_STATE(BlockFormattingState, isBlockFormattingState())
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsblockBlockMarginCollapsecppfromrev276885trunkSourceWebCorelayoutformattingContextsblockformattingBlockMarginCollapsecpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/block/BlockMarginCollapse.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/blockformatting/BlockMarginCollapse.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/block/BlockMarginCollapse.cpp                             (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/block/BlockMarginCollapse.cpp        2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,607 @@
</span><ins>+/*
+ * Copyright (C) 2018 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 "BlockFormattingContext.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "BlockFormattingState.h"
+#include "FloatingState.h"
+#include "InlineFormattingState.h"
+#include "LayoutBox.h"
+#include "LayoutContainerBox.h"
+#include "LayoutUnit.h"
+#include "RenderStyle.h"
+
+namespace WebCore {
+namespace Layout {
+
+static bool hasBorder(const BorderValue& borderValue)
+{
+    if (borderValue.style() == BorderStyle::None || borderValue.style() == BorderStyle::Hidden)
+        return false;
+    return !!borderValue.width();
+}
+
+static bool hasPadding(const Length& paddingValue)
+{
+    // FIXME: Check if percent value needs to be resolved.
+    return !paddingValue.isZero();
+}
+
+static bool hasBorderBefore(const Box& layoutBox)
+{
+    return hasBorder(layoutBox.style().borderBefore());
+}
+
+static bool hasBorderAfter(const Box& layoutBox)
+{
+    return hasBorder(layoutBox.style().borderAfter());
+}
+
+static bool hasPaddingBefore(const Box& layoutBox)
+{
+    return hasPadding(layoutBox.style().paddingBefore());
+}
+
+static bool hasPaddingAfter(const Box& layoutBox)
+{
+    return hasPadding(layoutBox.style().paddingAfter());
+}
+
+static bool establishesBlockFormattingContext(const Box& layoutBox)
+{
+    // WebKit treats the document element renderer as a block formatting context root. It probably only impacts margin collapsing, so let's not do
+    // a layout wide quirk on this for now.
+    if (layoutBox.isDocumentBox())
+        return true;
+    return layoutBox.establishesBlockFormattingContext();
+}
+
+bool BlockFormattingContext::MarginCollapse::hasClearance(const Box& layoutBox) const
+{
+    if (!layoutBox.hasFloatClear())
+        return false;
+    // FIXME: precomputedVerticalPositionForFormattingRoot logic ends up calling into this function when the layoutBox (first inflow child) has
+    // not been laid out.
+    return formattingContext().formattingState().hasClearance(layoutBox);
+}
+
+bool BlockFormattingContext::MarginCollapse::marginBeforeCollapsesWithParentMarginAfter(const Box& layoutBox) const
+{
+    // 1. This is the last in-flow child and its margins collapse through and the margin after collapses with parent's margin after or
+    // 2. This box's margin after collapses with the next sibling's margin before and that sibling collapses through and
+    // we can get to the last in-flow child like that.
+    auto* lastInFlowChild = layoutBox.containingBlock().lastInFlowChild();
+    for (auto* currentBox = &layoutBox; currentBox; currentBox = currentBox->nextInFlowSibling()) {
+        if (!marginsCollapseThrough(*currentBox))
+            return false;
+        if (currentBox == lastInFlowChild)
+            return marginAfterCollapsesWithParentMarginAfter(*currentBox); 
+        if (!marginAfterCollapsesWithNextSiblingMarginBefore(*currentBox))
+            return false;
+    }
+    ASSERT_NOT_REACHED();
+    return false;
+}
+
+bool BlockFormattingContext::MarginCollapse::marginBeforeCollapsesWithParentMarginBefore(const Box& layoutBox) const
+{
+    // The first inflow child could propagate its top margin to parent.
+    // https://www.w3.org/TR/CSS21/box.html#collapsing-margins
+    ASSERT(layoutBox.isBlockLevelBox());
+
+    if (formattingContext().quirks().shouldCollapseMarginBeforeWithParentMarginBefore(layoutBox))
+        return true;
+
+    // Margins between a floated box and any other box do not collapse.
+    if (layoutBox.isFloatingPositioned())
+        return false;
+
+    // Margins of absolutely positioned boxes do not collapse.
+    if (layoutBox.isOutOfFlowPositioned())
+        return false;
+
+    // Margins of inline-block boxes do not collapse.
+    if (layoutBox.isInlineBlockBox())
+        return false;
+
+    // Only the first inlflow child collapses with parent.
+    if (layoutBox.previousInFlowSibling())
+        return false;
+
+    auto& containingBlock = layoutBox.containingBlock();
+    // Margins of elements that establish new block formatting contexts do not collapse with their in-flow children
+    if (establishesBlockFormattingContext(containingBlock))
+        return false;
+
+    if (hasBorderBefore(containingBlock))
+        return false;
+
+    if (hasPaddingBefore(containingBlock))
+        return false;
+
+    // ...and the child has no clearance.
+    if (hasClearance(layoutBox))
+        return false;
+
+    return true;
+}
+
+bool BlockFormattingContext::MarginCollapse::marginBeforeCollapsesWithPreviousSiblingMarginAfter(const Box& layoutBox) const
+{
+    ASSERT(layoutBox.isBlockLevelBox());
+
+    if (!layoutBox.previousInFlowSibling())
+        return false;
+
+    auto& previousInFlowSibling = *layoutBox.previousInFlowSibling();
+    // Margins between a floated box and any other box do not collapse.
+    if (layoutBox.isFloatingPositioned() || previousInFlowSibling.isFloatingPositioned())
+        return false;
+
+    // Margins of absolutely positioned boxes do not collapse.
+    if ((layoutBox.isOutOfFlowPositioned() && !layoutBox.style().top().isAuto())
+        || (previousInFlowSibling.isOutOfFlowPositioned() && !previousInFlowSibling.style().bottom().isAuto()))
+        return false;
+
+    // Margins of inline-block boxes do not collapse.
+    if (layoutBox.isInlineBlockBox() || previousInFlowSibling.isInlineBlockBox())
+        return false;
+
+    // The bottom margin of an in-flow block-level element always collapses with the top margin of
+    // its next in-flow block-level sibling, unless that sibling has clearance.
+    if (hasClearance(layoutBox))
+        return false;
+
+    return true;
+}
+
+bool BlockFormattingContext::MarginCollapse::marginBeforeCollapsesWithFirstInFlowChildMarginBefore(const Box& layoutBox) const
+{
+    ASSERT(layoutBox.isBlockLevelBox());
+    // Margins of elements that establish new block formatting contexts do not collapse with their in-flow children.
+    if (establishesBlockFormattingContext(layoutBox))
+        return false;
+
+    // The top margin of an in-flow block element collapses with its first in-flow block-level
+    // child's top margin if the element has no top border...
+    if (hasBorderBefore(layoutBox))
+        return false;
+
+    // ...no top padding
+    if (hasPaddingBefore(layoutBox))
+        return false;
+
+    if (!is<ContainerBox>(layoutBox) || !downcast<ContainerBox>(layoutBox).hasInFlowChild())
+        return false;
+
+    auto& firstInFlowChild = *downcast<ContainerBox>(layoutBox).firstInFlowChild();
+    if (!firstInFlowChild.isBlockLevelBox())
+        return false;
+
+    // ...and the child has no clearance.
+    if (hasClearance(firstInFlowChild))
+        return false;
+
+    // Margins of inline-block boxes do not collapse.
+    if (firstInFlowChild.isInlineBlockBox())
+        return false;
+
+    return true;
+}
+
+bool BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithSiblingMarginBeforeWithClearance(const Box& layoutBox) const
+{
+    // If the top and bottom margins of an element with clearance are adjoining, its margins collapse with the adjoining margins
+    // of following siblings but that resulting margin does not collapse with the bottom margin of the parent block.
+    if (!marginsCollapseThrough(layoutBox))
+        return false;
+
+    for (auto* previousSibling = layoutBox.previousInFlowSibling(); previousSibling; previousSibling = previousSibling->previousInFlowSibling()) {
+        if (!marginsCollapseThrough(*previousSibling))
+            return false;
+        if (hasClearance(*previousSibling))
+            return true;
+    }
+    return false;
+}
+
+bool BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithParentMarginBefore(const Box& layoutBox) const
+{
+    // 1. This is the first in-flow child and its margins collapse through and the margin before collapses with parent's margin before or
+    // 2. This box's margin before collapses with the previous sibling's margin after and that sibling collapses through and
+    // we can get to the first in-flow child like that.
+    auto* firstInFlowChild = layoutBox.containingBlock().firstInFlowChild();
+    for (auto* currentBox = &layoutBox; currentBox; currentBox = currentBox->previousInFlowSibling()) {
+        if (!marginsCollapseThrough(*currentBox))
+            return false;
+        if (currentBox == firstInFlowChild)
+            return marginBeforeCollapsesWithParentMarginBefore(*currentBox); 
+        if (!marginBeforeCollapsesWithPreviousSiblingMarginAfter(*currentBox))
+            return false;
+    }
+    ASSERT_NOT_REACHED();
+    return false;
+}
+
+bool BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithParentMarginAfter(const Box& layoutBox) const
+{
+    ASSERT(layoutBox.isBlockLevelBox());
+
+    if (formattingContext().quirks().shouldCollapseMarginAfterWithParentMarginAfter(layoutBox))
+        return true;
+
+    // Margins between a floated box and any other box do not collapse.
+    if (layoutBox.isFloatingPositioned())
+        return false;
+
+    // Margins of absolutely positioned boxes do not collapse.
+    if (layoutBox.isOutOfFlowPositioned())
+        return false;
+
+    // Margins of inline-block boxes do not collapse.
+    if (layoutBox.isInlineBlockBox())
+        return false;
+
+    // Only the last inlflow child collapses with parent.
+    if (layoutBox.nextInFlowSibling())
+        return false;
+
+    auto& containingBlock = layoutBox.containingBlock();
+    // Margins of elements that establish new block formatting contexts do not collapse with their in-flow children.
+    if (establishesBlockFormattingContext(containingBlock))
+        return false;
+
+    // The bottom margin of an in-flow block box with a 'height' of 'auto' collapses with its last in-flow block-level child's bottom margin, if:
+    if (!containingBlock.style().height().isAuto())
+        return false;
+
+    // the box has no bottom padding, and
+    if (hasPaddingAfter(containingBlock))
+        return false;
+
+    // the box has no bottom border, and
+    if (hasBorderAfter(containingBlock))
+        return false;
+
+    // the child's bottom margin neither collapses with a top margin that has clearance...
+    if (marginAfterCollapsesWithSiblingMarginBeforeWithClearance(layoutBox))
+        return false;
+
+    // nor (if the box's min-height is non-zero) with the box's top margin.
+    auto computedMinHeight = containingBlock.style().logicalMinHeight();
+    if (!computedMinHeight.isAuto() && computedMinHeight.value() && marginAfterCollapsesWithParentMarginBefore(layoutBox))
+        return false;
+
+    return true;
+}
+
+bool BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithLastInFlowChildMarginAfter(const Box& layoutBox) const
+{
+    ASSERT(layoutBox.isBlockLevelBox());
+
+    // Margins of elements that establish new block formatting contexts do not collapse with their in-flow children.
+    if (establishesBlockFormattingContext(layoutBox))
+        return false;
+
+    if (!is<ContainerBox>(layoutBox) || !downcast<ContainerBox>(layoutBox).hasInFlowChild())
+        return false;
+
+    auto& lastInFlowChild = *downcast<ContainerBox>(layoutBox).lastInFlowChild();
+    if (!lastInFlowChild.isBlockLevelBox())
+        return false;
+
+    // The bottom margin of an in-flow block box with a 'height' of 'auto' collapses with its last in-flow block-level child's bottom margin, if:
+    if (!layoutBox.style().height().isAuto())
+        return false;
+
+    // the box has no bottom padding, and
+    if (hasPaddingAfter(layoutBox))
+        return false;
+
+    // the box has no bottom border, and
+    if (hasBorderAfter(layoutBox))
+        return false;
+
+    // the child's bottom margin neither collapses with a top margin that has clearance...
+    if (marginAfterCollapsesWithSiblingMarginBeforeWithClearance(lastInFlowChild))
+        return false;
+
+    // nor (if the box's min-height is non-zero) with the box's top margin.
+    auto computedMinHeight = layoutBox.style().logicalMinHeight();
+    if (!computedMinHeight.isAuto() && computedMinHeight.value()
+        && (marginAfterCollapsesWithParentMarginBefore(lastInFlowChild) || hasClearance(lastInFlowChild)))
+        return false;
+
+    // Margins of inline-block boxes do not collapse.
+    if (lastInFlowChild.isInlineBlockBox())
+        return false;
+
+    // This is a quirk behavior: When the margin after of the last inflow child (or a previous sibling with collapsed through margins)
+    // collapses with a quirk parent's the margin before, then the same margin after does not collapses with the parent's margin after.
+    if (formattingContext().quirks().shouldIgnoreCollapsedQuirkMargin(layoutBox) && marginAfterCollapsesWithParentMarginBefore(lastInFlowChild))
+        return false;
+
+    return true;
+}
+
+bool BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithNextSiblingMarginBefore(const Box& layoutBox) const
+{
+    ASSERT(layoutBox.isBlockLevelBox());
+
+    if (!layoutBox.nextInFlowSibling())
+        return false;
+
+    return marginBeforeCollapsesWithPreviousSiblingMarginAfter(*layoutBox.nextInFlowSibling());
+}
+
+bool BlockFormattingContext::MarginCollapse::marginsCollapseThrough(const Box& layoutBox) const
+{
+    ASSERT(layoutBox.isBlockLevelBox());
+
+    // A box's own margins collapse if the 'min-height' property is zero, and it has neither top or bottom borders nor top or bottom padding,
+    // and it has a 'height' of either 0 or 'auto', and it does not contain a line box, and all of its in-flow children's margins (if any) collapse.
+    if (hasBorderBefore(layoutBox) || hasBorderAfter(layoutBox))
+        return false;
+
+    if (hasPaddingBefore(layoutBox) || hasPaddingAfter(layoutBox))
+        return false;
+
+    // Margins are not adjoining when the box has clearance.
+    if (hasClearance(layoutBox))
+        return false;
+
+    auto& style = layoutBox.style();
+    auto computedHeightValueIsZero = style.height().isFixed() && !style.height().value();
+    if (!(style.height().isAuto() || computedHeightValueIsZero))
+        return false;
+
+    // FIXME: Check for computed 0 height.
+    if (!style.minHeight().isAuto())
+        return false;
+
+    // FIXME: Block replaced boxes clearly don't collapse through their margins, but I couldn't find it in the spec yet (and no, it's not a quirk).
+    if (layoutBox.isReplacedBox())
+        return false;
+
+    if (!is<ContainerBox>(layoutBox))
+        return true;
+
+    if (!downcast<ContainerBox>(layoutBox).hasInFlowChild())
+        return !establishesBlockFormattingContext(layoutBox);
+
+    if (layoutBox.establishesFormattingContext()) {
+        if (layoutBox.establishesInlineFormattingContext()) {
+            auto& layoutState = this->layoutState();
+            // If we get here through margin estimation, we don't necessarily have an actual state for this layout box since
+            // we haven't started laying it out yet.
+            auto& containerBox = downcast<ContainerBox>(layoutBox);
+            if (!layoutState.hasInlineFormattingState(containerBox))
+                return false;
+
+            auto isConsideredEmpty = [&] {
+                auto& inlineFormattingState = layoutState.establishedInlineFormattingState(containerBox);
+                if (!inlineFormattingState.lines().isEmpty())
+                    return false;
+                // Any float box in this formatting context prevents collapsing through.
+                auto& floats = inlineFormattingState.floatingState().floats();
+                for (auto& floatItem : floats) {
+                    if (floatItem.isInFormattingContextOf(containerBox))
+                        return false;
+                }
+                return true;
+            };
+            return isConsideredEmpty();
+        }
+
+        // A root of a non-inline formatting context (table, flex etc) with inflow descendants should not collapse through.
+        return false;
+    }
+
+    for (auto* inflowChild = downcast<ContainerBox>(layoutBox).firstInFlowOrFloatingChild(); inflowChild; inflowChild = inflowChild->nextInFlowOrFloatingSibling()) {
+        if (establishesBlockFormattingContext(*inflowChild))
+            return false;
+        if (!marginsCollapseThrough(*inflowChild))
+            return false;
+    }
+    return true;
+}
+
+UsedVerticalMargin::PositiveAndNegativePair::Values BlockFormattingContext::MarginCollapse::computedPositiveAndNegativeMargin(UsedVerticalMargin::PositiveAndNegativePair::Values a, UsedVerticalMargin::PositiveAndNegativePair::Values b) const
+{
+    UsedVerticalMargin::PositiveAndNegativePair::Values computedValues;
+    if (a.positive && b.positive)
+        computedValues.positive = std::max(*a.positive, *b.positive);
+    else
+        computedValues.positive = a.positive ? a.positive : b.positive;
+
+    if (a.negative && b.negative)
+        computedValues.negative = std::min(*a.negative, *b.negative);
+    else
+        computedValues.negative = a.negative ? a.negative : b.negative;
+
+    if (a.isNonZero() && b.isNonZero())
+        computedValues.isQuirk = a.isQuirk || b.isQuirk;
+    else if (a.isNonZero())
+        computedValues.isQuirk = a.isQuirk;
+    else
+        computedValues.isQuirk = b.isQuirk;
+
+    return computedValues;
+}
+
+Optional<LayoutUnit> BlockFormattingContext::MarginCollapse::marginValue(UsedVerticalMargin::PositiveAndNegativePair::Values marginValues) const
+{
+    // When two or more margins collapse, the resulting margin width is the maximum of the collapsing margins' widths.
+    // In the case of negative margins, the maximum of the absolute values of the negative adjoining margins is deducted from the maximum
+    // of the positive adjoining margins. If there are no positive margins, the maximum of the absolute values of the adjoining margins is deducted from zero.
+    if (!marginValues.negative)
+        return marginValues.positive;
+
+    if (!marginValues.positive)
+        return marginValues.negative;
+
+    return *marginValues.positive + *marginValues.negative;
+}
+
+void BlockFormattingContext::MarginCollapse::updateMarginAfterForPreviousSibling(BlockFormattingContext& blockFormattingContext, const MarginCollapse& marginCollapse, const Box& layoutBox)
+{
+    // 1. Get the margin before value from the next in-flow sibling. This is the same as this box's margin after value now since they are collapsed.
+    // 2. Update the collapsed margin after value as well as the positive/negative cache.
+    // 3. Check if the box's margins collapse through.
+    // 4. If so, update the positive/negative cache.
+    // 5. In case of collapsed through margins check if the before margin collapes with the previous inflow sibling's after margin.
+    // 6. If so, jump to #2.
+    // 7. No need to propagate to parent because its margin is not computed yet (pre-computed at most).
+    auto* currentBox = &layoutBox;
+    auto& blockFormattingState = blockFormattingContext.formattingState();
+    while (marginCollapse.marginBeforeCollapsesWithPreviousSiblingMarginAfter(*currentBox)) {
+        auto& previousSibling = *currentBox->previousInFlowSibling();
+        auto previousSiblingVerticalMargin = blockFormattingState.usedVerticalMargin(previousSibling);
+
+        auto collapsedVerticalMarginBefore = previousSiblingVerticalMargin.collapsedValues.before;
+        auto collapsedVerticalMarginAfter = blockFormattingContext.geometryForBox(*currentBox).marginBefore();
+
+        auto marginsCollapseThrough = marginCollapse.marginsCollapseThrough(previousSibling);
+        if (marginsCollapseThrough)
+            collapsedVerticalMarginBefore = collapsedVerticalMarginAfter;
+
+        // Update positive/negative cache.
+        auto previousSiblingPositiveNegativeMargin = blockFormattingState.usedVerticalMargin(previousSibling).positiveAndNegativeValues;
+        auto positiveNegativeMarginBefore = blockFormattingState.usedVerticalMargin(*currentBox).positiveAndNegativeValues.before;
+
+        auto adjustedPreviousSiblingVerticalMargin = previousSiblingVerticalMargin;
+        adjustedPreviousSiblingVerticalMargin.positiveAndNegativeValues.after = marginCollapse.computedPositiveAndNegativeMargin(positiveNegativeMarginBefore, previousSiblingPositiveNegativeMargin.after);
+        if (marginsCollapseThrough) {
+            adjustedPreviousSiblingVerticalMargin.positiveAndNegativeValues.before = marginCollapse.computedPositiveAndNegativeMargin(previousSiblingPositiveNegativeMargin.before, adjustedPreviousSiblingVerticalMargin.positiveAndNegativeValues.after);
+            adjustedPreviousSiblingVerticalMargin.positiveAndNegativeValues.after = adjustedPreviousSiblingVerticalMargin.positiveAndNegativeValues.before;
+        }
+        blockFormattingState.setUsedVerticalMargin(previousSibling, adjustedPreviousSiblingVerticalMargin);
+
+        if (!marginsCollapseThrough)
+            break;
+
+        currentBox = &previousSibling;
+    }
+}
+
+UsedVerticalMargin::PositiveAndNegativePair::Values BlockFormattingContext::MarginCollapse::positiveNegativeValues(const Box& layoutBox, MarginType marginType) const
+{
+    auto& formattingState = formattingContext().formattingState();
+    // By the time we get here in BFC layout to gather positive and negative margin values for either a previous sibling or a child box,
+    // we mush have computed and cached those values.
+    ASSERT(formattingState.hasUsedVerticalMargin(layoutBox));
+    auto positiveAndNegativeVerticalMargin = formattingState.usedVerticalMargin(layoutBox).positiveAndNegativeValues;
+    return marginType == MarginType::Before ? positiveAndNegativeVerticalMargin.before : positiveAndNegativeVerticalMargin.after; 
+}
+
+UsedVerticalMargin::PositiveAndNegativePair::Values BlockFormattingContext::MarginCollapse::positiveNegativeMarginBefore(const Box& layoutBox, UsedVerticalMargin::NonCollapsedValues nonCollapsedValues) const
+{
+    auto firstChildCollapsedMarginBefore = [&]() -> UsedVerticalMargin::PositiveAndNegativePair::Values {
+        if (!marginBeforeCollapsesWithFirstInFlowChildMarginBefore(layoutBox))
+            return { };
+        return positiveNegativeValues(*downcast<ContainerBox>(layoutBox).firstInFlowChild(), MarginType::Before);
+    };
+
+    auto previouSiblingCollapsedMarginAfter = [&]() -> UsedVerticalMargin::PositiveAndNegativePair::Values {
+        if (!marginBeforeCollapsesWithPreviousSiblingMarginAfter(layoutBox))
+            return { };
+        return positiveNegativeValues(*layoutBox.previousInFlowSibling(), MarginType::After);
+    };
+
+    // 1. Gather positive and negative margin values from first child if margins are adjoining.
+    // 2. Gather positive and negative margin values from previous inflow sibling if margins are adjoining.
+    // 3. Compute min/max positive and negative collapsed margin values using non-collpased computed margin before.
+    auto collapsedMarginBefore = computedPositiveAndNegativeMargin(firstChildCollapsedMarginBefore(), previouSiblingCollapsedMarginAfter());
+    if (collapsedMarginBefore.isQuirk && formattingContext().quirks().shouldIgnoreCollapsedQuirkMargin(layoutBox))
+        collapsedMarginBefore = { };
+
+    UsedVerticalMargin::PositiveAndNegativePair::Values nonCollapsedBefore;
+    if (nonCollapsedValues.before > 0)
+        nonCollapsedBefore = { nonCollapsedValues.before, { }, layoutBox.style().hasMarginBeforeQuirk() };
+    else if (nonCollapsedValues.before < 0)
+        nonCollapsedBefore = { { }, nonCollapsedValues.before, layoutBox.style().hasMarginBeforeQuirk() };
+
+    return computedPositiveAndNegativeMargin(collapsedMarginBefore, nonCollapsedBefore);
+}
+
+UsedVerticalMargin::PositiveAndNegativePair::Values BlockFormattingContext::MarginCollapse::positiveNegativeMarginAfter(const Box& layoutBox, UsedVerticalMargin::NonCollapsedValues nonCollapsedValues) const
+{
+    auto lastChildCollapsedMarginAfter = [&]() -> UsedVerticalMargin::PositiveAndNegativePair::Values {
+        if (!marginAfterCollapsesWithLastInFlowChildMarginAfter(layoutBox))
+            return { };
+        return positiveNegativeValues(*downcast<ContainerBox>(layoutBox).lastInFlowChild(), MarginType::After);
+    };
+
+    // We don't know yet the margin before value of the next sibling. Let's just pretend it does not have one and 
+    // update it later when we compute the next sibling's margin before. See updateMarginAfterForPreviousSibling.
+    UsedVerticalMargin::PositiveAndNegativePair::Values nonCollapsedAfter;
+    if (nonCollapsedValues.after > 0)
+        nonCollapsedAfter = { nonCollapsedValues.after, { }, layoutBox.style().hasMarginAfterQuirk() };
+    else if (nonCollapsedValues.after < 0)
+        nonCollapsedAfter = { { }, nonCollapsedValues.after, layoutBox.style().hasMarginAfterQuirk() };
+
+    return computedPositiveAndNegativeMargin(lastChildCollapsedMarginAfter(), nonCollapsedAfter);
+}
+
+LayoutUnit BlockFormattingContext::MarginCollapse::marginBeforeIgnoringCollapsingThrough(const Box& layoutBox, UsedVerticalMargin::NonCollapsedValues nonCollapsedValues)
+{
+    ASSERT(layoutBox.isBlockLevelBox());
+    return marginValue(positiveNegativeMarginBefore(layoutBox, nonCollapsedValues)).valueOr(nonCollapsedValues.before);
+}
+
+UsedVerticalMargin BlockFormattingContext::MarginCollapse::collapsedVerticalValues(const Box& layoutBox, UsedVerticalMargin::NonCollapsedValues nonCollapsedValues)
+{
+    ASSERT(layoutBox.isBlockLevelBox());
+    // 1. Get min/max margin top values from the first in-flow child if we are collapsing margin top with it.
+    // 2. Get min/max margin top values from the previous in-flow sibling, if we are collapsing margin top with it.
+    // 3. Get this layout box's computed margin top value.
+    // 4. Update the min/max value and compute the final margin.
+    auto positiveAndNegativeVerticalMargin = UsedVerticalMargin::PositiveAndNegativePair { this->positiveNegativeMarginBefore(layoutBox, nonCollapsedValues), this->positiveNegativeMarginAfter(layoutBox, nonCollapsedValues) };
+
+    auto marginsCollapseThrough = this->marginsCollapseThrough(layoutBox);
+    if (marginsCollapseThrough) {
+        positiveAndNegativeVerticalMargin.before = computedPositiveAndNegativeMargin(positiveAndNegativeVerticalMargin.before, positiveAndNegativeVerticalMargin.after);
+        positiveAndNegativeVerticalMargin.after = positiveAndNegativeVerticalMargin.before;
+    }
+
+    auto hasCollapsedMarginBefore = marginBeforeCollapsesWithFirstInFlowChildMarginBefore(layoutBox) || marginBeforeCollapsesWithPreviousSiblingMarginAfter(layoutBox);
+    auto hasCollapsedMarginAfter = marginAfterCollapsesWithLastInFlowChildMarginAfter(layoutBox);
+    auto usedVerticalMargin = UsedVerticalMargin { nonCollapsedValues, { }, positiveAndNegativeVerticalMargin };
+
+    if ((hasCollapsedMarginBefore && hasCollapsedMarginAfter) || marginsCollapseThrough)
+        usedVerticalMargin.collapsedValues = { marginValue(positiveAndNegativeVerticalMargin.before), marginValue(positiveAndNegativeVerticalMargin.after), marginsCollapseThrough };
+    else if (hasCollapsedMarginBefore)
+        usedVerticalMargin.collapsedValues = { marginValue(positiveAndNegativeVerticalMargin.before), { }, false };
+    else if (hasCollapsedMarginAfter)
+        usedVerticalMargin.collapsedValues = { { }, marginValue(positiveAndNegativeVerticalMargin.after), false };
+    return usedVerticalMargin;
+}
+
+}
+}
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsblockPrecomputedBlockMarginCollapsecppfromrev276885trunkSourceWebCorelayoutformattingContextsblockformattingPrecomputedBlockMarginCollapsecpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/block/PrecomputedBlockMarginCollapse.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/blockformatting/PrecomputedBlockMarginCollapse.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/block/PrecomputedBlockMarginCollapse.cpp                          (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/block/PrecomputedBlockMarginCollapse.cpp     2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,100 @@
</span><ins>+/*
+ * Copyright (C) 2020 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 "BlockFormattingContext.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "BlockFormattingState.h"
+#include "LayoutBox.h"
+#include "LayoutContainerBox.h"
+#include "LayoutState.h"
+#include "LayoutUnit.h"
+#include "RenderStyle.h"
+
+namespace WebCore {
+namespace Layout {
+
+UsedVerticalMargin::PositiveAndNegativePair::Values BlockFormattingContext::MarginCollapse::precomputedPositiveNegativeValues(const Box& layoutBox) const
+{
+    auto& blockFormattingState = downcast<BlockFormattingState>(layoutState().formattingStateForBox(layoutBox));
+    if (blockFormattingState.hasUsedVerticalMargin(layoutBox))
+        return blockFormattingState.usedVerticalMargin(layoutBox).positiveAndNegativeValues.before;
+
+    auto geometry = formattingContext().geometry();
+    auto horizontalConstraints = geometry.constraintsForInFlowContent(layoutBox.containingBlock()).horizontal;
+    auto computedVerticalMargin = geometry.computedVerticalMargin(layoutBox, horizontalConstraints);
+    auto nonCollapsedMargin = UsedVerticalMargin::NonCollapsedValues { computedVerticalMargin.before.valueOr(0), computedVerticalMargin.after.valueOr(0) };
+    return precomputedPositiveNegativeMarginBefore(layoutBox, nonCollapsedMargin);
+}
+
+UsedVerticalMargin::PositiveAndNegativePair::Values BlockFormattingContext::MarginCollapse::precomputedPositiveNegativeMarginBefore(const Box& layoutBox, UsedVerticalMargin::NonCollapsedValues nonCollapsedValues) const
+{
+    auto firstChildCollapsedMarginBefore = [&]() -> UsedVerticalMargin::PositiveAndNegativePair::Values {
+        if (!marginBeforeCollapsesWithFirstInFlowChildMarginBefore(layoutBox))
+            return { };
+        return precomputedPositiveNegativeValues(*downcast<ContainerBox>(layoutBox).firstInFlowChild());
+    };
+
+    auto previouSiblingCollapsedMarginAfter = [&]() -> UsedVerticalMargin::PositiveAndNegativePair::Values {
+        if (!marginBeforeCollapsesWithPreviousSiblingMarginAfter(layoutBox))
+            return { };
+        auto& previousInFlowSibling = *layoutBox.previousInFlowSibling();
+        auto& blockFormattingState = downcast<BlockFormattingState>(layoutState().formattingStateForBox(previousInFlowSibling));
+        return blockFormattingState.usedVerticalMargin(previousInFlowSibling).positiveAndNegativeValues.after;
+    };
+
+    // 1. Gather positive and negative margin values from first child if margins are adjoining.
+    // 2. Gather positive and negative margin values from previous inflow sibling if margins are adjoining.
+    // 3. Compute min/max positive and negative collapsed margin values using non-collpased computed margin before.
+    auto collapsedMarginBefore = computedPositiveAndNegativeMargin(firstChildCollapsedMarginBefore(), previouSiblingCollapsedMarginAfter());
+    if (collapsedMarginBefore.isQuirk && formattingContext().quirks().shouldIgnoreCollapsedQuirkMargin(layoutBox))
+        collapsedMarginBefore = { };
+
+    auto nonCollapsedBefore = UsedVerticalMargin::PositiveAndNegativePair::Values { };
+    if (nonCollapsedValues.before > 0)
+        nonCollapsedBefore = { nonCollapsedValues.before, { }, layoutBox.style().hasMarginBeforeQuirk() };
+    else if (nonCollapsedValues.before < 0)
+        nonCollapsedBefore = { { }, nonCollapsedValues.before, layoutBox.style().hasMarginBeforeQuirk() };
+
+    return computedPositiveAndNegativeMargin(collapsedMarginBefore, nonCollapsedBefore);
+}
+
+PrecomputedMarginBefore BlockFormattingContext::MarginCollapse::precomputedMarginBefore(const Box& layoutBox, UsedVerticalMargin::NonCollapsedValues usedNonCollapsedMargin)
+{
+    ASSERT(layoutBox.isBlockLevelBox());
+    // Don't pre-compute vertical margins for out of flow boxes.
+    ASSERT(layoutBox.isInFlow() || layoutBox.isFloatingPositioned());
+    ASSERT(!layoutBox.isReplacedBox());
+
+    auto positiveNegativeMarginBefore = precomputedPositiveNegativeMarginBefore(layoutBox, usedNonCollapsedMargin);
+    auto collapsedMarginBefore = marginValue(positiveNegativeMarginBefore);
+    return { usedNonCollapsedMargin.before, collapsedMarginBefore, positiveNegativeMarginBefore };
+}
+
+}
+}
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsblocktablewrapperTableWrapperBlockFormattingContextcppfromrev276885trunkSourceWebCorelayoutformattingContextsblockformattingtablewrapperTableWrapperBlockFormattingContextcpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/block/tablewrapper/TableWrapperBlockFormattingContext.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/blockformatting/tablewrapper/TableWrapperBlockFormattingContext.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/block/tablewrapper/TableWrapperBlockFormattingContext.cpp                         (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/block/tablewrapper/TableWrapperBlockFormattingContext.cpp    2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,238 @@
</span><ins>+/*
+ * Copyright (C) 2020 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 "TableWrapperBlockFormattingContext.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "BlockFormattingState.h"
+#include "InvalidationState.h"
+#include "LayoutBoxGeometry.h"
+#include "LayoutChildIterator.h"
+#include "LayoutContext.h"
+#include "LayoutInitialContainingBlock.h"
+#include "TableFormattingContext.h"
+#include "TableFormattingState.h"
+
+namespace WebCore {
+namespace Layout {
+
+WTF_MAKE_ISO_ALLOCATED_IMPL(TableWrapperBlockFormattingContext);
+
+TableWrapperBlockFormattingContext::TableWrapperBlockFormattingContext(const ContainerBox& formattingContextRoot, BlockFormattingState& formattingState)
+    : BlockFormattingContext(formattingContextRoot, formattingState)
+{
+}
+
+void TableWrapperBlockFormattingContext::layoutInFlowContent(InvalidationState&, const ConstraintsForInFlowContent& constraints)
+{
+    // The table generates a principal block container box called the table wrapper box that contains the table box itself and any caption boxes
+    // (in document order). The table box is a block-level box that contains the table's internal table boxes.
+    // The caption boxes are principal block-level boxes that retain their own content, padding, margin, and border areas, and are rendered
+    // as normal block boxes inside the table wrapper box. Whether the caption boxes are placed before or after the table box is decided by
+    // the 'caption-side' property, as described below.
+    for (auto& child : childrenOfType<ContainerBox>(root())) {
+        if (child.isTableBox())
+            layoutTableBox(child, constraints);
+        else if (child.isTableCaption())
+            ASSERT_NOT_IMPLEMENTED_YET();
+        else
+            ASSERT_NOT_REACHED();
+    }
+}
+
+void TableWrapperBlockFormattingContext::layoutTableBox(const ContainerBox& tableBox, const ConstraintsForInFlowContent& constraints)
+{
+    layoutState().ensureTableFormattingState(tableBox);
+
+    computeBorderAndPaddingForTableBox(tableBox, constraints.horizontal);
+    computeStaticVerticalPosition(tableBox, constraints.vertical);
+    computeWidthAndMarginForTableBox(tableBox, constraints.horizontal);
+    computeStaticHorizontalPosition(tableBox, constraints.horizontal);
+
+    if (tableBox.hasChild()) {
+        auto invalidationState = InvalidationState { };
+        LayoutContext::createFormattingContext(tableBox, layoutState())->layoutInFlowContent(invalidationState, geometry().constraintsForInFlowContent(tableBox));
+    }
+
+    computeHeightAndMarginForTableBox(tableBox, constraints);
+}
+
+void TableWrapperBlockFormattingContext::computeBorderAndPaddingForTableBox(const ContainerBox& tableBox, const HorizontalConstraints& horizontalConstraints)
+{
+    ASSERT(tableBox.isTableBox());
+    if (!tableBox.hasChild() || tableBox.style().borderCollapse() == BorderCollapse::Separate) {
+        BlockFormattingContext::computeBorderAndPadding(tableBox, horizontalConstraints);
+        return;
+    }
+    // UAs must compute an initial left and right border width for the table by examining
+    // the first and last cells in the first row of the table.
+    // The left border width of the table is half of the first cell's collapsed left border,
+    // and the right border width of the table is half of the last cell's collapsed right border.
+    // The top border width of the table is computed by examining all cells who collapse their top
+    // borders with the top border of the table. The top border width of the table is equal to half of the
+    // maximum collapsed top border. The bottom border width is computed by examining all cells whose bottom borders collapse
+    // with the bottom of the table. The bottom border width is equal to half of the maximum collapsed bottom border.
+    auto& grid = layoutState().establishedTableFormattingState(tableBox).tableGrid();
+    auto tableBorder = geometry().computedBorder(tableBox);
+
+    auto& firstColumnFirstRowBox = grid.slot({ 0 , 0 })->cell().box();
+    auto leftBorder = std::max(tableBorder.horizontal.left, geometry().computedBorder(firstColumnFirstRowBox).horizontal.left);
+
+    auto& lastColumnFirstRow = grid.slot({ grid.columns().size() - 1, 0 })->cell().box();
+    auto rightBorder = std::max(tableBorder.horizontal.right, geometry().computedBorder(lastColumnFirstRow).horizontal.right);
+
+    auto topBorder = tableBorder.vertical.top;
+    auto bottomBorder = tableBorder.vertical.bottom;
+    auto lastRowIndex = grid.rows().size() - 1;
+    for (size_t columnIndex = 0; columnIndex < grid.columns().size(); ++columnIndex) {
+        auto& boxInFirstRox = grid.slot({ columnIndex, 0 })->cell().box();
+        auto& boxInLastRow = grid.slot({ columnIndex, lastRowIndex })->cell().box();
+
+        topBorder = std::max(topBorder, geometry().computedBorder(boxInFirstRox).vertical.top);
+        bottomBorder = std::max(bottomBorder, geometry().computedBorder(boxInLastRow).vertical.bottom);
+    }
+
+    topBorder = std::max(topBorder, geometry().computedBorder(*tableBox.firstChild()).vertical.top);
+    for (auto& section : childrenOfType<ContainerBox>(tableBox)) {
+        auto horiztonalBorder = geometry().computedBorder(section).horizontal;
+        leftBorder = std::max(leftBorder, horiztonalBorder.left);
+        rightBorder = std::max(rightBorder, horiztonalBorder.right);
+    }
+    bottomBorder = std::max(bottomBorder, geometry().computedBorder(*tableBox.lastChild()).vertical.bottom);
+
+    auto& rows = grid.rows().list();
+    topBorder = std::max(topBorder, geometry().computedBorder(rows.first().box()).vertical.top);
+    for (auto& row : rows) {
+        auto horiztonalBorder = geometry().computedBorder(row.box()).horizontal;
+        leftBorder = std::max(leftBorder, horiztonalBorder.left);
+        rightBorder = std::max(rightBorder, horiztonalBorder.right);
+    }
+    bottomBorder = std::max(bottomBorder, geometry().computedBorder(rows.last().box()).vertical.bottom);
+
+    auto collapsedBorder = Edges { { leftBorder, rightBorder }, { topBorder, bottomBorder } };
+    grid.setCollapsedBorder(collapsedBorder);
+
+    auto& boxGeometry = formattingState().boxGeometry(tableBox);
+    boxGeometry.setBorder(collapsedBorder / 2);
+    boxGeometry.setPadding(geometry().computedPadding(tableBox, horizontalConstraints.logicalWidth));
+}
+
+void TableWrapperBlockFormattingContext::computeWidthAndMarginForTableBox(const ContainerBox& tableBox, const HorizontalConstraints& horizontalConstraints)
+{
+    ASSERT(tableBox.isTableBox());
+    // This is a special table "fit-content size" behavior handling. Not in the spec though.
+    // Table returns its final width as min/max. Use this final width value to computed horizontal margins etc.
+    auto& formattingStateForTableBox = layoutState().establishedTableFormattingState(tableBox);
+    auto intrinsicWidthConstraints = IntrinsicWidthConstraints { };
+    if (auto precomputedIntrinsicWidthConstraints = formattingStateForTableBox.intrinsicWidthConstraints())
+        intrinsicWidthConstraints = *precomputedIntrinsicWidthConstraints;
+    else {
+        if (tableBox.hasChild())
+            intrinsicWidthConstraints = LayoutContext::createFormattingContext(tableBox, layoutState())->computedIntrinsicWidthConstraints();
+        formattingStateForTableBox.setIntrinsicWidthConstraints(intrinsicWidthConstraints);
+    }
+
+    auto availableHorizontalSpace = horizontalConstraints.logicalWidth;
+    // 1. The table generates a principal block container box called the table wrapper box that contains the table box itself and any caption boxes.
+    // 2. The table wrapper box establishes a block formatting context, and the table box establishes a table formatting context.
+    // 3. The computed values of properties 'position', 'float', 'margin-*', 'top', 'right', 'bottom', and 'left' on
+    //    the table element are used on the table wrapper box and not the table box; all other values of non-inheritable
+    //    properties are used on the table box and not the table wrapper box.
+    // 4. In a block formatting context, each box's left outer edge touches the left edge of the containing block.
+    //    This is true even in the presence of floats, unless the box establishes a new block formatting context (in which case the
+    //    box itself may become narrower due to the floats)
+    //
+    // Now consider the following case:
+    // <div style="display: block; width: 500px;">
+    //     <div style="float: left; width: 100px;"></div>
+    //         <div style="display: table; width: 10%;"></div>
+    //     </div>
+    // </div>
+    // 1. We create a table wrapper box to wrap the "display: table" block level box (#1).
+    // 2. The table wrapper box's width property is set to auto (#3).
+    // 3. Since it establishes a new block formatting context, the available horizontal space gets shrunk by the float (#4)
+    // 4. The table wrapper box's used width computes to 500px - 100px -> 400px;
+    //
+    // Now we are inside the BFC established by the table wrapper box and try to resolve the table's width -> %10.
+    // According to the normal BFC rules, it should compute to 10% of the containing block's logical width: 400px -> 40px.
+    // However in practice it computes to 50px (10% of 500px).
+    // Similar setup with non-table content would resolve the inner block level box's width to 40px;
+    // This needs clarification in the spec.
+    auto horizontalConstraintForResolvingWidth = m_horizontalConstraintsIgnoringFloats.logicalWidth;
+    auto geometry = this->geometry();
+    auto computedWidth = geometry.computedWidth(tableBox, horizontalConstraintForResolvingWidth);
+    auto computedMaxWidth = geometry.computedMaxWidth(tableBox, horizontalConstraintForResolvingWidth);
+    auto computedMinWidth = geometry.computedMinWidth(tableBox, horizontalConstraintForResolvingWidth);
+    // Use the generic shrink-to-fit-width logic as the initial width for the table.
+    auto usedWidth = std::min(std::max(intrinsicWidthConstraints.minimum, availableHorizontalSpace), intrinsicWidthConstraints.maximum);
+    if (computedWidth || computedMinWidth || computedMaxWidth) {
+        if (computedWidth) {
+            // Normalize the computed width value first.
+            if (computedMaxWidth && *computedWidth > *computedMaxWidth)
+                computedWidth = computedMaxWidth;
+            if (computedMinWidth && *computedWidth < *computedMinWidth)
+                computedWidth = computedMinWidth;
+            usedWidth = *computedWidth < intrinsicWidthConstraints.minimum ? intrinsicWidthConstraints.minimum : *computedWidth;
+        }
+
+        if (computedMaxWidth && *computedMaxWidth < usedWidth)
+            usedWidth = intrinsicWidthConstraints.minimum;
+        if (computedMinWidth && *computedMinWidth > usedWidth)
+            usedWidth = *computedMinWidth;
+    }
+
+    auto contentWidthAndMargin = geometry.inFlowContentWidthAndMargin(tableBox, horizontalConstraints, OverriddenHorizontalValues { usedWidth, { } });
+
+    auto& boxGeometry = formattingState().boxGeometry(tableBox);
+    boxGeometry.setContentBoxWidth(contentWidthAndMargin.contentWidth);
+    boxGeometry.setHorizontalMargin({ contentWidthAndMargin.usedMargin.start, contentWidthAndMargin.usedMargin.end });
+}
+
+void TableWrapperBlockFormattingContext::computeHeightAndMarginForTableBox(const ContainerBox& tableBox, const ConstraintsForInFlowContent& constraints)
+{
+    ASSERT(tableBox.isTableBox());
+    // Table is a special BFC content. Its height is mainly driven by the content. Computed height, min-height and max-height are all
+    // already been taken into account during the TFC layout.
+    auto heightAndMargin = geometry().inFlowContentHeightAndMargin(tableBox, constraints.horizontal, { quirks().overriddenTableHeight(tableBox) });
+
+    auto marginCollapse = this->marginCollapse();
+    auto verticalMargin = marginCollapse.collapsedVerticalValues(tableBox, heightAndMargin.nonCollapsedMargin);
+    // Cache the computed positive and negative margin value pair.
+    formattingState().setUsedVerticalMargin(tableBox, verticalMargin);
+
+    auto& boxGeometry = formattingState().boxGeometry(tableBox);
+    boxGeometry.setLogicalTop(verticalPositionWithMargin(tableBox, verticalMargin, constraints.vertical));
+    boxGeometry.setContentBoxHeight(heightAndMargin.contentHeight);
+    boxGeometry.setVerticalMargin({ marginBefore(verticalMargin), marginAfter(verticalMargin) });
+    // Adjust the previous sibling's margin bottom now that this box's vertical margin is computed.
+    MarginCollapse::updateMarginAfterForPreviousSibling(*this, marginCollapse, tableBox);
+}
+
+}
+}
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsblocktablewrapperTableWrapperBlockFormattingContexthfromrev276885trunkSourceWebCorelayoutformattingContextsblockformattingtablewrapperTableWrapperBlockFormattingContexth"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/block/tablewrapper/TableWrapperBlockFormattingContext.h (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/blockformatting/tablewrapper/TableWrapperBlockFormattingContext.h) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/block/tablewrapper/TableWrapperBlockFormattingContext.h                           (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/block/tablewrapper/TableWrapperBlockFormattingContext.h      2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,76 @@
</span><ins>+/*
+ * Copyright (C) 2020 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 "BlockFormattingContext.h"
+#include <wtf/IsoMalloc.h>
+
+namespace WebCore {
+namespace Layout {
+
+// This class implements the special block formatting context layout logic for the table wrapper.
+// https://www.w3.org/TR/CSS22/tables.html#model
+class TableWrapperBlockFormattingContext final : public BlockFormattingContext {
+    WTF_MAKE_ISO_ALLOCATED(TableWrapperBlockFormattingContext);
+public:
+    TableWrapperBlockFormattingContext(const ContainerBox& formattingContextRoot, BlockFormattingState&);
+
+    void layoutInFlowContent(InvalidationState&, const ConstraintsForInFlowContent&) final;
+
+    void setHorizontalConstraintsIgnoringFloats(const HorizontalConstraints& horizontalConstraints) { m_horizontalConstraintsIgnoringFloats = horizontalConstraints; }
+
+private:
+    class Quirks : public BlockFormattingContext::Quirks {
+    public:
+        Quirks(const TableWrapperBlockFormattingContext&);
+
+        Optional<LayoutUnit> overriddenTableHeight(const ContainerBox& tableBox) const;
+    };
+    TableWrapperBlockFormattingContext::Quirks quirks() const { return Quirks(*this); }
+
+    void layoutTableBox(const ContainerBox& tableBox, const ConstraintsForInFlowContent&);
+
+    void computeBorderAndPaddingForTableBox(const ContainerBox&, const HorizontalConstraints&);
+    void computeWidthAndMarginForTableBox(const ContainerBox&, const HorizontalConstraints&);
+    void computeHeightAndMarginForTableBox(const ContainerBox&, const ConstraintsForInFlowContent&);
+
+private:
+    HorizontalConstraints m_horizontalConstraintsIgnoringFloats;
+};
+
+inline TableWrapperBlockFormattingContext::Quirks::Quirks(const TableWrapperBlockFormattingContext& formattingContext)
+    : BlockFormattingContext::Quirks(formattingContext)
+{
+}
+
+}
+}
+
+SPECIALIZE_TYPE_TRAITS_LAYOUT_FORMATTING_CONTEXT(TableWrapperBlockFormattingContext, isTableWrapperBlockFormattingContext())
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsblocktablewrapperTableWrapperBlockFormattingContextQuirkscppfromrev276885trunkSourceWebCorelayoutformattingContextsblockformattingtablewrapperTableWrapperBlockFormattingContextQuirkscpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/block/tablewrapper/TableWrapperBlockFormattingContextQuirks.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/blockformatting/tablewrapper/TableWrapperBlockFormattingContextQuirks.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/block/tablewrapper/TableWrapperBlockFormattingContextQuirks.cpp                           (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/block/tablewrapper/TableWrapperBlockFormattingContextQuirks.cpp      2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,48 @@
</span><ins>+/*
+ * Copyright (C) 2020 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 "TableWrapperBlockFormattingContext.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "LayoutState.h"
+
+namespace WebCore {
+namespace Layout {
+
+Optional<LayoutUnit> TableWrapperBlockFormattingContext::Quirks::overriddenTableHeight(const ContainerBox& tableBox) const
+{
+    if (layoutState().inQuirksMode()) {
+        // In quirks mode always use the content height. Note that the tables with content take computed values into account.
+        return geometry().contentHeightForFormattingContextRoot(tableBox);
+    }
+    return tableBox.hasInFlowOrFloatingChild() ? geometry().contentHeightForFormattingContextRoot(tableBox) : Optional<LayoutUnit> { };
+}
+
+}
+}
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsflexFlexFormattingContextcppfromrev276885trunkSourceWebCorelayoutformattingContextsflexformattingFlexFormattingContextcpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/flex/FlexFormattingContext.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/flexformatting/FlexFormattingContext.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/flex/FlexFormattingContext.cpp                            (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/flex/FlexFormattingContext.cpp       2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,123 @@
</span><ins>+/*
+ * Copyright (C) 2020 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 "FlexFormattingContext.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "FlexFormattingState.h"
+#include "InlineRect.h"
+#include "InvalidationState.h"
+#include "LayoutBoxGeometry.h"
+#include "LayoutChildIterator.h"
+#include "LayoutContext.h"
+#include <wtf/IsoMallocInlines.h>
+
+namespace WebCore {
+namespace Layout {
+
+WTF_MAKE_ISO_ALLOCATED_IMPL(FlexFormattingContext);
+
+FlexFormattingContext::FlexFormattingContext(const ContainerBox& formattingContextRoot, FlexFormattingState& formattingState)
+    : FormattingContext(formattingContextRoot, formattingState)
+{
+}
+
+void FlexFormattingContext::layoutInFlowContent(InvalidationState&, const ConstraintsForInFlowContent& constraints)
+{
+    computeIntrinsicWidthConstraintsForFlexItems();
+    sizeAndPlaceFlexItems(constraints);
+}
+
+LayoutUnit FlexFormattingContext::usedContentHeight() const
+{
+    auto& lines = formattingState().lines();
+    return LayoutUnit { lines.last().bottom() - lines.first().top() };
+}
+
+FormattingContext::IntrinsicWidthConstraints FlexFormattingContext::computedIntrinsicWidthConstraints()
+{
+    return { };
+}
+
+void FlexFormattingContext::sizeAndPlaceFlexItems(const ConstraintsForInFlowContent& constraints)
+{
+    auto& formattingState = this->formattingState();
+    auto geometry = this->geometry();
+    auto flexItemMainAxisStart = constraints.horizontal.logicalLeft;
+    auto flexItemMainAxisEnd = flexItemMainAxisStart;
+    auto flexItemCrosAxisStart = constraints.vertical.logicalTop;
+    auto flexItemCrosAxisEnd = flexItemCrosAxisStart;
+    for (auto& flexItem : childrenOfType<ContainerBox>(root())) {
+        ASSERT(flexItem.establishesFormattingContext());
+        // FIXME: This is just a simple, let's layout the flex items and place them next to each other setup.
+        auto intrinsicWidths = formattingState.intrinsicWidthConstraintsForBox(flexItem);
+        auto flexItemLogicalWidth = std::min(std::max(intrinsicWidths->minimum, constraints.horizontal.logicalWidth), intrinsicWidths->maximum);
+        auto flexItemConstraints = ConstraintsForInFlowContent { { { }, flexItemLogicalWidth }, { } };
+
+        auto invalidationState = InvalidationState { };
+        LayoutContext::createFormattingContext(flexItem, layoutState())->layoutInFlowContent(invalidationState, flexItemConstraints);
+
+        auto computeFlexItemGeometry = [&] {
+            auto& flexItemGeometry = formattingState.boxGeometry(flexItem);
+
+            flexItemGeometry.setLogicalTopLeft(LayoutPoint { flexItemMainAxisEnd, flexItemCrosAxisStart });
+
+            flexItemGeometry.setBorder(geometry.computedBorder(flexItem));
+            flexItemGeometry.setPadding(geometry.computedPadding(flexItem, constraints.horizontal.logicalWidth));
+
+            auto computedHorizontalMargin = geometry.computedHorizontalMargin(flexItem, constraints.horizontal);
+            flexItemGeometry.setHorizontalMargin({ computedHorizontalMargin.start.valueOr(0_lu), computedHorizontalMargin.end.valueOr(0_lu) });
+
+            auto computedVerticalMargin = geometry.computedVerticalMargin(flexItem, constraints.horizontal);
+            flexItemGeometry.setVerticalMargin({ computedVerticalMargin.before.valueOr(0_lu), computedVerticalMargin.after.valueOr(0_lu) });
+
+            flexItemGeometry.setContentBoxHeight(geometry.contentHeightForFormattingContextRoot(flexItem));
+            flexItemGeometry.setContentBoxWidth(flexItemLogicalWidth);
+            flexItemMainAxisEnd= BoxGeometry::borderBoxRect(flexItemGeometry).right();
+            flexItemCrosAxisEnd = std::max(flexItemCrosAxisEnd, BoxGeometry::borderBoxRect(flexItemGeometry).bottom());
+        };
+        computeFlexItemGeometry();
+    }
+    auto flexLine = InlineRect { flexItemCrosAxisStart, flexItemMainAxisStart, flexItemMainAxisEnd - flexItemMainAxisStart, flexItemCrosAxisEnd - flexItemCrosAxisStart };
+    formattingState.addLine(flexLine);
+}
+
+void FlexFormattingContext::computeIntrinsicWidthConstraintsForFlexItems()
+{
+    auto& formattingState = this->formattingState();
+    auto geometry = this->geometry();
+    for (auto& flexItem : childrenOfType<ContainerBox>(root())) {
+        if (formattingState.intrinsicWidthConstraintsForBox(flexItem))
+            continue;
+        formattingState.setIntrinsicWidthConstraintsForBox(flexItem, geometry.intrinsicWidthConstraints(flexItem));
+    }
+}
+
+}
+}
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsflexFlexFormattingContexthfromrev276885trunkSourceWebCorelayoutformattingContextsflexformattingFlexFormattingContexth"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/flex/FlexFormattingContext.h (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/flexformatting/FlexFormattingContext.h) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/flex/FlexFormattingContext.h                              (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/flex/FlexFormattingContext.h 2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,79 @@
</span><ins>+/*
+ * Copyright (C) 2020 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 "FlexFormattingState.h"
+#include "FormattingContext.h"
+#include <wtf/IsoMalloc.h>
+
+namespace WebCore {
+namespace Layout {
+
+class InvalidationState;
+// This class implements the layout logic for flex formatting contexts.
+// https://www.w3.org/TR/css-flexbox-1/
+class FlexFormattingContext final : public FormattingContext {
+    WTF_MAKE_ISO_ALLOCATED(FlexFormattingContext);
+public:
+    FlexFormattingContext(const ContainerBox& formattingContextRoot, FlexFormattingState&);
+    void layoutInFlowContent(InvalidationState&, const ConstraintsForInFlowContent&) override;
+    LayoutUnit usedContentHeight() const override;
+
+    IntrinsicWidthConstraints computedIntrinsicWidthConstraints() override;
+
+private:
+    // This class implements positioning and sizing for flex items.
+    class Geometry : public FormattingContext::Geometry {
+    public:
+        Geometry(const FlexFormattingContext&);
+
+        IntrinsicWidthConstraints intrinsicWidthConstraints(const ContainerBox&);
+
+    private:
+        const FlexFormattingContext& formattingContext() const { return downcast<FlexFormattingContext>(FormattingContext::Geometry::formattingContext()); }
+    };
+    FlexFormattingContext::Geometry geometry() const { return Geometry(*this); }
+
+    void sizeAndPlaceFlexItems(const ConstraintsForInFlowContent&);
+    void computeIntrinsicWidthConstraintsForFlexItems();
+
+    const FlexFormattingState& formattingState() const { return downcast<FlexFormattingState>(FormattingContext::formattingState()); }
+    FlexFormattingState& formattingState() { return downcast<FlexFormattingState>(FormattingContext::formattingState()); }
+};
+
+inline FlexFormattingContext::Geometry::Geometry(const FlexFormattingContext& flexFormattingContext)
+    : FormattingContext::Geometry(flexFormattingContext)
+{
+}
+
+}
+}
+
+SPECIALIZE_TYPE_TRAITS_LAYOUT_FORMATTING_CONTEXT(FlexFormattingContext, isFlexFormattingContext())
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsflexFlexFormattingContextGeometrycppfromrev276885trunkSourceWebCorelayoutformattingContextsflexformattingFlexFormattingContextGeometrycpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/flex/FlexFormattingContextGeometry.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/flexformatting/FlexFormattingContextGeometry.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/flex/FlexFormattingContextGeometry.cpp                            (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/flex/FlexFormattingContextGeometry.cpp       2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,76 @@
</span><ins>+/*
+ * Copyright (C) 2020 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 "FlexFormattingContext.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "FlexFormattingState.h"
+#include "FormattingContext.h"
+#include "LayoutContext.h"
+
+namespace WebCore {
+namespace Layout {
+
+FormattingContext::IntrinsicWidthConstraints FlexFormattingContext::Geometry::intrinsicWidthConstraints(const ContainerBox& flexItem)
+{
+    auto fixedMarginBorderAndPadding = [&](auto& layoutBox) {
+        auto& style = layoutBox.style();
+        return fixedValue(style.marginStart()).valueOr(0)
+            + LayoutUnit { style.borderLeftWidth() }
+            + fixedValue(style.paddingLeft()).valueOr(0)
+            + fixedValue(style.paddingRight()).valueOr(0)
+            + LayoutUnit { style.borderRightWidth() }
+            + fixedValue(style.marginEnd()).valueOr(0);
+    };
+
+    auto computedIntrinsicWidthConstraints = [&]() -> IntrinsicWidthConstraints {
+        auto logicalWidth = flexItem.style().logicalWidth();
+        // Minimum/maximum width can't be depending on the containing block's width.
+        auto needsResolvedContainingBlockWidth = logicalWidth.isCalculated() || logicalWidth.isPercent() || logicalWidth.isRelative();
+        if (needsResolvedContainingBlockWidth)
+            return { };
+
+        if (auto width = fixedValue(logicalWidth))
+            return { *width, *width };
+
+        ASSERT(flexItem.establishesFormattingContext());
+        auto intrinsicWidthConstraints = LayoutContext::createFormattingContext(flexItem, layoutState())->computedIntrinsicWidthConstraints();
+        if (logicalWidth.isMinContent())
+            return { intrinsicWidthConstraints.minimum, intrinsicWidthConstraints.minimum };
+        if (logicalWidth.isMaxContent())
+            return { intrinsicWidthConstraints.maximum, intrinsicWidthConstraints.maximum };
+        return intrinsicWidthConstraints;
+    };
+    auto intrinsicWidthConstraints = constrainByMinMaxWidth(flexItem, computedIntrinsicWidthConstraints());
+    intrinsicWidthConstraints.expand(fixedMarginBorderAndPadding(flexItem));
+    return intrinsicWidthConstraints;
+}
+
+}
+}
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsflexFlexFormattingStatecppfromrev276885trunkSourceWebCorelayoutformattingContextsflexformattingFlexFormattingStatecpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/flex/FlexFormattingState.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/flexformatting/FlexFormattingState.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/flex/FlexFormattingState.cpp                              (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/flex/FlexFormattingState.cpp 2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,49 @@
</span><ins>+/*
+ * Copyright (C) 2020 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 "FlexFormattingState.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include <wtf/IsoMallocInlines.h>
+
+namespace WebCore {
+namespace Layout {
+
+WTF_MAKE_ISO_ALLOCATED_IMPL(FlexFormattingState);
+
+FlexFormattingState::FlexFormattingState(Ref<FloatingState>&& floatingState, LayoutState& layoutState)
+    : FormattingState(WTFMove(floatingState), Type::Flex, layoutState)
+{
+}
+
+FlexFormattingState::~FlexFormattingState()
+{
+}
+
+}
+}
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsflexFlexFormattingStatehfromrev276885trunkSourceWebCorelayoutformattingContextsflexformattingFlexFormattingStateh"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/flex/FlexFormattingState.h (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/flexformatting/FlexFormattingState.h) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/flex/FlexFormattingState.h                                (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/flex/FlexFormattingState.h   2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,55 @@
</span><ins>+/*
+ * Copyright (C) 2020 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 "FormattingState.h"
+#include "InlineRect.h"
+#include <wtf/IsoMalloc.h>
+
+namespace WebCore {
+namespace Layout {
+
+class FlexFormattingState : public FormattingState {
+    WTF_MAKE_ISO_ALLOCATED(FlexFormattingState);
+public:
+    FlexFormattingState(Ref<FloatingState>&&, LayoutState&);
+    ~FlexFormattingState();
+
+    const auto& lines() const { return m_lines; }
+    void addLine(const InlineRect& line) { m_lines.append(line); }
+
+private:
+    Vector<InlineRect> m_lines;
+};
+
+}
+}
+
+SPECIALIZE_TYPE_TRAITS_LAYOUT_FORMATTING_STATE(FlexFormattingState, isFlexFormattingState())
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineContentBreakercppfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingInlineContentBreakercpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/InlineContentBreaker.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/InlineContentBreaker.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineContentBreaker.cpp                           (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineContentBreaker.cpp      2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,531 @@
</span><ins>+/*
+ * Copyright (C) 2018 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 "InlineContentBreaker.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "FontCascade.h"
+#include "Hyphenation.h"
+#include "InlineItem.h"
+#include "InlineTextItem.h"
+#include "LayoutContainerBox.h"
+#include "TextUtil.h"
+
+namespace WebCore {
+namespace Layout {
+
+
+#if ASSERT_ENABLED
+static inline bool hasTrailingTextContent(const InlineContentBreaker::ContinuousContent& continuousContent)
+{
+    for (auto& run : WTF::makeReversedRange(continuousContent.runs())) {
+        auto& inlineItem = run.inlineItem;
+        if (inlineItem.isInlineBoxStart() || inlineItem.isInlineBoxEnd())
+            continue;
+        return inlineItem.isText();
+    }
+    return false;
+}
+#endif
+
+static inline bool hasLeadingTextContent(const InlineContentBreaker::ContinuousContent& continuousContent)
+{
+    for (auto& run : continuousContent.runs()) {
+        auto& inlineItem = run.inlineItem;
+        if (inlineItem.isInlineBoxStart() || inlineItem.isInlineBoxEnd())
+            continue;
+        return inlineItem.isText();
+    }
+    return false;
+}
+
+static inline bool hasTextRun(const InlineContentBreaker::ContinuousContent& continuousContent)
+{
+    // <span>text</span> is considered a text run even with the [inline box start][inline box end] inline items.
+    // Based on standards commit boundary rules it would be enough to check the first inline item, but due to the table quirk, we can have
+    // image and text next to each other inside a continuous set of runs (see InlineFormattingContext::Quirks::hasSoftWrapOpportunityAtImage).
+    for (auto& run : continuousContent.runs()) {
+        if (run.inlineItem.isText())
+            return true;
+    }
+    return false;
+}
+
+static inline bool isVisuallyEmptyWhitespaceContent(const InlineContentBreaker::ContinuousContent& continuousContent)
+{
+    // [<span></span> ] [<span> </span>] [ <span style="padding: 0px;"></span>] are all considered visually empty whitespace content.
+    // [<span style="border: 1px solid red"></span> ] while this is whitespace content only, it is not considered visually empty.
+    // Due to commit boundary rules, we just need to check the first non-typeless inline item (can't have both [img] and [text])
+    for (auto& run : continuousContent.runs()) {
+        auto& inlineItem = run.inlineItem;
+        // FIXME: check for padding border etc.
+        if (inlineItem.isInlineBoxStart() || inlineItem.isInlineBoxEnd())
+            continue;
+        return inlineItem.isText() && downcast<InlineTextItem>(inlineItem).isWhitespace();
+    }
+    return false;
+}
+
+static inline bool isNonContentRunsOnly(const InlineContentBreaker::ContinuousContent& continuousContent)
+{
+    // <span></span> <- non content runs.
+    for (auto& run : continuousContent.runs()) {
+        auto& inlineItem = run.inlineItem;
+        if (inlineItem.isInlineBoxStart() || inlineItem.isInlineBoxEnd())
+            continue;
+        return false;
+    }
+    return true;
+}
+
+static inline Optional<size_t> firstTextRunIndex(const InlineContentBreaker::ContinuousContent& continuousContent)
+{
+    auto& runs = continuousContent.runs();
+    for (size_t index = 0; index < runs.size(); ++index) {
+        if (runs[index].inlineItem.isText())
+            return index;
+    }
+    return { };
+}
+
+bool InlineContentBreaker::isWrappingAllowed(const InlineItem& inlineItem)
+{
+    auto& styleToUse = inlineItem.isBox() ? inlineItem.layoutBox().parent().style() : inlineItem.layoutBox().style(); 
+    // Do not try to wrap overflown 'pre' and 'no-wrap' content to next line.
+    return styleToUse.whiteSpace() != WhiteSpace::Pre && styleToUse.whiteSpace() != WhiteSpace::NoWrap;
+}
+
+bool InlineContentBreaker::shouldKeepEndOfLineWhitespace(const ContinuousContent& continuousContent) const
+{
+    // Grab the style and check for white-space property to decide whether we should let this whitespace content overflow the current line.
+    // Note that the "keep" in this context means we let the whitespace content sit on the current line.
+    // It might very well get collapsed when we close the line (normal/nowrap/pre-line).
+    // See https://www.w3.org/TR/css-text-3/#white-space-property
+    auto whitespace = continuousContent.runs()[*firstTextRunIndex(continuousContent)].inlineItem.style().whiteSpace();
+    return whitespace == WhiteSpace::Normal || whitespace == WhiteSpace::NoWrap || whitespace == WhiteSpace::PreWrap || whitespace == WhiteSpace::PreLine;
+}
+
+InlineContentBreaker::Result InlineContentBreaker::processInlineContent(const ContinuousContent& candidateContent, const LineStatus& lineStatus)
+{
+    ASSERT(!std::isnan(lineStatus.availableWidth));
+    auto processCandidateContent = [&] {
+        if (candidateContent.logicalWidth() <= lineStatus.availableWidth)
+            return Result { Result::Action::Keep };
+#if USE_FLOAT_AS_INLINE_LAYOUT_UNIT
+        // Preferred width computation sums up floats while line breaker subtracts them. This can lead to epsilon-scale differences.
+        if (WTF::areEssentiallyEqual(candidateContent.logicalWidth(), lineStatus.availableWidth))
+            return Result { Result::Action::Keep };
+#endif
+        return processOverflowingContent(candidateContent, lineStatus);
+    };
+
+    auto result = processCandidateContent();
+    if (result.action == Result::Action::Wrap && lineStatus.trailingSoftHyphenWidth && hasLeadingTextContent(candidateContent)) {
+        // A trailing soft hyphen with a wrapped text content turns into a visible hyphen.
+        // Let's check if there's enough space for the hyphen character.
+        auto hyphenOverflows = *lineStatus.trailingSoftHyphenWidth > lineStatus.availableWidth;
+        auto action = hyphenOverflows ? Result::Action::RevertToLastNonOverflowingWrapOpportunity : Result::Action::WrapWithHyphen;
+        result = { action, IsEndOfLine::Yes };
+    }
+    return result;
+}
+
+struct OverflowingTextContent {
+    size_t runIndex { 0 }; // Overflowing run index. There's always an overflowing run.
+    struct BreakingPosition {
+        size_t runIndex { 0 };
+        struct TrailingContent {
+            // Trailing content is either the run's left side (when we break the run somewhere in the middle) or the previous run.
+            // Sometimes the breaking position is at the very beginning of the first run, so there's no trailing run at all.
+            bool overflows { false };
+            Optional<InlineContentBreaker::PartialRun> partialRun { };
+        };
+        Optional<TrailingContent> trailingContent { };
+    };
+    Optional<BreakingPosition> breakingPosition { }; // Where we actually break this overflowing content.
+};
+
+InlineContentBreaker::Result InlineContentBreaker::processOverflowingContent(const ContinuousContent& overflowContent, const LineStatus& lineStatus) const
+{
+    auto continuousContent = ContinuousContent { overflowContent };
+    ASSERT(!continuousContent.runs().isEmpty());
+
+    ASSERT(continuousContent.logicalWidth() > lineStatus.availableWidth);
+    if (continuousContent.hasTrailingCollapsibleContent()) {
+        ASSERT(hasTrailingTextContent(overflowContent));
+        // First check if the content fits without the trailing collapsible part.
+        if (continuousContent.nonCollapsibleLogicalWidth() <= lineStatus.availableWidth)
+            return { Result::Action::Keep, IsEndOfLine::No };
+        // Now check if we can trim the line too.
+        if (lineStatus.hasFullyCollapsibleTrailingRun && continuousContent.isFullyCollapsible()) {
+            // If this new content is fully collapsible, it should surely fit.
+            return { Result::Action::Keep, IsEndOfLine::No };
+        }
+    } else if (lineStatus.collapsibleWidth && isNonContentRunsOnly(continuousContent)) {
+        // Let's see if the non-content runs fit when the line has trailing collapsible content.
+        // "text content <span style="padding: 1px"></span>" <- the <span></span> runs could fit after collapsing the trailing whitespace.
+        if (continuousContent.logicalWidth() <= lineStatus.availableWidth + lineStatus.collapsibleWidth)
+            return { Result::Action::Keep };
+    }
+    if (isVisuallyEmptyWhitespaceContent(continuousContent) && shouldKeepEndOfLineWhitespace(continuousContent)) {
+        // This overflowing content apparently falls into the remove/hang end-of-line-spaces category.
+        // see https://www.w3.org/TR/css-text-3/#white-space-property matrix
+        return { Result::Action::Keep };
+    }
+
+    size_t overflowingRunIndex = 0;
+    if (hasTextRun(continuousContent)) {
+        auto tryBreakingContentWithText = [&]() -> Optional<Result> {
+            // 1. This text content is not breakable.
+            // 2. This breakable text content does not fit at all. Not even the first glyph. This is a very special case.
+            // 3. We can break the content but it still overflows.
+            // 4. Managed to break the content before the overflow point.
+            auto overflowingContent = processOverflowingContentWithText(continuousContent, lineStatus);
+            overflowingRunIndex = overflowingContent.runIndex;
+            if (!overflowingContent.breakingPosition)
+                return { };
+            auto trailingContent = overflowingContent.breakingPosition->trailingContent;
+            if (!trailingContent) {
+                // We tried to break the content but the available space can't even accommodate the first glyph.
+                // 1. Wrap the content over to the next line when we've got content on the line already.
+                // 2. Keep the first glyph on the empty line (or keep the whole run if it has only one glyph/completely empty).
+                if (lineStatus.hasContent)
+                    return Result { Result::Action::Wrap, IsEndOfLine::Yes };
+                auto leadingTextRunIndex = *firstTextRunIndex(continuousContent);
+                auto& inlineTextItem = downcast<InlineTextItem>(continuousContent.runs()[leadingTextRunIndex].inlineItem);
+                if (inlineTextItem.length() <= 1)
+                    return Result { Result::Action::Keep, IsEndOfLine::Yes };
+                auto firstCharacterWidth = TextUtil::width(inlineTextItem, inlineTextItem.start(), inlineTextItem.start() + 1, lineStatus.contentLogicalRight);
+                auto firstCharacterRun = PartialRun { 1, firstCharacterWidth };
+                return Result { Result::Action::Break, IsEndOfLine::Yes, Result::PartialTrailingContent { leadingTextRunIndex, firstCharacterRun } };
+            }
+            if (trailingContent->overflows && lineStatus.hasContent) {
+                // We managed to break a run with overflow but the line already has content. Let's wrap it to the next line.
+                return Result { Result::Action::Wrap, IsEndOfLine::Yes };
+            }
+            // Either we managed to break with no overflow or the line is empty.
+            auto trailingPartialContent = Result::PartialTrailingContent { overflowingContent.breakingPosition->runIndex, trailingContent->partialRun };
+            return Result { Result::Action::Break, IsEndOfLine::Yes, trailingPartialContent };
+        };
+        if (auto result = tryBreakingContentWithText())
+            return *result;
+    } else if (continuousContent.runs().size() > 1) {
+        // FIXME: Add support for various content.
+        auto& runs = continuousContent.runs();
+        for (size_t i = 0; i < runs.size(); ++i) {
+            if (runs[i].inlineItem.isBox()) {
+                overflowingRunIndex = i;
+                break;
+            }
+        }
+    }
+
+    // If we are not allowed to break this overflowing content, we still need to decide whether keep it or wrap it to the next line.
+    if (!lineStatus.hasContent)
+        return { Result::Action::Keep, IsEndOfLine::No };
+    // Now either wrap this content over to the next line or revert back to an earlier wrapping opportunity, or not wrap at all.
+    auto shouldWrapUnbreakableContentToNextLine = [&] {
+        // The individual runs in this continuous content don't break, let's check if we are allowed to wrap this content to next line (e.g. pre would prevent us from wrapping).
+        // Parent style drives the wrapping behavior here.
+        // e.g. <div style="white-space: nowrap">some text<div style="display: inline-block; white-space: pre-wrap"></div></div>.
+        // While the inline-block has pre-wrap which allows wrapping, the content lives in a nowrap context.
+        return isWrappingAllowed(continuousContent.runs()[overflowingRunIndex].inlineItem);
+    };
+    if (shouldWrapUnbreakableContentToNextLine())
+        return { Result::Action::Wrap, IsEndOfLine::Yes };
+    if (lineStatus.hasWrapOpportunityAtPreviousPosition)
+        return { Result::Action::RevertToLastWrapOpportunity, IsEndOfLine::Yes };
+    return { Result::Action::Keep, IsEndOfLine::No };
+}
+
+OverflowingTextContent InlineContentBreaker::processOverflowingContentWithText(const ContinuousContent& continuousContent, const LineStatus& lineStatus) const
+{
+    auto& runs = continuousContent.runs();
+    ASSERT(!runs.isEmpty());
+
+    auto isBreakableRun = [] (auto& run) {
+        ASSERT(run.inlineItem.isText() || run.inlineItem.isInlineBoxStart() || run.inlineItem.isInlineBoxEnd() || run.inlineItem.layoutBox().isImage());
+        if (!run.inlineItem.isText()) {
+            // Can't break horizontal spacing -> e.g. <span style="padding-right: 100px;">textcontent</span>, if the [inline box end] is the overflown inline item
+            // we need to check if there's another inline item beyond the [inline box end] to split.
+            return false;
+        }
+        // Check if this text run needs to stay on the current line.  
+        return isWrappingAllowed(run.inlineItem);
+    };
+
+    auto findTrailingRunIndex = [&] (auto breakableRunIndex) -> Optional<size_t> {
+        // When the breaking position is at the beginning of the run, the trailing run is the previous one.
+        if (!breakableRunIndex)
+            return { };
+        // Try not break content at inline box boundary
+        // e.g. <span>fits</span><span>overflows</span>
+        // when the text "overflows" completely overflows, let's break the content right before the '<span>'.
+        // FIXME: Add support for subsequent empty inline boxes e.g.
+        auto trailingCandidateIndex = breakableRunIndex - 1;
+        auto isAtInlineBox = runs[trailingCandidateIndex].inlineItem.isInlineBoxStart();
+        return !isAtInlineBox ? trailingCandidateIndex : trailingCandidateIndex ? makeOptional(trailingCandidateIndex - 1) : WTF::nullopt;
+    };
+
+    // 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>
+
+    // First find the overflowing run. 
+    auto accumulatedContentWidth = InlineLayoutUnit { };
+    auto overflowingRunIndex = runs.size(); 
+    for (size_t index = 0; index < runs.size(); ++index) {
+        auto runLogicalWidth = runs[index].logicalWidth;
+        if (accumulatedContentWidth + runLogicalWidth  > lineStatus.availableWidth) {
+            overflowingRunIndex = index;
+            break;
+        }
+        accumulatedContentWidth += runLogicalWidth;
+    }
+    // We have to have an overflowing run.
+    RELEASE_ASSERT(overflowingRunIndex < runs.size());
+
+    auto tryBreakingOverflowingRun = [&]() -> Optional<OverflowingTextContent::BreakingPosition> {
+        auto overflowingRun = runs[overflowingRunIndex];
+        if (!isBreakableRun(overflowingRun))
+            return { };
+        if (auto partialRun = tryBreakingTextRun(overflowingRun, lineStatus.contentLogicalRight + accumulatedContentWidth, std::max(0.0f, lineStatus.availableWidth - accumulatedContentWidth), lineStatus.hasWrapOpportunityAtPreviousPosition)) {
+            if (partialRun->length)
+                return OverflowingTextContent::BreakingPosition { overflowingRunIndex, OverflowingTextContent::BreakingPosition::TrailingContent { false, partialRun } };
+            // When the breaking position is at the beginning of the run, the trailing run is the previous one.
+            if (auto trailingRunIndex = findTrailingRunIndex(overflowingRunIndex))
+                return OverflowingTextContent::BreakingPosition { *trailingRunIndex, OverflowingTextContent::BreakingPosition::TrailingContent { } };
+            // Sometimes we can't accommodate even the very first character. 
+            // Note that this is different from when there's no breakable run in this set.
+            return OverflowingTextContent::BreakingPosition { };
+        }
+        return { };
+    };
+    // Check if we actually break this run.
+    if (auto breakingPosition = tryBreakingOverflowingRun())
+        return { overflowingRunIndex, breakingPosition };
+
+    auto tryBreakingPreviousNonOverflowingRuns = [&]() -> Optional<OverflowingTextContent::BreakingPosition> {
+        auto previousContentWidth = accumulatedContentWidth;
+        for (auto index = overflowingRunIndex; index--;) {
+            auto& run = runs[index];
+            previousContentWidth -= run.logicalWidth;
+            if (!isBreakableRun(run))
+                continue;
+            ASSERT(run.inlineItem.isText());
+            if (auto partialRun = tryBreakingTextRun(run, lineStatus.contentLogicalRight + previousContentWidth, { }, lineStatus.hasWrapOpportunityAtPreviousPosition)) {
+                // We know this run fits, so if breaking is allowed on the run, it should return a non-empty left-side
+                // since it's either at hyphen position or the entire run is returned.
+                ASSERT(partialRun->length);
+                auto runIsFullyAccommodated = partialRun->length == downcast<InlineTextItem>(run.inlineItem).length();
+                return OverflowingTextContent::BreakingPosition { index, OverflowingTextContent::BreakingPosition::TrailingContent { false, runIsFullyAccommodated ? WTF::nullopt : partialRun } };
+            }
+        }
+        return { };
+    };
+    // We did not manage to break the run that actually overflows the line.
+    // Let's try to find a previous breaking position starting from the overflowing run. It surely fits.
+    if (auto breakingPosition = tryBreakingPreviousNonOverflowingRuns())
+        return { overflowingRunIndex, breakingPosition };
+
+    auto tryBreakingNextOverflowingRuns = [&]() -> Optional<OverflowingTextContent::BreakingPosition> {
+        auto nextContentWidth = accumulatedContentWidth + runs[overflowingRunIndex].logicalWidth;
+        for (auto index = overflowingRunIndex + 1; index < runs.size(); ++index) {
+            auto& run = runs[index];
+            if (isBreakableRun(run)) {
+                ASSERT(run.inlineItem.isText());
+                // We know that this run does not fit the available space. If we can break it at any position, let's just use the start of the run.
+                if (wordBreakBehavior(run.inlineItem.style(), lineStatus.hasWrapOpportunityAtPreviousPosition) == WordBreakRule::AtArbitraryPosition) {
+                    // We must be on an inline box boundary. Let's go back to the run in front of the inline box start run.
+                    // e.g. <span>unbreakable_and_overflow<span style="word-break: break-all">breakable</span>
+                    // We are at "breakable", <span> is at index - 1 and the trailing run is at index - 2.
+                    ASSERT(runs[index - 1].inlineItem.isInlineBoxStart());
+                    auto trailingRunIndex = findTrailingRunIndex(index);
+                    if (!trailingRunIndex) {
+                        // This continuous content did not fit from the get-go. No trailing run.
+                        return OverflowingTextContent::BreakingPosition { };
+                    }
+                    // At worst we are back to the overflowing run, like in the example above.
+                    ASSERT(*trailingRunIndex >= overflowingRunIndex);
+                    return OverflowingTextContent::BreakingPosition { *trailingRunIndex, OverflowingTextContent::BreakingPosition::TrailingContent { true } };
+                }
+                if (auto partialRun = tryBreakingTextRun(run, lineStatus.contentLogicalRight + nextContentWidth, { }, lineStatus.hasWrapOpportunityAtPreviousPosition)) {
+                    ASSERT(partialRun->length);
+                    // We managed to break this text run mid content. It has to be a hyphen break.
+                    return OverflowingTextContent::BreakingPosition { index, OverflowingTextContent::BreakingPosition::TrailingContent { true, partialRun } };
+                }
+            }
+            nextContentWidth += run.logicalWidth;
+        }
+        return { };
+    };
+    // At this point we know that there's no breakable run all the way to the overflowing run.
+    // Now we need to check if any run after the overflowing content can break.
+    // e.g. <span>this_content_overflows_but_not_breakable<span><span style="word-break: break-all">but_this_is_breakable</span>
+    if (auto breakingPosition = tryBreakingNextOverflowingRuns())
+        return { overflowingRunIndex, breakingPosition };
+
+    // Give up, there's no breakable run in here.
+    return { overflowingRunIndex };
+}
+
+OptionSet<InlineContentBreaker::WordBreakRule> InlineContentBreaker::wordBreakBehavior(const RenderStyle& style, bool hasWrapOpportunityAtPreviousPosition) const
+{
+    // Disregard any prohibition against line breaks mandated by the word-break property.
+    // The different wrapping opportunities must not be prioritized. Hyphenation is not applied.
+    if (style.lineBreak() == LineBreak::Anywhere)
+        return { WordBreakRule::AtArbitraryPosition };
+    // Breaking is allowed within “words”.
+    if (style.wordBreak() == WordBreak::BreakAll)
+        return { WordBreakRule::AtArbitraryPosition };
+    // Breaking is forbidden within “words”.
+    if (style.wordBreak() == WordBreak::KeepAll)
+        return { };
+
+    auto breakRules = OptionSet<WordBreakRule> { };
+    auto hyphenationIsAllowed = !n_hyphenationIsDisabled && style.hyphens() == Hyphens::Auto && canHyphenate(style.computedLocale());
+    if (hyphenationIsAllowed)
+        breakRules.add({ WordBreakRule::AtHyphenationOpportunities });
+    // For compatibility with legacy content, the word-break property also supports a deprecated break-word keyword.
+    // When specified, this has the same effect as word-break: normal and overflow-wrap: anywhere, regardless of the actual value of the overflow-wrap property.
+    if (style.wordBreak() == WordBreak::BreakWord && !hasWrapOpportunityAtPreviousPosition) {
+        breakRules.add({ WordBreakRule::AtArbitraryPosition });
+        return breakRules;
+    }
+    // OverflowWrap::Break: An otherwise unbreakable sequence of characters may be broken at an arbitrary point if there are no otherwise-acceptable break points in the line.
+    if (style.overflowWrap() == OverflowWrap::Break && !hasWrapOpportunityAtPreviousPosition) {
+        breakRules.add({ WordBreakRule::AtArbitraryPosition });
+        return breakRules;
+    }
+    return breakRules;
+}
+
+Optional<InlineContentBreaker::PartialRun> InlineContentBreaker::tryBreakingTextRun(const ContinuousContent::Run& overflowingRun, InlineLayoutUnit logicalLeft, Optional<InlineLayoutUnit> availableWidth, bool hasWrapOpportunityAtPreviousPosition) const
+{
+    ASSERT(overflowingRun.inlineItem.isText());
+    auto& inlineTextItem = downcast<InlineTextItem>(overflowingRun.inlineItem);
+    auto& style = inlineTextItem.style();
+    auto availableSpaceIsInfinite = !availableWidth.hasValue();
+
+    auto breakRules = wordBreakBehavior(style, hasWrapOpportunityAtPreviousPosition);
+    if (breakRules.isEmpty())
+        return { };
+
+    if (breakRules.contains(WordBreakRule::AtHyphenationOpportunities)) {
+        auto tryBreakingAtHyphenationOpportunity = [&]() -> Optional<PartialRun> {
+            // Find the hyphen position as follows:
+            // 1. Split the text by taking the hyphen width into account
+            // 2. Find the last hyphen position before the split position
+            if (!availableSpaceIsInfinite && !*availableWidth) {
+                // We won't be able to find hyphen location when there's no available space.
+                return { };
+            }
+            auto runLength = inlineTextItem.length();
+            unsigned limitBefore = style.hyphenationLimitBefore() == RenderStyle::initialHyphenationLimitBefore() ? 0 : style.hyphenationLimitBefore();
+            unsigned limitAfter = style.hyphenationLimitAfter() == RenderStyle::initialHyphenationLimitAfter() ? 0 : style.hyphenationLimitAfter();
+            // Check if this run can accommodate the before/after limits at all before start measuring text.
+            if (limitBefore >= runLength || limitAfter >= runLength || limitBefore + limitAfter > runLength)
+                return { };
+
+            unsigned leftSideLength = runLength;
+            auto& fontCascade = style.fontCascade();
+            auto hyphenWidth = InlineLayoutUnit { fontCascade.width(TextRun { StringView { style.hyphenString() } }) };
+            if (!availableSpaceIsInfinite) {
+                auto availableWidthExcludingHyphen = *availableWidth - hyphenWidth;
+                if (availableWidthExcludingHyphen <= 0 || !enoughWidthForHyphenation(availableWidthExcludingHyphen, fontCascade.pixelSize()))
+                    return { };
+                leftSideLength = TextUtil::split(inlineTextItem, overflowingRun.logicalWidth, availableWidthExcludingHyphen, logicalLeft).length;
+            }
+            if (leftSideLength < limitBefore)
+                return { };
+            // Adjust before index to accommodate the limit-after value (it's the last potential hyphen location in this run).
+            auto hyphenBefore = std::min(leftSideLength, runLength - limitAfter) + 1;
+            unsigned hyphenLocation = lastHyphenLocation(StringView(inlineTextItem.inlineTextBox().content()).substring(inlineTextItem.start(), inlineTextItem.length()), hyphenBefore, style.computedLocale());
+            if (!hyphenLocation || hyphenLocation < limitBefore)
+                return { };
+            // hyphenLocation is relative to the start of this InlineItemText.
+            ASSERT(inlineTextItem.start() + hyphenLocation < inlineTextItem.end());
+            auto trailingPartialRunWidthWithHyphen = TextUtil::width(inlineTextItem, inlineTextItem.start(), inlineTextItem.start() + hyphenLocation, logicalLeft);
+            return PartialRun { hyphenLocation, trailingPartialRunWidthWithHyphen, hyphenWidth };
+        };
+        if (auto partialRun = tryBreakingAtHyphenationOpportunity())
+            return partialRun;
+    }
+
+    if (breakRules.contains(WordBreakRule::AtArbitraryPosition)) {
+        auto tryBreakingAtArbitraryPosition = [&]() -> PartialRun {
+            if (!inlineTextItem.length()) {
+                // Empty text runs may be breakable based on style, but in practice we can't really split them any further.
+                return { };
+            }
+            if (availableSpaceIsInfinite) {
+                // When the run can be split at arbitrary position let's just return the entire run when it is intended to fit on the line.
+                ASSERT(inlineTextItem.length());
+                auto trailingPartialRunWidth = TextUtil::width(inlineTextItem, logicalLeft);
+                return { inlineTextItem.length(), trailingPartialRunWidth };
+            }
+            if (!*availableWidth) {
+                // Fast path for cases when there's no room at all. The content is breakable but we don't have space for it.
+                return { };
+            }
+            auto splitData = TextUtil::split(inlineTextItem, overflowingRun.logicalWidth, *availableWidth, logicalLeft);
+            return { splitData.length, splitData.logicalWidth };
+        };
+        // With arbitrary breaking there's always a valid breaking position (even if it is before the first position).
+        return tryBreakingAtArbitraryPosition();
+    }
+    return { };
+}
+
+void InlineContentBreaker::ContinuousContent::append(const InlineItem& inlineItem, InlineLayoutUnit logicalWidth, Optional<InlineLayoutUnit> collapsibleWidth)
+{
+    m_runs.append({ inlineItem, logicalWidth });
+    m_logicalWidth = clampTo<InlineLayoutUnit>(m_logicalWidth + logicalWidth);
+    if (!collapsibleWidth) {
+        m_collapsibleLogicalWidth = { };
+        return;
+    }
+    if (*collapsibleWidth == logicalWidth) {
+        // Fully collapsible run.
+        m_collapsibleLogicalWidth += logicalWidth;
+        ASSERT(m_collapsibleLogicalWidth <= m_logicalWidth);
+        return;
+    }
+    // Partially collapsible run.
+    m_collapsibleLogicalWidth = *collapsibleWidth;
+    ASSERT(m_collapsibleLogicalWidth <= m_logicalWidth);
+}
+
+void InlineContentBreaker::ContinuousContent::reset()
+{
+    m_logicalWidth = { };
+    m_collapsibleLogicalWidth = { };
+    m_runs.clear();
+}
+
+}
+}
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineContentBreakerhfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingInlineContentBreakerh"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/InlineContentBreaker.h (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/InlineContentBreaker.h) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineContentBreaker.h                             (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineContentBreaker.h        2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,150 @@
</span><ins>+/*
+ * Copyright (C) 2018 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 "LayoutUnits.h"
+
+namespace WebCore {
+
+class RenderStyle;
+
+namespace Layout {
+
+class InlineItem;
+struct OverflowingTextContent;
+
+class InlineContentBreaker {
+public:
+    struct PartialRun {
+        size_t length { 0 };
+        InlineLayoutUnit logicalWidth { 0 };
+        Optional<InlineLayoutUnit> hyphenWidth { };
+    };
+    enum class IsEndOfLine { No, Yes };
+    struct Result {
+        enum class Action {
+            Keep, // Keep content on the current line.
+            Break, // Partial content is on the current line.
+            Wrap, // Content is wrapped to the next line.
+            WrapWithHyphen, // Content is wrapped to the next line and the current line ends with a visible hyphen.
+            // The current content overflows and can't get broken up into smaller bits.
+            RevertToLastWrapOpportunity, // The content needs to be reverted back to the last wrap opportunity.
+            RevertToLastNonOverflowingWrapOpportunity // The content needs to be reverted back to a wrap opportunity that still fits the line.
+        };
+        struct PartialTrailingContent {
+            size_t trailingRunIndex { 0 };
+            Optional<PartialRun> partialRun; // nullopt partial run means the trailing run is a complete run.
+        };
+        Action action { Action::Keep };
+        IsEndOfLine isEndOfLine { IsEndOfLine::No };
+        Optional<PartialTrailingContent> partialTrailingContent { };
+        const InlineItem* lastWrapOpportunityItem { nullptr };
+    };
+
+    // This struct represents the amount of continuous content committed to content breaking at a time (no in-between wrap opportunities).
+    // e.g.
+    // <div>text content <span>span1</span>between<span>span2</span></div>
+    // [text][ ][content][ ][inline box start][span1][inline box end][between][inline box start][span2][inline box end]
+    // continuous candidate content at a time:
+    // 1. [text]
+    // 2. [ ]
+    // 3. [content]
+    // 4. [ ]
+    // 5. [inline box start][span1][inline box end][between][inline box start][span2][inline box end]
+    // see https://drafts.csswg.org/css-text-3/#line-break-details
+    struct ContinuousContent {
+        InlineLayoutUnit logicalWidth() const { return m_logicalWidth; }
+        InlineLayoutUnit collapsibleLogicalWidth() const { return m_collapsibleLogicalWidth; }
+        InlineLayoutUnit nonCollapsibleLogicalWidth() const { return logicalWidth() - collapsibleLogicalWidth(); }
+        bool hasTrailingCollapsibleContent() const { return !!collapsibleLogicalWidth(); }
+        bool isFullyCollapsible() const { return logicalWidth() == collapsibleLogicalWidth(); }
+
+        void append(const InlineItem&, InlineLayoutUnit logicalWidth, Optional<InlineLayoutUnit> collapsibleWidth);
+        void reset();
+
+        struct Run {
+            Run(const InlineItem&, InlineLayoutUnit logicalWidth);
+            Run(const Run&);
+            Run& operator=(const Run&);
+
+            const InlineItem& inlineItem;
+            InlineLayoutUnit logicalWidth { 0 };
+        };
+        using RunList = Vector<Run, 3>;
+        const RunList& runs() const { return m_runs; }
+
+    private:
+        RunList m_runs;
+        InlineLayoutUnit m_logicalWidth { 0 };
+        InlineLayoutUnit m_collapsibleLogicalWidth { 0 };
+    };
+
+    struct LineStatus {
+        InlineLayoutUnit contentLogicalRight { 0 };
+        InlineLayoutUnit availableWidth { 0 };
+        InlineLayoutUnit collapsibleWidth { 0 };
+        Optional<InlineLayoutUnit> trailingSoftHyphenWidth;
+        bool hasFullyCollapsibleTrailingRun { false };
+        bool hasContent { false };
+        bool hasWrapOpportunityAtPreviousPosition { false };
+    };
+    Result processInlineContent(const ContinuousContent&, const LineStatus&);
+    void setHyphenationDisabled() { n_hyphenationIsDisabled = true; }
+
+    static bool isWrappingAllowed(const InlineItem&);
+
+private:
+    Result processOverflowingContent(const ContinuousContent&, const LineStatus&) const;
+    OverflowingTextContent processOverflowingContentWithText(const ContinuousContent&, const LineStatus&) const;
+    Optional<PartialRun> tryBreakingTextRun(const ContinuousContent::Run& overflowRun, InlineLayoutUnit logicalLeft, Optional<InlineLayoutUnit> availableWidth, bool hasWrapOpportunityAtPreviousPosition) const;
+
+    enum class WordBreakRule {
+        AtArbitraryPosition        = 1 << 0,
+        AtHyphenationOpportunities = 1 << 1
+    };
+    OptionSet<WordBreakRule> wordBreakBehavior(const RenderStyle&, bool hasWrapOpportunityAtPreviousPosition) const;
+    bool shouldKeepEndOfLineWhitespace(const ContinuousContent&) const;
+
+    bool n_hyphenationIsDisabled { false };
+};
+
+inline InlineContentBreaker::ContinuousContent::Run::Run(const InlineItem& inlineItem, InlineLayoutUnit logicalWidth)
+    : inlineItem(inlineItem)
+    , logicalWidth(logicalWidth)
+{
+}
+
+inline InlineContentBreaker::ContinuousContent::Run::Run(const Run& other)
+    : inlineItem(other.inlineItem)
+    , logicalWidth(other.logicalWidth)
+{
+}
+
+}
+}
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineFormattingContextcppfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingInlineFormattingContextcpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContext.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/InlineFormattingContext.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContext.cpp                                (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContext.cpp   2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,611 @@
</span><ins>+/*
+ * Copyright (C) 2018 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 "InlineFormattingContext.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "FloatingContext.h"
+#include "FontCascade.h"
+#include "InlineFormattingState.h"
+#include "InlineLineBox.h"
+#include "InlineLineRun.h"
+#include "InlineTextItem.h"
+#include "InvalidationState.h"
+#include "LayoutBox.h"
+#include "LayoutContainerBox.h"
+#include "LayoutContext.h"
+#include "LayoutInitialContainingBlock.h"
+#include "LayoutInlineTextBox.h"
+#include "LayoutLineBreakBox.h"
+#include "LayoutReplacedBox.h"
+#include "LayoutState.h"
+#include "Logging.h"
+#include "RuntimeEnabledFeatures.h"
+#include "TextUtil.h"
+#include <wtf/IsoMallocInlines.h>
+#include <wtf/text/TextStream.h>
+
+namespace WebCore {
+namespace Layout {
+
+WTF_MAKE_ISO_ALLOCATED_IMPL(InlineFormattingContext);
+
+InlineFormattingContext::InlineFormattingContext(const ContainerBox& formattingContextRoot, InlineFormattingState& formattingState)
+    : FormattingContext(formattingContextRoot, formattingState)
+{
+}
+
+static inline const Box* nextInlineLevelBoxToLayout(const Box& layoutBox, const ContainerBox& stayWithin)
+{
+    // Atomic inline-level boxes and floats are opaque boxes meaning that they are
+    // responsible for their own content (do not need to descend into their subtrees).
+    // Only inline boxes may have relevant descendant content.
+    if (layoutBox.isInlineBox()) {
+        if (is<ContainerBox>(layoutBox) && downcast<ContainerBox>(layoutBox).hasInFlowOrFloatingChild()) {
+            // Anonymous inline text boxes/line breaks can't have descendant content by definition.
+            ASSERT(!layoutBox.isInlineTextBox() && !layoutBox.isLineBreakBox());
+            return downcast<ContainerBox>(layoutBox).firstInFlowOrFloatingChild();
+        }
+    }
+
+    for (auto* nextInPreOrder = &layoutBox; nextInPreOrder && nextInPreOrder != &stayWithin; nextInPreOrder = &nextInPreOrder->parent()) {
+        if (auto* nextSibling = nextInPreOrder->nextInFlowOrFloatingSibling())
+            return nextSibling;
+    }
+    return nullptr;
+}
+
+void InlineFormattingContext::layoutInFlowContent(InvalidationState& invalidationState, const ConstraintsForInFlowContent& constraints)
+{
+    LOG_WITH_STREAM(FormattingContextLayout, stream << "[Start] -> inline formatting context -> formatting root(" << &root() << ")");
+    ASSERT(root().hasInFlowOrFloatingChild());
+
+    invalidateFormattingState(invalidationState);
+    auto* layoutBox = root().firstInFlowOrFloatingChild();
+    // 1. Visit each inline box and partially compute their geometry (margins, padding and borders).
+    // 2. Collect the inline items (flatten the the layout tree) and place them on lines in bidirectional order. 
+    while (layoutBox) {
+        ASSERT(layoutBox->isInlineLevelBox() || layoutBox->isFloatingPositioned());
+
+        if (layoutBox->isAtomicInlineLevelBox() || layoutBox->isFloatingPositioned()) {
+            // Inline-blocks, inline-tables and replaced elements (img, video) can be sized but not yet positioned.
+            if (is<ContainerBox>(layoutBox) && layoutBox->establishesFormattingContext()) {
+                ASSERT(layoutBox->isInlineBlockBox() || layoutBox->isInlineTableBox() || layoutBox->isFloatingPositioned());
+                auto& formattingRoot = downcast<ContainerBox>(*layoutBox);
+                computeBorderAndPadding(formattingRoot, constraints.horizontal);
+                computeWidthAndMargin(formattingRoot, constraints.horizontal);
+
+                if (formattingRoot.hasChild()) {
+                    auto formattingContext = LayoutContext::createFormattingContext(formattingRoot, layoutState());
+                    if (formattingRoot.hasInFlowOrFloatingChild())
+                        formattingContext->layoutInFlowContent(invalidationState, geometry().constraintsForInFlowContent(formattingRoot));
+                    computeHeightAndMargin(formattingRoot, constraints.horizontal);
+                    formattingContext->layoutOutOfFlowContent(invalidationState, geometry().constraintsForOutOfFlowContent(formattingRoot));
+                } else
+                    computeHeightAndMargin(formattingRoot, constraints.horizontal);
+            } else {
+                // Replaced and other type of leaf atomic inline boxes.
+                computeBorderAndPadding(*layoutBox, constraints.horizontal);
+                computeWidthAndMargin(*layoutBox, constraints.horizontal);
+                computeHeightAndMargin(*layoutBox, constraints.horizontal);
+            }
+        } else if (layoutBox->isLineBreakBox()) {
+            auto& boxGeometry = formattingState().boxGeometry(*layoutBox);
+            boxGeometry.setHorizontalMargin({ });
+            boxGeometry.setBorder({ });
+            boxGeometry.setPadding({ });
+            boxGeometry.setContentBoxWidth({ });
+            boxGeometry.setVerticalMargin({ });
+        } else if (layoutBox->isInlineBox()) {
+            // Text wrapper boxes (anonymous inline level boxes) don't have box geometries (they only generate runs).
+            if (!layoutBox->isInlineTextBox()) {
+                // 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);
+                computeHorizontalMargin(*layoutBox, constraints.horizontal);
+                formattingState().boxGeometry(*layoutBox).setVerticalMargin({ });
+            }
+        } else
+            ASSERT_NOT_REACHED();
+
+        layoutBox = nextInlineLevelBoxToLayout(*layoutBox, root());
+    }
+
+    collectInlineContentIfNeeded();
+
+    auto& inlineItems = formattingState().inlineItems();
+    lineLayout(inlineItems, { 0, inlineItems.size() }, constraints);
+    LOG_WITH_STREAM(FormattingContextLayout, stream << "[End] -> inline formatting context -> formatting root(" << &root() << ")");
+}
+
+void InlineFormattingContext::lineLayoutForIntergration(InvalidationState& invalidationState, const ConstraintsForInFlowContent& constraints)
+{
+    invalidateFormattingState(invalidationState);
+    collectInlineContentIfNeeded();
+    auto& inlineItems = formattingState().inlineItems();
+    lineLayout(inlineItems, { 0, inlineItems.size() }, constraints);
+}
+
+LayoutUnit InlineFormattingContext::usedContentHeight() const
+{
+    // 10.6.7 'Auto' heights for block formatting context roots
+
+    // If it only has inline-level children, the height is the distance between the top of the topmost line box and the bottom of the bottommost line box.
+
+    // In addition, if the element has any floating descendants whose bottom margin edge is below the element's bottom content edge,
+    // then the height is increased to include those edges. Only floats that participate in this block formatting context are taken
+    // into account, e.g., floats inside absolutely positioned descendants or other floats are not.
+    auto& lines = formattingState().lines();
+    // Even empty content generates a line.
+    ASSERT(!lines.isEmpty());
+    auto top = LayoutUnit { lines.first().lineBoxLogicalRect().top() };
+    auto bottom = LayoutUnit { lines.last().lineBoxLogicalRect().bottom() + formattingState().clearGapAfterLastLine() };
+
+    auto floatingContext = FloatingContext { *this, formattingState().floatingState() };
+    if (auto floatBottom = floatingContext.bottom()) {
+        bottom = std::max(*floatBottom, bottom);
+        top = std::min(*floatingContext.top(), top);
+    }
+    return bottom - top;
+}
+
+void InlineFormattingContext::lineLayout(InlineItems& inlineItems, LineBuilder::InlineItemRange needsLayoutRange, const ConstraintsForInFlowContent& constraints)
+{
+    auto& formattingState = this->formattingState();
+    formattingState.lineRuns().reserveInitialCapacity(formattingState.inlineItems().size());
+    InlineLayoutUnit lineLogicalTop = constraints.vertical.logicalTop;
+    struct PreviousLine {
+        LineBuilder::InlineItemRange range;
+        size_t overflowContentLength { 0 };
+        Optional<InlineLayoutUnit> overflowLogicalWidth;
+    };
+    Optional<PreviousLine> previousLine;
+    auto& floatingState = formattingState.floatingState();
+    auto floatingContext = FloatingContext { *this, floatingState };
+    auto isFirstLine = formattingState.lines().isEmpty();
+
+    auto lineBuilder = LineBuilder { *this, floatingState, constraints.horizontal, inlineItems };
+    while (!needsLayoutRange.isEmpty()) {
+        // Turn previous line's overflow content length into the next line's leading content partial length.
+        // "sp[<-line break->]lit_content" -> overflow length: 11 -> leading partial content length: 11.
+        auto partialLeadingContentLength = previousLine ? previousLine->overflowContentLength : 0;
+        auto leadingLogicalWidth = previousLine ? previousLine->overflowLogicalWidth : WTF::nullopt;
+        auto initialLineConstraints = InlineRect { lineLogicalTop, constraints.horizontal.logicalLeft, constraints.horizontal.logicalWidth, quirks().initialLineHeight() };
+        auto lineContent = lineBuilder.layoutInlineContent(needsLayoutRange, partialLeadingContentLength, leadingLogicalWidth, initialLineConstraints, isFirstLine);
+        auto lineLogicalRect = computeGeometryForLineContent(lineContent, constraints.horizontal);
+
+        auto lineContentRange = lineContent.inlineItemRange;
+        if (!lineContentRange.isEmpty()) {
+            ASSERT(needsLayoutRange.start < lineContentRange.end);
+            isFirstLine = false;
+            lineLogicalTop = geometry().logicalTopForNextLine(lineContent, lineLogicalRect.bottom(), floatingContext);
+            if (lineContent.isLastLineWithInlineContent) {
+                // The final content height of this inline formatting context should include the cleared floats as well.
+                formattingState.setClearGapAfterLastLine(lineLogicalTop - lineLogicalRect.bottom());
+            }
+            // When the trailing content is partial, we need to reuse the last InlineTextItem.
+            auto lastInlineItemNeedsPartialLayout = lineContent.partialTrailingContentLength;
+            if (lastInlineItemNeedsPartialLayout) {
+                auto lineLayoutHasAdvanced = !previousLine
+                    || lineContentRange.end > previousLine->range.end
+                    || (previousLine->overflowContentLength && previousLine->overflowContentLength > lineContent.partialTrailingContentLength);
+                if (!lineLayoutHasAdvanced) {
+                    ASSERT_NOT_REACHED();
+                    // Move over to the next run if we are stuck on this partial content (when the overflow content length remains the same).
+                    // We certainly lose some content, but we would be busy looping otherwise.
+                    lastInlineItemNeedsPartialLayout = false;
+                }
+            }
+            needsLayoutRange.start = lastInlineItemNeedsPartialLayout ? lineContentRange.end - 1 : lineContentRange.end;
+            previousLine = PreviousLine { lineContentRange, lineContent.partialTrailingContentLength, lineContent.overflowLogicalWidth };
+            continue;
+        }
+        // Floats prevented us placing any content on the line.
+        ASSERT(lineContent.runs.isEmpty());
+        ASSERT(lineContent.hasIntrusiveFloat);
+        // Move the next line below the intrusive float.
+        auto floatConstraints = floatingContext.constraints(toLayoutUnit(lineLogicalTop), toLayoutUnit(lineLogicalRect.bottom()));
+        ASSERT(floatConstraints.left || floatConstraints.right);
+        static auto inifitePoint = PointInContextRoot::max();
+        // In case of left and right constraints, we need to pick the one that's closer to the current line.
+        lineLogicalTop = std::min(floatConstraints.left.valueOr(inifitePoint).y, floatConstraints.right.valueOr(inifitePoint).y);
+        ASSERT(lineLogicalTop < inifitePoint.y);
+    }
+}
+
+FormattingContext::IntrinsicWidthConstraints InlineFormattingContext::computedIntrinsicWidthConstraints()
+{
+    auto& layoutState = this->layoutState();
+    ASSERT(!formattingState().intrinsicWidthConstraints());
+
+    if (!root().hasInFlowOrFloatingChild()) {
+        auto constraints = geometry().constrainByMinMaxWidth(root(), { });
+        formattingState().setIntrinsicWidthConstraints(constraints);
+        return constraints;
+    }
+
+    Vector<const Box*> formattingContextRootList;
+    auto horizontalConstraints = HorizontalConstraints { 0_lu, 0_lu };
+    auto* layoutBox = root().firstInFlowOrFloatingChild();
+    // In order to compute the max/min widths, we need to compute margins, borders and padding for certain inline boxes first.
+    while (layoutBox) {
+        if (layoutBox->isInlineTextBox()) {
+            layoutBox = nextInlineLevelBoxToLayout(*layoutBox, root());
+            continue;
+        }
+        if (layoutBox->isReplacedBox()) {
+            computeBorderAndPadding(*layoutBox, horizontalConstraints);
+            computeWidthAndMargin(*layoutBox, horizontalConstraints);
+        } else if (layoutBox->isFloatingPositioned() || layoutBox->isAtomicInlineLevelBox()) {
+            ASSERT(layoutBox->establishesFormattingContext());
+            formattingContextRootList.append(layoutBox);
+
+            computeBorderAndPadding(*layoutBox, horizontalConstraints);
+            computeHorizontalMargin(*layoutBox, horizontalConstraints);
+            computeIntrinsicWidthForFormattingRoot(*layoutBox);
+        } else if (layoutBox->isInlineBox()) {
+            computeBorderAndPadding(*layoutBox, horizontalConstraints);
+            computeHorizontalMargin(*layoutBox, horizontalConstraints);
+        } else
+            ASSERT_NOT_REACHED();
+        layoutBox = nextInlineLevelBoxToLayout(*layoutBox, root());
+    }
+
+    collectInlineContentIfNeeded();
+
+    auto maximumLineWidth = [&](auto availableWidth) {
+        // Switch to the min/max formatting root width values before formatting the lines.
+        for (auto* formattingRoot : formattingContextRootList) {
+            auto intrinsicWidths = layoutState.formattingStateForBox(*formattingRoot).intrinsicWidthConstraintsForBox(*formattingRoot);
+            auto& boxGeometry = formattingState().boxGeometry(*formattingRoot);
+            auto contentWidth = (availableWidth ? intrinsicWidths->maximum : intrinsicWidths->minimum) - boxGeometry.horizontalMarginBorderAndPadding();
+            boxGeometry.setContentBoxWidth(contentWidth);
+        }
+        return computedIntrinsicWidthForConstraint(availableWidth);
+    };
+
+    auto minimumContentWidth = ceiledLayoutUnit(maximumLineWidth(0));
+    auto maximumContentWidth = ceiledLayoutUnit(maximumLineWidth(maxInlineLayoutUnit()));
+    auto constraints = geometry().constrainByMinMaxWidth(root(), { minimumContentWidth, maximumContentWidth });
+    formattingState().setIntrinsicWidthConstraints(constraints);
+    return constraints;
+}
+
+InlineLayoutUnit InlineFormattingContext::computedIntrinsicWidthForConstraint(InlineLayoutUnit availableWidth) const
+{
+    auto& inlineItems = formattingState().inlineItems();
+    auto lineBuilder = LineBuilder { *this, inlineItems };
+    auto layoutRange = LineBuilder::InlineItemRange { 0 , inlineItems.size() };
+    auto maximumLineWidth = InlineLayoutUnit { };
+    auto maximumFloatWidth = LayoutUnit { };
+    while (!layoutRange.isEmpty()) {
+        auto intrinsicContent = lineBuilder.computedIntrinsicWidth(layoutRange, availableWidth);
+        layoutRange.start = intrinsicContent.inlineItemRange.end;
+        maximumLineWidth = std::max(maximumLineWidth, intrinsicContent.logicalWidth);
+        // FIXME: Add support for clear.
+        for (auto* floatBox : intrinsicContent.floats)
+            maximumFloatWidth += geometryForBox(*floatBox).marginBoxWidth();
+    }
+    return maximumLineWidth + maximumFloatWidth;
+}
+
+void InlineFormattingContext::computeIntrinsicWidthForFormattingRoot(const Box& formattingRoot)
+{
+    ASSERT(formattingRoot.establishesFormattingContext());
+    auto constraints = IntrinsicWidthConstraints { };
+    if (auto fixedWidth = geometry().fixedValue(formattingRoot.style().logicalWidth()))
+        constraints = { *fixedWidth, *fixedWidth };
+    else {
+        auto hasInflowOrFloatingContent = is<ContainerBox>(formattingRoot) && downcast<ContainerBox>(formattingRoot).hasInFlowOrFloatingChild();
+        // The intrinsic sizes of the size containment box are determined as if the element had no content.
+        auto shouldIgnoreChildContent = formattingRoot.isSizeContainmentBox();
+        if (hasInflowOrFloatingContent && !shouldIgnoreChildContent)
+            constraints = LayoutContext::createFormattingContext(downcast<ContainerBox>(formattingRoot), layoutState())->computedIntrinsicWidthConstraints();
+    }
+    constraints = geometry().constrainByMinMaxWidth(formattingRoot, constraints);
+    constraints.expand(geometryForBox(formattingRoot).horizontalMarginBorderAndPadding());
+    formattingState().setIntrinsicWidthConstraintsForBox(formattingRoot, constraints);
+}
+
+void InlineFormattingContext::computeHorizontalMargin(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints)
+{
+    auto computedHorizontalMargin = geometry().computedHorizontalMargin(layoutBox, horizontalConstraints);
+    formattingState().boxGeometry(layoutBox).setHorizontalMargin({ computedHorizontalMargin.start.valueOr(0), computedHorizontalMargin.end.valueOr(0) });
+}
+
+void InlineFormattingContext::computeWidthAndMargin(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints)
+{
+    auto compute = [&](Optional<LayoutUnit> usedWidth) {
+        if (layoutBox.isFloatingPositioned())
+            return geometry().floatingContentWidthAndMargin(layoutBox, horizontalConstraints, { usedWidth, { } });
+        if (layoutBox.isInlineBlockBox())
+            return geometry().inlineBlockContentWidthAndMargin(layoutBox, horizontalConstraints, { usedWidth, { } });
+        if (layoutBox.isReplacedBox())
+            return geometry().inlineReplacedContentWidthAndMargin(downcast<ReplacedBox>(layoutBox), horizontalConstraints, { }, { usedWidth, { } });
+        ASSERT_NOT_REACHED();
+        return ContentWidthAndMargin { };
+    };
+
+    auto contentWidthAndMargin = compute({ });
+
+    auto availableWidth = horizontalConstraints.logicalWidth;
+    if (auto maxWidth = geometry().computedMaxWidth(layoutBox, availableWidth)) {
+        auto maxWidthAndMargin = compute(maxWidth);
+        if (contentWidthAndMargin.contentWidth > maxWidthAndMargin.contentWidth)
+            contentWidthAndMargin = maxWidthAndMargin;
+    }
+
+    auto minWidth = geometry().computedMinWidth(layoutBox, availableWidth).valueOr(0);
+    auto minWidthAndMargin = compute(minWidth);
+    if (contentWidthAndMargin.contentWidth < minWidthAndMargin.contentWidth)
+        contentWidthAndMargin = minWidthAndMargin;
+
+    auto& boxGeometry = formattingState().boxGeometry(layoutBox);
+    boxGeometry.setContentBoxWidth(contentWidthAndMargin.contentWidth);
+    boxGeometry.setHorizontalMargin({ contentWidthAndMargin.usedMargin.start, contentWidthAndMargin.usedMargin.end });
+}
+
+void InlineFormattingContext::computeHeightAndMargin(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints)
+{
+    auto compute = [&](Optional<LayoutUnit> usedHeight) {
+        if (layoutBox.isFloatingPositioned())
+            return geometry().floatingContentHeightAndMargin(layoutBox, horizontalConstraints, { usedHeight });
+        if (layoutBox.isInlineBlockBox())
+            return geometry().inlineBlockContentHeightAndMargin(layoutBox, horizontalConstraints, { usedHeight });
+        if (layoutBox.isReplacedBox())
+            return geometry().inlineReplacedContentHeightAndMargin(downcast<ReplacedBox>(layoutBox), horizontalConstraints, { }, { usedHeight });
+        ASSERT_NOT_REACHED();
+        return ContentHeightAndMargin { };
+    };
+
+    auto contentHeightAndMargin = compute({ });
+    if (auto maxHeight = geometry().computedMaxHeight(layoutBox)) {
+        auto maxHeightAndMargin = compute(maxHeight);
+        if (contentHeightAndMargin.contentHeight > maxHeightAndMargin.contentHeight)
+            contentHeightAndMargin = maxHeightAndMargin;
+    }
+
+    if (auto minHeight = geometry().computedMinHeight(layoutBox)) {
+        auto minHeightAndMargin = compute(minHeight);
+        if (contentHeightAndMargin.contentHeight < minHeightAndMargin.contentHeight)
+            contentHeightAndMargin = minHeightAndMargin;
+    }
+    auto& boxGeometry = formattingState().boxGeometry(layoutBox);
+    boxGeometry.setContentBoxHeight(contentHeightAndMargin.contentHeight);
+    boxGeometry.setVerticalMargin({ contentHeightAndMargin.nonCollapsedMargin.before, contentHeightAndMargin.nonCollapsedMargin.after });
+}
+
+void InlineFormattingContext::collectInlineContentIfNeeded()
+{
+    auto& formattingState = this->formattingState();
+    if (!formattingState.inlineItems().isEmpty())
+        return;
+    // Traverse the tree and create inline items out of inline boxes and leaf nodes. This essentially turns the tree inline structure into a flat one.
+    // <span>text<span></span><img></span> -> [InlineBoxStart][InlineLevelBox][InlineBoxStart][InlineBoxEnd][InlineLevelBox][InlineBoxEnd]
+    ASSERT(root().hasInFlowOrFloatingChild());
+    LayoutQueue layoutQueue;
+    layoutQueue.append(root().firstInFlowOrFloatingChild());
+    while (!layoutQueue.isEmpty()) {
+        while (true) {
+            auto& layoutBox = *layoutQueue.last();
+            auto isBoxWithInlineContent = layoutBox.isInlineBox() && !layoutBox.isInlineTextBox() && !layoutBox.isLineBreakBox();
+            if (!isBoxWithInlineContent)
+                break;
+            // This is the start of an inline box (e.g. <span>).
+            formattingState.addInlineItem({ layoutBox, InlineItem::Type::InlineBoxStart });
+            auto& inlineBoxWithInlineContent = downcast<ContainerBox>(layoutBox);
+            if (!inlineBoxWithInlineContent.hasInFlowOrFloatingChild())
+                break;
+            layoutQueue.append(inlineBoxWithInlineContent.firstInFlowOrFloatingChild());
+        }
+
+        while (!layoutQueue.isEmpty()) {
+            auto& layoutBox = *layoutQueue.takeLast();
+            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())
+                formattingState.addInlineItem({ layoutBox, InlineItem::Type::Box });
+            else if (layoutBox.isInlineTextBox()) {
+                InlineTextItem::createAndAppendTextItems(formattingState.inlineItems(), downcast<InlineTextBox>(layoutBox));
+            } else if (layoutBox.isInlineBox())
+                formattingState.addInlineItem({ layoutBox, InlineItem::Type::InlineBoxEnd });
+            else
+                ASSERT_NOT_REACHED();
+
+            if (auto* nextSibling = layoutBox.nextInFlowOrFloatingSibling()) {
+                layoutQueue.append(nextSibling);
+                break;
+            }
+        }
+    }
+}
+
+InlineRect InlineFormattingContext::computeGeometryForLineContent(const LineBuilder::LineContent& lineContent, const HorizontalConstraints& horizontalConstraints)
+{
+    auto& formattingState = this->formattingState();
+    auto geometry = this->geometry();
+
+    formattingState.addLineBox(geometry.lineBoxForLineContent(lineContent));
+    const auto& lineBox = formattingState.lineBoxes().last();
+    auto lineIndex = formattingState.lines().size();
+    auto& lineBoxLogicalRect = lineBox.logicalRect();
+    if (!lineBox.hasContent()) {
+        // Fast path for lines with no content e.g. <div><span></span><span></span></div> or <span><div></div></span> where we construct empty pre and post blocks.
+        ASSERT(!lineBox.contentLogicalWidth() && !lineBoxLogicalRect.height());
+        auto updateInlineBoxesGeometryIfApplicable = [&] {
+            if (!lineBox.hasInlineBox())
+                return;
+            Vector<const Box*> layoutBoxList;
+            // Collect the empty inline boxes that showed up first on this line.
+            // Note that an inline box end on an empty line does not make the inline box taller.
+            // (e.g. <div>text<span><br></span></div>) <- the <span> inline box is as tall as the line even though the </span> is after a <br> so technically is on the following (empty)line.
+            for (auto& lineRun : lineContent.runs) {
+                if (lineRun.isInlineBoxStart())
+                    layoutBoxList.append(&lineRun.layoutBox());
+            }
+            for (auto* layoutBox : layoutBoxList) {
+                auto& boxGeometry = formattingState.boxGeometry(*layoutBox);
+                auto inlineBoxLogicalHeight = LayoutUnit::fromFloatCeil(lineBox.logicalBorderBoxForInlineBox(*layoutBox, boxGeometry).height());
+                boxGeometry.setContentBoxHeight(inlineBoxLogicalHeight);
+                boxGeometry.setContentBoxWidth({ });
+                boxGeometry.setLogicalTopLeft(toLayoutPoint(lineBoxLogicalRect.topLeft()));
+            }
+        };
+        updateInlineBoxesGeometryIfApplicable();
+        formattingState.addLine({ lineBoxLogicalRect, { { }, { } }, { }, { }, { } });
+        return lineBoxLogicalRect;
+    }
+
+    auto rootInlineBoxLogicalRect = lineBox.logicalRectForRootInlineBox();
+    auto enclosingTopAndBottom = InlineLineGeometry::EnclosingTopAndBottom { rootInlineBoxLogicalRect.top(), rootInlineBoxLogicalRect.bottom() };
+    HashSet<const Box*> inlineBoxStartSet;
+    HashSet<const Box*> inlineBoxEndSet;
+
+    auto constructLineRunsAndUpdateBoxGeometry = [&] {
+        // Create the inline runs on the current line. This is mostly text and atomic inline runs.
+        for (auto& lineRun : lineContent.runs) {
+            // FIXME: We should not need to construct a line run for <br>.
+            auto& layoutBox = lineRun.layoutBox();
+            if (lineRun.isText()) {
+                formattingState.addLineRun({ lineIndex, layoutBox, lineBox.logicalRectForTextRun(lineRun), lineRun.expansion(), lineRun.textContent() });
+                continue;
+            }
+            if (lineRun.isLineBreak()) {
+                if (layoutBox.isLineBreakBox()) {
+                    // Only hard linebreaks have associated layout boxes.
+                    auto lineBreakBoxRect = lineBox.logicalRectForLineBreakBox(layoutBox);
+                    formattingState.addLineRun({ lineIndex, layoutBox, lineBreakBoxRect, lineRun.expansion(), { } });
+
+                    auto& boxGeometry = formattingState.boxGeometry(layoutBox);
+                    lineBreakBoxRect.moveBy(lineBoxLogicalRect.topLeft());
+                    boxGeometry.setLogicalTopLeft(toLayoutPoint(lineBreakBoxRect.topLeft()));
+                    boxGeometry.setContentBoxHeight(toLayoutUnit(lineBreakBoxRect.height()));
+                } else 
+                    formattingState.addLineRun({ lineIndex, layoutBox, lineBox.logicalRectForTextRun(lineRun), lineRun.expansion(), lineRun.textContent() });
+                continue;
+            }
+            if (lineRun.isBox()) {
+                ASSERT(layoutBox.isAtomicInlineLevelBox());
+                auto& boxGeometry = formattingState.boxGeometry(layoutBox);
+                auto logicalBorderBox = lineBox.logicalBorderBoxForAtomicInlineLevelBox(layoutBox, boxGeometry);
+                formattingState.addLineRun({ lineIndex, layoutBox, logicalBorderBox, lineRun.expansion(), { } });
+
+                auto borderBoxLogicalTopLeft = logicalBorderBox.topLeft();
+                // Note that inline boxes are relative to the line and their top position can be negative.
+                borderBoxLogicalTopLeft.moveBy(lineBoxLogicalRect.topLeft());
+                if (layoutBox.isInFlowPositioned())
+                    borderBoxLogicalTopLeft += geometry.inFlowPositionedPositionOffset(layoutBox, horizontalConstraints);
+                // 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(borderBoxLogicalTopLeft));
+
+                auto borderBoxTop = borderBoxLogicalTopLeft.y();
+                auto borderBoxBottom = borderBoxTop + boxGeometry.borderBoxHeight();
+                enclosingTopAndBottom.top = std::min(enclosingTopAndBottom.top, borderBoxTop);
+                enclosingTopAndBottom.bottom = std::max(enclosingTopAndBottom.bottom, borderBoxBottom);
+                continue;
+            }
+            if (lineRun.isInlineBoxStart()) {
+                auto& boxGeometry = formattingState.boxGeometry(layoutBox);
+                auto inlineBoxLogicalRect = lineBox.logicalBorderBoxForInlineBox(layoutBox, boxGeometry);
+                formattingState.addLineRun({ lineIndex, layoutBox, inlineBoxLogicalRect, lineRun.expansion(), { } });
+                inlineBoxStartSet.add(&layoutBox);
+                enclosingTopAndBottom.top = std::min(enclosingTopAndBottom.top, inlineBoxLogicalRect.top());
+                continue;
+            }
+            if (lineRun.isInlineBoxEnd()) {
+                inlineBoxEndSet.add(&layoutBox);
+                if (!inlineBoxStartSet.contains(&layoutBox)) {
+                    // An inline box can span multiple lines. Use the [inline box end] signal to include it in the enclosing geometry
+                    // only when it starts at a previous line.
+                    auto inlineBoxLogicalRect = lineBox.logicalBorderBoxForInlineBox(layoutBox, formattingState.boxGeometry(layoutBox));
+                    enclosingTopAndBottom.bottom = std::max(enclosingTopAndBottom.bottom, inlineBoxLogicalRect.bottom());
+                }
+                continue;
+            }
+            ASSERT(lineRun.isWordBreakOpportunity());
+        }
+    };
+    constructLineRunsAndUpdateBoxGeometry();
+
+    auto updateBoxGeometryForInlineBoxes = [&] {
+        // FIXME: We may want to keep around an inline box only set.
+        if (!lineBox.hasInlineBox())
+            return;
+        // 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& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
+            if (!inlineLevelBox.isInlineBox())
+                continue;
+            auto& layoutBox = inlineLevelBox.layoutBox();
+            auto& boxGeometry = formattingState.boxGeometry(layoutBox);
+            // Inline boxes may or may not be wrapped and have runs on multiple lines (e.g. <span>first line<br>second line<br>third line</span>)
+            auto inlineBoxBorderBox = lineBox.logicalBorderBoxForInlineBox(layoutBox, boxGeometry);
+            auto inlineBoxSize = LayoutSize { LayoutUnit::fromFloatCeil(inlineBoxBorderBox.width()), LayoutUnit::fromFloatCeil(inlineBoxBorderBox.height()) };
+            auto logicalRect = Rect { LayoutPoint { inlineBoxBorderBox.topLeft() }, inlineBoxSize };
+            logicalRect.moveBy(LayoutPoint { lineBoxLogicalRect.topLeft() });
+            if (inlineBoxStartSet.contains(&layoutBox)) {
+                // This inline box showed up first on this line.
+                boxGeometry.setLogicalTopLeft(logicalRect.topLeft());
+                auto contentBoxHeight = logicalRect.height() - (boxGeometry.verticalBorder() + boxGeometry.verticalPadding().valueOr(0_lu));
+                boxGeometry.setContentBoxHeight(contentBoxHeight);
+                auto contentBoxWidth = logicalRect.width() - (boxGeometry.horizontalBorder() + boxGeometry.horizontalPadding().valueOr(0_lu));
+                boxGeometry.setContentBoxWidth(contentBoxWidth);
+                continue;
+            }
+            // Middle or end of the inline box. Let's stretch the box as needed.
+            auto enclosingBorderBoxRect = BoxGeometry::borderBoxRect(boxGeometry);
+            enclosingBorderBoxRect.expandToContain(logicalRect);
+            boxGeometry.setLogicalLeft(enclosingBorderBoxRect.left());
+
+            boxGeometry.setContentBoxHeight(enclosingBorderBoxRect.height() - (boxGeometry.verticalBorder() + boxGeometry.verticalPadding().valueOr(0_lu)));
+            boxGeometry.setContentBoxWidth(enclosingBorderBoxRect.width() - (boxGeometry.horizontalBorder() + boxGeometry.horizontalPadding().valueOr(0_lu)));
+        }
+    };
+    updateBoxGeometryForInlineBoxes();
+
+    auto constructLineGeometry = [&] {
+        formattingState.addLine({ lineBoxLogicalRect, enclosingTopAndBottom, lineBox.alignmentBaseline(), rootInlineBoxLogicalRect.left(), lineContent.contentLogicalWidth });
+    };
+    constructLineGeometry();
+
+    return lineBoxLogicalRect;
+}
+
+void InlineFormattingContext::invalidateFormattingState(const InvalidationState&)
+{
+    // Find out what we need to invalidate. This is where we add some smarts to do partial line layout.
+    // For now let's just clear the runs.
+    formattingState().clearLineAndRuns();
+    // FIXME: This is also where we would delete inline items if their content changed.
+}
+
+}
+}
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineFormattingContexthfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingInlineFormattingContexth"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContext.h (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/InlineFormattingContext.h) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContext.h                          (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContext.h     2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,119 @@
</span><ins>+/*
+ * Copyright (C) 2018 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 "FormattingContext.h"
+#include "InlineLineBuilder.h"
+#include <wtf/IsoMalloc.h>
+
+namespace WebCore {
+namespace Layout {
+
+class InlineFormattingState;
+class InvalidationState;
+class LineBox;
+
+// This class implements the layout logic for inline formatting contexts.
+// https://www.w3.org/TR/CSS22/visuren.html#inline-formatting
+class InlineFormattingContext final : public FormattingContext {
+    WTF_MAKE_ISO_ALLOCATED(InlineFormattingContext);
+public:
+    InlineFormattingContext(const ContainerBox& formattingContextRoot, InlineFormattingState&);
+    void layoutInFlowContent(InvalidationState&, const ConstraintsForInFlowContent&) override;
+    LayoutUnit usedContentHeight() const override;
+
+    class Quirks : public FormattingContext::Quirks {
+    public:
+        InlineLayoutUnit initialLineHeight() const;
+        bool hasSoftWrapOpportunityAtImage() const;
+        bool inlineLevelBoxAffectsLineBox(const LineBox::InlineLevelBox&, const LineBox&) const;
+
+    private:
+        friend class InlineFormattingContext;
+        Quirks(const InlineFormattingContext&);
+
+        const InlineFormattingContext& formattingContext() const { return downcast<InlineFormattingContext>(FormattingContext::Quirks::formattingContext()); }
+
+    };
+    InlineFormattingContext::Quirks quirks() const { return Quirks(*this); }
+
+    const InlineFormattingState& formattingState() const { return downcast<InlineFormattingState>(FormattingContext::formattingState()); }
+    InlineFormattingState& formattingState() { return downcast<InlineFormattingState>(FormattingContext::formattingState()); }
+
+    void lineLayoutForIntergration(InvalidationState&, const ConstraintsForInFlowContent&);
+
+private:
+    IntrinsicWidthConstraints computedIntrinsicWidthConstraints() override;
+
+    class Geometry : public FormattingContext::Geometry {
+    public:
+        LineBox lineBoxForLineContent(const LineBuilder::LineContent&);
+        InlineLayoutUnit logicalTopForNextLine(const LineBuilder::LineContent&, InlineLayoutUnit previousLineLogicalBottom, const FloatingContext&) const;
+
+        ContentHeightAndMargin inlineBlockContentHeightAndMargin(const Box&, const HorizontalConstraints&, const OverriddenVerticalValues&) const;
+        ContentWidthAndMargin inlineBlockContentWidthAndMargin(const Box&, const HorizontalConstraints&, const OverriddenHorizontalValues&);
+
+    private:
+        friend class InlineFormattingContext;
+        Geometry(const InlineFormattingContext&);
+
+        const InlineFormattingContext& formattingContext() const { return downcast<InlineFormattingContext>(FormattingContext::Geometry::formattingContext()); }
+
+    };
+    InlineFormattingContext::Geometry geometry() const { return Geometry(*this); }
+
+    void lineLayout(InlineItems&, LineBuilder::InlineItemRange, const ConstraintsForInFlowContent&);
+
+    void computeIntrinsicWidthForFormattingRoot(const Box&);
+    InlineLayoutUnit computedIntrinsicWidthForConstraint(InlineLayoutUnit availableWidth) const;
+
+    void computeHorizontalMargin(const Box&, const HorizontalConstraints&);
+    void computeHeightAndMargin(const Box&, const HorizontalConstraints&);
+    void computeWidthAndMargin(const Box&, const HorizontalConstraints&);
+
+    void collectInlineContentIfNeeded();
+    InlineRect computeGeometryForLineContent(const LineBuilder::LineContent&, const HorizontalConstraints&);
+    void invalidateFormattingState(const InvalidationState&);
+};
+
+inline InlineFormattingContext::Geometry::Geometry(const InlineFormattingContext& inlineFormattingContext)
+    : FormattingContext::Geometry(inlineFormattingContext)
+{
+}
+
+inline InlineFormattingContext::Quirks::Quirks(const InlineFormattingContext& inlineFormattingContext)
+    : FormattingContext::Quirks(inlineFormattingContext)
+{
+}
+
+}
+}
+
+SPECIALIZE_TYPE_TRAITS_LAYOUT_FORMATTING_CONTEXT(InlineFormattingContext, isInlineFormattingContext())
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineFormattingContextGeometrycppfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingInlineFormattingContextGeometrycpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContextGeometry.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/InlineFormattingContextGeometry.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContextGeometry.cpp                                (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContextGeometry.cpp   2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,727 @@
</span><ins>+/*
+ * Copyright (C) 2018 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 "InlineFormattingContext.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "FloatingContext.h"
+#include "FormattingContext.h"
+#include "InlineLineBox.h"
+#include "LayoutBox.h"
+#include "LayoutContainerBox.h"
+#include "LayoutReplacedBox.h"
+#include "LengthFunctions.h"
+
+namespace WebCore {
+namespace Layout {
+
+class LineBoxBuilder {
+public:
+    LineBoxBuilder(const InlineFormattingContext&);
+    LineBox build(const LineBuilder::LineContent&);
+
+private:
+    struct SimplifiedVerticalAlignment {
+        SimplifiedVerticalAlignment(const LineBox::InlineLevelBox& rootInlineBox);
+
+        static bool canUseSimplifiedAlignment(const LineBox::InlineLevelBox& rootInlineBox, const LineBox::InlineLevelBox&, Optional<const BoxGeometry> inlineLevelBoxGeometry);
+
+        void align(LineBox::InlineLevelBox&);
+
+        InlineLayoutUnit rootInlineBoxLogicalTop() const { return m_rootInlineBoxLogicalTop; }
+        InlineLayoutUnit lineBoxHeight() const { return m_lineBoxLogicalBottom - m_lineBoxLogicalTop; }
+
+        void setEnabled(bool enabled) { m_isEnabled = enabled; }
+        bool isEnabled() const { return m_isEnabled; }
+
+    private:
+        void adjust(const LineBox::InlineLevelBox&);
+
+        const LineBox::InlineLevelBox& m_rootInlineBox;
+        bool m_isEnabled { true };
+        InlineLayoutUnit m_lineBoxLogicalTop { 0 };
+        InlineLayoutUnit m_lineBoxLogicalBottom { 0 };
+        InlineLayoutUnit m_rootInlineBoxLogicalTop { 0 };
+    };
+
+    void setVerticalGeometryForInlineBox(LineBox::InlineLevelBox&) const;
+    void constructAndAlignInlineLevelBoxes(LineBox&, const Line::RunList&);
+    void computeLineBoxHeightAndAlignInlineLevelBoxesVertically(LineBox&);
+
+    const InlineFormattingContext& formattingContext() const { return m_inlineFormattingContext; }
+    const Box& rootBox() const { return formattingContext().root(); }
+    LayoutState& layoutState() const { return formattingContext().layoutState(); }
+
+    bool isRootLayoutBox(const ContainerBox& containerBox) const { return &containerBox == &rootBox(); }
+
+private:
+    const InlineFormattingContext& m_inlineFormattingContext;
+};
+
+struct HangingTrailingWhitespaceContent {
+public:
+    void reset();
+
+    InlineLayoutUnit width() const { return m_width; }
+    bool isConditional() const { return m_isConditional; }
+
+    void setIsConditional() { m_isConditional = true; }
+    void expand(InlineLayoutUnit width) { m_width += width; }
+
+private:
+    bool m_isConditional { false };
+    InlineLayoutUnit m_width { 0 };
+};
+
+void HangingTrailingWhitespaceContent::reset()
+{
+    m_isConditional = false;
+    m_width =  0;
+}
+
+static HangingTrailingWhitespaceContent collectHangingTrailingWhitespaceContent(const Line::RunList& runs, bool isLastLineWithInlineContent)
+{
+    auto hangingContent = HangingTrailingWhitespaceContent { };
+    if (isLastLineWithInlineContent)
+        hangingContent.setIsConditional();
+    for (auto& run : WTF::makeReversedRange(runs)) {
+        if (run.isInlineBoxStart() || run.isInlineBoxEnd())
+            continue;
+        if (run.isLineBreak()) {
+            hangingContent.setIsConditional();
+            continue;
+        }
+        if (!run.hasTrailingWhitespace())
+            break;
+        // Check if we have a preserved or hung whitespace.
+        if (run.style().whiteSpace() != WhiteSpace::PreWrap)
+            break;
+        // This is either a normal or conditionally hanging trailing whitespace.
+        hangingContent.expand(run.trailingWhitespaceWidth());
+    }
+    return hangingContent;
+}
+
+static Optional<InlineLayoutUnit> horizontalAlignmentOffset(const Line::RunList& runs, TextAlignMode textAlign, InlineLayoutUnit lineLogicalWidth, InlineLayoutUnit contentLogicalWidth, bool isLastLine)
+{
+    auto availableWidth = lineLogicalWidth - contentLogicalWidth;
+    auto hangingTrailingWhitespaceContent = collectHangingTrailingWhitespaceContent(runs, isLastLine);
+    availableWidth += hangingTrailingWhitespaceContent.width();
+    if (availableWidth <= 0)
+        return { };
+
+    auto computedHorizontalAlignment = [&] {
+        if (textAlign != TextAlignMode::Justify)
+            return textAlign;
+        // Text is justified according to the method specified by the text-justify property,
+        // in order to exactly fill the line box. Unless otherwise specified by text-align-last,
+        // the last line before a forced break or the end of the block is start-aligned.
+        if (isLastLine || (!runs.isEmpty() && runs.last().isLineBreak()))
+            return TextAlignMode::Start;
+        return TextAlignMode::Justify;
+    };
+
+    switch (computedHorizontalAlignment()) {
+    case TextAlignMode::Left:
+    case TextAlignMode::WebKitLeft:
+    case TextAlignMode::Start:
+        return { };
+    case TextAlignMode::Right:
+    case TextAlignMode::WebKitRight:
+    case TextAlignMode::End:
+        return availableWidth;
+    case TextAlignMode::Center:
+    case TextAlignMode::WebKitCenter:
+        return availableWidth / 2;
+    case TextAlignMode::Justify:
+        // TextAlignMode::Justify is a run alignment (and we only do inline box alignment here)
+        return { };
+    default:
+        ASSERT_NOT_IMPLEMENTED_YET();
+        return { };
+    }
+    ASSERT_NOT_REACHED();
+    return { };
+}
+
+LineBoxBuilder::LineBoxBuilder(const InlineFormattingContext& inlineFormattingContext)
+    : m_inlineFormattingContext(inlineFormattingContext)
+{
+}
+
+LineBox LineBoxBuilder::build(const LineBuilder::LineContent& lineContent)
+{
+    auto& runs = lineContent.runs;
+    auto lineLogicalWidth = lineContent.lineLogicalWidth;
+    auto contentLogicalWidth = lineContent.contentLogicalWidth;
+    auto horizontalAlignmentOffset = Layout::horizontalAlignmentOffset(runs, rootBox().style().textAlign(), lineLogicalWidth, contentLogicalWidth, lineContent.isLastLineWithInlineContent);
+    auto lineBox = LineBox { rootBox(), lineContent.logicalTopLeft, lineLogicalWidth, horizontalAlignmentOffset.valueOr(InlineLayoutUnit { }), contentLogicalWidth, lineContent.nonSpanningInlineLevelBoxCount };
+    constructAndAlignInlineLevelBoxes(lineBox, runs);
+    return lineBox;
+}
+
+void LineBoxBuilder::setVerticalGeometryForInlineBox(LineBox::InlineLevelBox& inlineLevelBox) const
+{
+    ASSERT(inlineLevelBox.isInlineBox() || inlineLevelBox.isLineBreakBox());
+    auto& fontMetrics = inlineLevelBox.style().fontMetrics();
+    InlineLayoutUnit ascent = fontMetrics.ascent();
+    InlineLayoutUnit descent = fontMetrics.descent();
+    auto logicalHeight = ascent + descent;
+    // We need floor/ceil to match legacy layout integral positioning.
+    inlineLevelBox.setBaseline(floorf(ascent));
+    inlineLevelBox.setDescent(ceil(descent));
+    inlineLevelBox.setLogicalHeight(logicalHeight);
+
+    // FIXME: Adjust layout bounds with fallback font when applicable.
+    auto& style = inlineLevelBox.layoutBox().style();
+    auto lineHeight = style.lineHeight();
+    if (lineHeight.isNegative()) {
+        // If line-height computes to normal and either text-edge is leading or this is the root inline box,
+        // the font’s line gap metric may also be incorporated into A and D by adding half to each side as half-leading.
+        // https://www.w3.org/TR/css-inline-3/#inline-height
+        // Since text-edge is not supported yet and the initial value is leading, we should just apply it to
+        // all inline boxes.
+        auto halfLineGap = (fontMetrics.lineSpacing() - logicalHeight) / 2;
+        ascent += halfLineGap;
+        descent += halfLineGap;
+    } else {
+        InlineLayoutUnit lineHeight = style.computedLineHeight();
+        InlineLayoutUnit halfLeading = (lineHeight - (ascent + descent)) / 2;
+        ascent += halfLeading;
+        descent += halfLeading;
+    }
+    // We need floor/ceil to match legacy layout integral positioning.
+    inlineLevelBox.setLayoutBounds(LineBox::InlineLevelBox::LayoutBounds { floorf(ascent), ceil(descent) });
+}
+
+void LineBoxBuilder::constructAndAlignInlineLevelBoxes(LineBox& lineBox, const Line::RunList& runs)
+{
+    auto& rootInlineBox = lineBox.rootInlineBox();
+    setVerticalGeometryForInlineBox(rootInlineBox);
+
+    auto simplifiedVerticalAlignment = SimplifiedVerticalAlignment { rootInlineBox };
+    // FIXME: Add fast path support for line-height content.
+    simplifiedVerticalAlignment.setEnabled(layoutState().inStandardsMode() && rootBox().style().lineHeight().isNegative());
+
+    auto simplifiedAlignVerticallyIfApplicable = [&](auto& inlineLevelBox, Optional<const BoxGeometry> boxGeometry) {
+        if (!simplifiedVerticalAlignment.isEnabled())
+            return;
+        if (!SimplifiedVerticalAlignment::canUseSimplifiedAlignment(rootInlineBox, inlineLevelBox, boxGeometry)) {
+            simplifiedVerticalAlignment.setEnabled(false);
+            return;
+        }
+        simplifiedVerticalAlignment.align(inlineLevelBox);
+    };
+
+    auto createWrappedInlineBoxes = [&] {
+        if (runs.isEmpty())
+            return;
+        // An inline box may not necessarily start on the current line:
+        // <span id=outer>line break<br>this content's parent inline box('outer') <span id=inner>starts on the previous line</span></span>
+        // We need to make sure that there's an LineBox::InlineLevelBox for every inline box that's present on the current line.
+        // In nesting case we need to create LineBox::InlineLevelBoxes for the inline box ancestors.
+        // We only have to do it on the first run as any subsequent inline content is either at the same/higher nesting level or
+        // nested with a [inline box start] run.
+        auto& firstRun = runs[0];
+        auto& firstRunParentLayoutBox = firstRun.layoutBox().parent();
+        // If the parent is the formatting root, we can stop here. This is root inline box content, there's no nesting inline box from the previous line(s)
+        // unless the inline box closing is forced over to the current line.
+        // e.g.
+        // <span>normally the inline box closing forms a continuous content</span>
+        // <span>unless it's forced to the next line<br></span>
+        auto firstRunNeedsInlineBox = firstRun.isInlineBoxEnd();
+        if (!firstRunNeedsInlineBox && isRootLayoutBox(firstRunParentLayoutBox))
+            return;
+        Vector<const Box*> layoutBoxesWithoutInlineBoxes;
+        if (firstRunNeedsInlineBox)
+            layoutBoxesWithoutInlineBoxes.append(&firstRun.layoutBox());
+        auto* ancestor = &firstRunParentLayoutBox;
+        while (!isRootLayoutBox(*ancestor)) {
+            layoutBoxesWithoutInlineBoxes.append(ancestor);
+            ancestor = &ancestor->parent();
+        }
+        // Construct the missing LineBox::InlineBoxes starting with the topmost layout box.
+        for (auto* layoutBox : WTF::makeReversedRange(layoutBoxesWithoutInlineBoxes)) {
+            auto inlineBox = LineBox::InlineLevelBox::createInlineBox(*layoutBox, rootInlineBox.logicalLeft(), lineBox.contentLogicalWidth());
+            setVerticalGeometryForInlineBox(inlineBox);
+            simplifiedAlignVerticallyIfApplicable(inlineBox, { });
+            lineBox.addInlineLevelBox(WTFMove(inlineBox));
+        }
+    };
+    createWrappedInlineBoxes();
+
+    auto lineHasContent = false;
+    for (auto& run : runs) {
+        auto& layoutBox = run.layoutBox();
+        auto runHasContent = [&] () -> bool {
+            ASSERT(!lineHasContent);
+            if (run.isText() || run.isBox() || run.isSoftLineBreak() || run.isHardLineBreak())
+                return true;
+            auto& inlineBoxGeometry = formattingContext().geometryForBox(layoutBox);
+            // Even negative horizontal margin makes the line "contentful".
+            if (run.isInlineBoxStart())
+                return inlineBoxGeometry.marginStart() || inlineBoxGeometry.borderLeft() || inlineBoxGeometry.paddingLeft().valueOr(0_lu);
+            if (run.isInlineBoxEnd())
+                return inlineBoxGeometry.marginEnd() || inlineBoxGeometry.borderRight() || inlineBoxGeometry.paddingRight().valueOr(0_lu);
+            if (run.isWordBreakOpportunity())
+                return false;
+            ASSERT_NOT_REACHED();
+            return true;
+        };
+        lineHasContent = lineHasContent || runHasContent();
+        auto logicalLeft = rootInlineBox.logicalLeft() + run.logicalLeft();
+        if (run.isBox()) {
+            auto& inlineLevelBoxGeometry = formattingContext().geometryForBox(layoutBox);
+            auto marginBoxHeight = inlineLevelBoxGeometry.marginBoxHeight();
+            auto ascent = InlineLayoutUnit { };
+            if (layoutState().shouldNotSynthesizeInlineBlockBaseline()) {
+                // Integration codepath constructs replaced boxes for inline-block content.
+                ASSERT(layoutBox.isReplacedBox());
+                ascent = *downcast<ReplacedBox>(layoutBox).baseline();
+            } else if (layoutBox.isInlineBlockBox()) {
+                // The baseline of an 'inline-block' is the baseline of its last line box in the normal flow, unless it has either no in-flow line boxes or
+                // if its 'overflow' property has a computed value other than 'visible', in which case the baseline is the bottom margin edge.
+                auto synthesizeBaseline = !layoutBox.establishesInlineFormattingContext() || !layoutBox.style().isOverflowVisible();
+                if (synthesizeBaseline)
+                    ascent = marginBoxHeight;
+                else {
+                    auto& formattingState = layoutState().establishedInlineFormattingState(downcast<ContainerBox>(layoutBox));
+                    auto& lastLine = formattingState.lines().last();
+                    auto inlineBlockBaseline = lastLine.lineBoxLogicalRect().top() + lastLine.baseline();
+                    ascent = inlineLevelBoxGeometry.marginBefore() + inlineLevelBoxGeometry.borderTop() + inlineLevelBoxGeometry.paddingTop().valueOr(0) + inlineBlockBaseline;
+                }
+            } else if (layoutBox.isReplacedBox())
+                ascent = downcast<ReplacedBox>(layoutBox).baseline().valueOr(marginBoxHeight);
+            else
+                ascent = marginBoxHeight;
+            logicalLeft += std::max(0_lu, inlineLevelBoxGeometry.marginStart());
+            auto atomicInlineLevelBox = LineBox::InlineLevelBox::createAtomicInlineLevelBox(layoutBox, logicalLeft, { inlineLevelBoxGeometry.borderBoxWidth(), marginBoxHeight });
+            atomicInlineLevelBox.setBaseline(ascent);
+            atomicInlineLevelBox.setLayoutBounds(LineBox::InlineLevelBox::LayoutBounds { ascent, marginBoxHeight - ascent });
+            simplifiedAlignVerticallyIfApplicable(atomicInlineLevelBox, inlineLevelBoxGeometry);
+            lineBox.addInlineLevelBox(WTFMove(atomicInlineLevelBox));
+            continue;
+        }
+        if (run.isInlineBoxStart()) {
+            // At this point we don't know yet how wide this inline box is. Let's assume it's as long as the line is
+            // and adjust it later if we come across an inlineBoxEnd run (see below).
+            auto initialLogicalWidth = lineBox.contentLogicalWidth() - run.logicalLeft();
+            ASSERT(initialLogicalWidth >= 0);
+            // Inline box run is based on margin box. Let's convert it to border box.
+            auto marginStart = std::max(0_lu, formattingContext().geometryForBox(layoutBox).marginStart());
+            logicalLeft += marginStart;
+            initialLogicalWidth -= marginStart;
+            auto inlineBox = LineBox::InlineLevelBox::createInlineBox(layoutBox, logicalLeft, initialLogicalWidth);
+            setVerticalGeometryForInlineBox(inlineBox);
+            simplifiedAlignVerticallyIfApplicable(inlineBox, { });
+            lineBox.addInlineLevelBox(WTFMove(inlineBox));
+            continue;
+        }
+        if (run.isInlineBoxEnd()) {
+            // Adjust the logical width when the inline box closes on this line.
+            auto& inlineBox = lineBox.inlineLevelBoxForLayoutBox(layoutBox);
+            ASSERT(inlineBox.isInlineBox());
+            // Inline box run is based on margin box. Let's convert it to border box.
+            auto marginEnd = std::max(0_lu, formattingContext().geometryForBox(layoutBox).marginEnd());
+            auto inlineBoxLogicalRight = logicalLeft + run.logicalWidth() - marginEnd;
+            inlineBox.setLogicalWidth(inlineBoxLogicalRight - inlineBox.logicalLeft());
+            simplifiedAlignVerticallyIfApplicable(inlineBox, { });
+            continue;
+        }
+        if (run.isText() || run.isSoftLineBreak()) {
+            // FIXME: Adjust non-empty inline box height when glyphs from the non-primary font stretch the box.
+            lineBox.inlineLevelBoxForLayoutBox(layoutBox.parent()).setHasContent();
+            continue;
+        }
+        if (run.isHardLineBreak()) {
+            auto lineBreakBox = LineBox::InlineLevelBox::createLineBreakBox(layoutBox, logicalLeft);
+            setVerticalGeometryForInlineBox(lineBreakBox);
+            simplifiedAlignVerticallyIfApplicable(lineBreakBox, { });
+            lineBox.addInlineLevelBox(WTFMove(lineBreakBox));
+            continue;
+        }
+        if (run.isWordBreakOpportunity()) {
+            lineBox.addInlineLevelBox(LineBox::InlineLevelBox::createGenericInlineLevelBox(layoutBox, logicalLeft));
+            continue;
+        }
+        ASSERT_NOT_REACHED();
+    }
+
+    lineBox.setHasContent(lineHasContent);
+    if (simplifiedVerticalAlignment.isEnabled() || !lineHasContent) {
+        // We should always be able to exercise the fast path when the line has no content at all, even in non-standards mode or with line-height set.
+        rootInlineBox.setLogicalTop(lineHasContent ? simplifiedVerticalAlignment.rootInlineBoxLogicalTop() : -rootInlineBox.baseline());
+        lineBox.setLogicalHeight(lineHasContent ? simplifiedVerticalAlignment.lineBoxHeight() : InlineLayoutUnit());
+    } else
+        computeLineBoxHeightAndAlignInlineLevelBoxesVertically(lineBox);
+}
+
+void LineBoxBuilder::computeLineBoxHeightAndAlignInlineLevelBoxesVertically(LineBox& lineBox)
+{
+    ASSERT(lineBox.hasContent());
+    // This function (partially) implements:
+    // 2.2. Layout Within Line Boxes
+    // https://www.w3.org/TR/css-inline-3/#line-layout
+    // 1. Compute the line box height using the layout bounds geometry. This height computation strictly uses layout bounds and not normal inline level box geometries.
+    // 2. Compute the baseline/logical top position of the root inline box. Aligned boxes push the root inline box around inside the line box.
+    // 3. Finally align the inline level boxes using (mostly) normal inline level box geometries.
+    auto quirks = formattingContext().quirks();
+    auto& rootInlineBox = lineBox.rootInlineBox();
+
+    auto computeLineBoxLogicalHeight = [&] {
+        // Line box height computation is based on the layout bounds of the inline boxes and not their logical (ascent/descent) dimensions.
+        struct AbsoluteTopAndBottom {
+            InlineLayoutUnit top { 0 };
+            InlineLayoutUnit bottom { 0 };
+        };
+        HashMap<const LineBox::InlineLevelBox*, AbsoluteTopAndBottom> inlineLevelBoxAbsoluteTopAndBottomMap;
+
+        auto minimumLogicalTop = Optional<InlineLayoutUnit> { };
+        auto maximumLogicalBottom = Optional<InlineLayoutUnit> { };
+        if (quirks.inlineLevelBoxAffectsLineBox(rootInlineBox, lineBox)) {
+            minimumLogicalTop = InlineLayoutUnit { };
+            maximumLogicalBottom = rootInlineBox.layoutBounds().height();
+            inlineLevelBoxAbsoluteTopAndBottomMap.add(&rootInlineBox, AbsoluteTopAndBottom { *minimumLogicalTop, *maximumLogicalBottom });
+        } else
+            inlineLevelBoxAbsoluteTopAndBottomMap.add(&rootInlineBox, AbsoluteTopAndBottom { });
+
+        Vector<LineBox::InlineLevelBox*> lineBoxRelativeInlineLevelBoxes;
+        for (auto& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
+            auto& layoutBox = inlineLevelBox.layoutBox();
+            if (inlineLevelBox.hasLineBoxRelativeAlignment()) {
+                lineBoxRelativeInlineLevelBoxes.append(&inlineLevelBox);
+                continue;
+            }
+            auto& parentInlineBox = lineBox.inlineLevelBoxForLayoutBox(layoutBox.parent());
+            // Logical top is relative to the parent inline box's layout bounds.
+            // Note that this logical top is not the final logical top of the inline level box.
+            // This is the logical top in the context of the layout bounds geometry which may be very different from the inline box's normal geometry.
+            auto logicalTop = InlineLayoutUnit { };
+            switch (inlineLevelBox.verticalAlign()) {
+            case VerticalAlign::Baseline: {
+                auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().ascent;
+                logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
+                break;
+            }
+            case VerticalAlign::Middle: {
+                auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().height() / 2 + parentInlineBox.style().fontMetrics().xHeight() / 2;
+                logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
+                break;
+            }
+            case VerticalAlign::BaselineMiddle: {
+                auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().height() / 2;
+                logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
+                break;
+            }
+            case VerticalAlign::Length: {
+                auto& style = inlineLevelBox.style();
+                auto logicalTopOffsetFromParentBaseline = floatValueForLength(style.verticalAlignLength(), style.computedLineHeight()) + inlineLevelBox.layoutBounds().ascent;
+                logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
+                break;
+            }
+            case VerticalAlign::TextTop: {
+                // Note that text-top aligns with the inline box's font metrics top (ascent) and not the layout bounds top.
+                logicalTop = parentInlineBox.layoutBounds().ascent - parentInlineBox.baseline();
+                break;
+            }
+            case VerticalAlign::TextBottom: {
+                // Note that text-bottom aligns with the inline box's font metrics bottom (descent) and not the layout bounds bottom.
+                auto parentInlineBoxLayoutBounds = parentInlineBox.layoutBounds();
+                auto parentInlineBoxLogicalBottom = parentInlineBoxLayoutBounds.height() - parentInlineBoxLayoutBounds.descent + parentInlineBox.descent().valueOr(InlineLayoutUnit());
+                logicalTop = parentInlineBoxLogicalBottom - inlineLevelBox.layoutBounds().height();
+                break;
+            }
+            default:
+                ASSERT_NOT_IMPLEMENTED_YET();
+                break;
+            }
+            auto parentInlineBoxAbsoluteTopAndBottom = inlineLevelBoxAbsoluteTopAndBottomMap.get(&parentInlineBox);
+            auto absoluteLogicalTop = parentInlineBoxAbsoluteTopAndBottom.top + logicalTop;
+            auto absoluteLogicalBottom = absoluteLogicalTop + inlineLevelBox.layoutBounds().height();
+            inlineLevelBoxAbsoluteTopAndBottomMap.add(&inlineLevelBox, AbsoluteTopAndBottom { absoluteLogicalTop, absoluteLogicalBottom });
+            // Stretch the min/max absolute values if applicable.
+            if (quirks.inlineLevelBoxAffectsLineBox(inlineLevelBox, lineBox)) {
+                minimumLogicalTop = std::min(minimumLogicalTop.valueOr(absoluteLogicalTop), absoluteLogicalTop);
+                maximumLogicalBottom = std::max(maximumLogicalBottom.valueOr(absoluteLogicalBottom), absoluteLogicalBottom);
+            }
+        }
+        // The line box height computation is as follows:
+        // 1. Stretch the line box with the non-line-box relative aligned inline box absolute top and bottom values.
+        // 2. Check if the line box relative aligned inline boxes (top, bottom etc) have enough room and stretch the line box further if needed.
+        auto lineBoxLogicalHeight = maximumLogicalBottom.valueOr(InlineLayoutUnit()) - minimumLogicalTop.valueOr(InlineLayoutUnit());
+        for (auto* lineBoxRelativeInlineLevelBox : lineBoxRelativeInlineLevelBoxes) {
+            if (!quirks.inlineLevelBoxAffectsLineBox(*lineBoxRelativeInlineLevelBox, lineBox))
+                continue;
+            lineBoxLogicalHeight = std::max(lineBoxLogicalHeight, lineBoxRelativeInlineLevelBox->layoutBounds().height());
+        }
+        lineBox.setLogicalHeight(lineBoxLogicalHeight);
+    };
+    computeLineBoxLogicalHeight();
+
+    auto computeRootInlineBoxVerticalPosition = [&] {
+        HashMap<const LineBox::InlineLevelBox*, InlineLayoutUnit> inlineLevelBoxAbsoluteBaselineOffsetMap;
+        inlineLevelBoxAbsoluteBaselineOffsetMap.add(&rootInlineBox, InlineLayoutUnit { });
+
+        auto maximumTopOffsetFromRootInlineBoxBaseline = Optional<InlineLayoutUnit> { };
+        if (quirks.inlineLevelBoxAffectsLineBox(rootInlineBox, lineBox))
+            maximumTopOffsetFromRootInlineBoxBaseline = rootInlineBox.layoutBounds().ascent;
+
+        for (auto& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
+            auto absoluteBaselineOffset = InlineLayoutUnit { };
+            if (!inlineLevelBox.hasLineBoxRelativeAlignment()) {
+                auto& layoutBox = inlineLevelBox.layoutBox();
+                auto& parentInlineBox = lineBox.inlineLevelBoxForLayoutBox(layoutBox.parent());
+                auto baselineOffsetFromParentBaseline = InlineLayoutUnit { };
+
+                switch (inlineLevelBox.verticalAlign()) {
+                case VerticalAlign::Baseline:
+                    baselineOffsetFromParentBaseline = { };
+                    break;
+                case VerticalAlign::Middle: {
+                    auto logicalTopOffsetFromParentBaseline = (inlineLevelBox.layoutBounds().height() / 2 + parentInlineBox.style().fontMetrics().xHeight() / 2);
+                    baselineOffsetFromParentBaseline = logicalTopOffsetFromParentBaseline - inlineLevelBox.layoutBounds().ascent;
+                    break;
+                }
+                case VerticalAlign::BaselineMiddle: {
+                    auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().height() / 2;
+                    baselineOffsetFromParentBaseline = logicalTopOffsetFromParentBaseline - inlineLevelBox.layoutBounds().ascent;
+                    break;
+                }
+                case VerticalAlign::Length: {
+                    auto& style = inlineLevelBox.style();
+                    auto verticalAlignOffset = floatValueForLength(style.verticalAlignLength(), style.computedLineHeight());
+                    auto logicalTopOffsetFromParentBaseline = verticalAlignOffset + inlineLevelBox.baseline();
+                    baselineOffsetFromParentBaseline = logicalTopOffsetFromParentBaseline - inlineLevelBox.baseline();
+                    break;
+                }
+                case VerticalAlign::TextTop:
+                    baselineOffsetFromParentBaseline = parentInlineBox.baseline() - inlineLevelBox.layoutBounds().ascent;
+                    break;
+                case VerticalAlign::TextBottom:
+                    baselineOffsetFromParentBaseline = inlineLevelBox.layoutBounds().descent - *parentInlineBox.descent();
+                    break;
+                default:
+                    ASSERT_NOT_IMPLEMENTED_YET();
+                    break;
+                }
+                absoluteBaselineOffset = inlineLevelBoxAbsoluteBaselineOffsetMap.get(&parentInlineBox) + baselineOffsetFromParentBaseline;
+            } else {
+                switch (inlineLevelBox.verticalAlign()) {
+                case VerticalAlign::Top: {
+                    absoluteBaselineOffset = rootInlineBox.layoutBounds().ascent - inlineLevelBox.layoutBounds().ascent;
+                    break;
+                }
+                case VerticalAlign::Bottom: {
+                    absoluteBaselineOffset = inlineLevelBox.layoutBounds().descent - rootInlineBox.layoutBounds().descent;
+                    break;
+                }
+                default:
+                    ASSERT_NOT_IMPLEMENTED_YET();
+                    break;
+                }
+            }
+            inlineLevelBoxAbsoluteBaselineOffsetMap.add(&inlineLevelBox, absoluteBaselineOffset);
+            auto affectsRootInlineBoxVerticalPosition = quirks.inlineLevelBoxAffectsLineBox(inlineLevelBox, lineBox);
+            if (affectsRootInlineBoxVerticalPosition) {
+                auto topOffsetFromRootInlineBoxBaseline = absoluteBaselineOffset + inlineLevelBox.layoutBounds().ascent;
+                if (maximumTopOffsetFromRootInlineBoxBaseline)
+                    maximumTopOffsetFromRootInlineBoxBaseline = std::max(*maximumTopOffsetFromRootInlineBoxBaseline, topOffsetFromRootInlineBoxBaseline);
+                else {
+                    // We are is quirk mode and the root inline box has no content. The root inline box's baseline is anchored at 0.
+                    // However negative ascent (e.g negative top margin) can "push" the root inline box upwards and have a negative value.
+                    maximumTopOffsetFromRootInlineBoxBaseline = inlineLevelBox.layoutBounds().ascent >= 0
+                        ? std::max(0.0f, topOffsetFromRootInlineBoxBaseline)
+                        : topOffsetFromRootInlineBoxBaseline;
+                }
+            }
+        }
+        auto rootInlineBoxLogicalTop = maximumTopOffsetFromRootInlineBoxBaseline.valueOr(0.f) - rootInlineBox.baseline();
+        rootInlineBox.setLogicalTop(rootInlineBoxLogicalTop);
+    };
+    computeRootInlineBoxVerticalPosition();
+
+    auto alignInlineLevelBoxes = [&] {
+        for (auto& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
+            auto& layoutBox = inlineLevelBox.layoutBox();
+            auto& parentInlineBox = lineBox.inlineLevelBoxForLayoutBox(layoutBox.parent());
+            auto logicalTop = InlineLayoutUnit { };
+            switch (inlineLevelBox.verticalAlign()) {
+            case VerticalAlign::Baseline:
+                logicalTop = parentInlineBox.baseline() - inlineLevelBox.baseline();
+                break;
+            case VerticalAlign::Middle: {
+                auto logicalTopOffsetFromParentBaseline = (inlineLevelBox.logicalHeight() / 2 + parentInlineBox.style().fontMetrics().xHeight() / 2);
+                logicalTop = parentInlineBox.baseline() - logicalTopOffsetFromParentBaseline;
+                break;
+            }
+            case VerticalAlign::BaselineMiddle: {
+                auto logicalTopOffsetFromParentBaseline = inlineLevelBox.logicalHeight() / 2;
+                logicalTop = parentInlineBox.baseline() - logicalTopOffsetFromParentBaseline;
+                break;
+            }
+            case VerticalAlign::Length: {
+                auto& style = inlineLevelBox.style();
+                auto verticalAlignOffset = floatValueForLength(style.verticalAlignLength(), style.computedLineHeight());
+                auto logicalTopOffsetFromParentBaseline = verticalAlignOffset + inlineLevelBox.baseline();
+                logicalTop = parentInlineBox.baseline() - logicalTopOffsetFromParentBaseline;
+                break;
+            }
+            // Note that (text)top/bottom align their layout bounds.
+            case VerticalAlign::TextTop:
+                logicalTop = inlineLevelBox.layoutBounds().ascent - inlineLevelBox.baseline();
+                break;
+            case VerticalAlign::TextBottom:
+                logicalTop = parentInlineBox.logicalHeight() - inlineLevelBox.layoutBounds().descent - inlineLevelBox.baseline();
+                break;
+            case VerticalAlign::Top:
+                // Note that this logical top is not relative to the parent inline box.
+                logicalTop = inlineLevelBox.layoutBounds().ascent - inlineLevelBox.baseline();
+                break;
+            case VerticalAlign::Bottom:
+                // Note that this logical top is not relative to the parent inline box.
+                logicalTop = lineBox.logicalHeight() - inlineLevelBox.layoutBounds().descent - inlineLevelBox.baseline();
+                break;
+            default:
+                ASSERT_NOT_IMPLEMENTED_YET();
+                break;
+            }
+            inlineLevelBox.setLogicalTop(logicalTop);
+        }
+    };
+    alignInlineLevelBoxes();
+}
+
+LineBoxBuilder::SimplifiedVerticalAlignment::SimplifiedVerticalAlignment(const LineBox::InlineLevelBox& rootInlineBox)
+    : m_rootInlineBox(rootInlineBox)
+{
+    adjust(rootInlineBox);
+}
+
+bool LineBoxBuilder::SimplifiedVerticalAlignment::canUseSimplifiedAlignment(const LineBox::InlineLevelBox& rootInlineBox, const LineBox::InlineLevelBox& inlineLevelBox, Optional<const BoxGeometry> inlineLevelBoxGeometry)
+{
+    if (inlineLevelBox.isAtomicInlineLevelBox()) {
+        ASSERT(inlineLevelBoxGeometry);
+        // Baseline aligned, non-stretchy direct children are considered to be simple for now.
+        auto& layoutBox = inlineLevelBox.layoutBox();
+        return &layoutBox.parent() == &rootInlineBox.layoutBox()
+            && layoutBox.style().verticalAlign() == VerticalAlign::Baseline
+            && !inlineLevelBoxGeometry->marginBefore()
+            && !inlineLevelBoxGeometry->marginAfter()
+            && inlineLevelBoxGeometry->marginBoxHeight() <= rootInlineBox.baseline();
+    }
+    if (inlineLevelBox.isLineBreakBox()) {
+        // Baseline aligned, non-stretchy line breaks e.g. <div><span><br></span></div> but not <div><span style="font-size: 100px;"><br></span></div>.
+        return inlineLevelBox.layoutBox().style().verticalAlign() == VerticalAlign::Baseline && inlineLevelBox.baseline() <= rootInlineBox.baseline();
+    }
+    if (inlineLevelBox.isInlineBox()) {
+        // Baseline aligned, non-stretchy inline boxes e.g. <div><span></span></div> but not <div><span style="font-size: 100px;"></span></div>.
+        return inlineLevelBox.layoutBox().style().verticalAlign() == VerticalAlign::Baseline && inlineLevelBox.layoutBounds() == rootInlineBox.layoutBounds();
+    }
+    return false;
+}
+
+void LineBoxBuilder::SimplifiedVerticalAlignment::align(LineBox::InlineLevelBox& inlineLevelBox)
+{
+    if (inlineLevelBox.isAtomicInlineLevelBox() || inlineLevelBox.isLineBreakBox() || inlineLevelBox.isInlineBox()) {
+        // Only baseline alignment for now.
+        inlineLevelBox.setLogicalTop(m_rootInlineBox.baseline() - inlineLevelBox.baseline());
+        adjust(inlineLevelBox);
+        return;
+    }
+    ASSERT_NOT_IMPLEMENTED_YET();
+}
+
+void LineBoxBuilder::SimplifiedVerticalAlignment::adjust(const LineBox::InlineLevelBox& inlineLevelBox)
+{
+    auto layoutBoundsLogicalTop = m_rootInlineBox.layoutBounds().ascent - inlineLevelBox.layoutBounds().ascent;
+    m_lineBoxLogicalTop = std::min(m_lineBoxLogicalTop, layoutBoundsLogicalTop);
+    m_lineBoxLogicalBottom = std::max(m_lineBoxLogicalBottom, layoutBoundsLogicalTop + inlineLevelBox.layoutBounds().height());
+    m_rootInlineBoxLogicalTop = std::max(m_rootInlineBoxLogicalTop, inlineLevelBox.layoutBounds().ascent - m_rootInlineBox.baseline());
+}
+
+LineBox InlineFormattingContext::Geometry::lineBoxForLineContent(const LineBuilder::LineContent& lineContent)
+{
+    return LineBoxBuilder(formattingContext()).build(lineContent);
+}
+
+InlineLayoutUnit InlineFormattingContext::Geometry::logicalTopForNextLine(const LineBuilder::LineContent& lineContent, InlineLayoutUnit previousLineLogicalBottom, const FloatingContext& floatingContext) const
+{
+    // Normally the next line's logical top is the previous line's logical bottom, but when the line ends
+    // with the clear property set, the next line needs to clear the existing floats.
+    if (lineContent.runs.isEmpty())
+        return previousLineLogicalBottom;
+    auto& lastRunLayoutBox = lineContent.runs.last().layoutBox(); 
+    if (lastRunLayoutBox.style().clear() == Clear::None)
+        return previousLineLogicalBottom;
+    auto positionWithClearance = floatingContext.verticalPositionWithClearance(lastRunLayoutBox);
+    if (!positionWithClearance)
+        return previousLineLogicalBottom;
+    return std::max(previousLineLogicalBottom, InlineLayoutUnit(positionWithClearance->position));
+}
+
+ContentWidthAndMargin InlineFormattingContext::Geometry::inlineBlockContentWidthAndMargin(const Box& formattingContextRoot, const HorizontalConstraints& horizontalConstraints, const OverriddenHorizontalValues& overriddenHorizontalValues)
+{
+    ASSERT(formattingContextRoot.isInFlow());
+
+    // 10.3.10 'Inline-block', replaced elements in normal flow
+
+    // Exactly as inline replaced elements.
+    if (formattingContextRoot.isReplacedBox())
+        return inlineReplacedContentWidthAndMargin(downcast<ReplacedBox>(formattingContextRoot), horizontalConstraints, { }, overriddenHorizontalValues);
+
+    // 10.3.9 'Inline-block', non-replaced elements in normal flow
+
+    // If 'width' is 'auto', the used value is the shrink-to-fit width as for floating elements.
+    // A computed value of 'auto' for 'margin-left' or 'margin-right' becomes a used value of '0'.
+    // #1
+    auto width = computedValue(formattingContextRoot.style().logicalWidth(), horizontalConstraints.logicalWidth);
+    if (!width)
+        width = shrinkToFitWidth(formattingContextRoot, horizontalConstraints.logicalWidth);
+
+    // #2
+    auto computedHorizontalMargin = Geometry::computedHorizontalMargin(formattingContextRoot, horizontalConstraints);
+
+    return ContentWidthAndMargin { *width, { computedHorizontalMargin.start.valueOr(0_lu), computedHorizontalMargin.end.valueOr(0_lu) } };
+}
+
+ContentHeightAndMargin InlineFormattingContext::Geometry::inlineBlockContentHeightAndMargin(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints, const OverriddenVerticalValues& overriddenVerticalValues) const
+{
+    ASSERT(layoutBox.isInFlow());
+
+    // 10.6.2 Inline replaced elements, block-level replaced elements in normal flow, 'inline-block' replaced elements in normal flow and floating replaced elements
+    if (layoutBox.isReplacedBox())
+        return inlineReplacedContentHeightAndMargin(downcast<ReplacedBox>(layoutBox), horizontalConstraints, { }, overriddenVerticalValues);
+
+    // 10.6.6 Complicated cases
+    // - 'Inline-block', non-replaced elements.
+    return complicatedCases(layoutBox, horizontalConstraints, overriddenVerticalValues);
+}
+
+}
+}
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineFormattingContextQuirkscppfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingInlineFormattingContextQuirkscpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContextQuirks.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/InlineFormattingContextQuirks.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContextQuirks.cpp                          (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContextQuirks.cpp     2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,121 @@
</span><ins>+/*
+ * 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 "InlineFormattingContext.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "LayoutBoxGeometry.h"
+
+namespace WebCore {
+namespace Layout {
+
+InlineLayoutUnit InlineFormattingContext::Quirks::initialLineHeight() const
+{
+    // Negative lineHeight value means the line-height is not set
+    auto& root = formattingContext().root();
+    if (layoutState().inStandardsMode() || !root.style().lineHeight().isNegative())
+        return root.style().computedLineHeight();
+    return root.style().fontMetrics().floatHeight();
+}
+
+bool InlineFormattingContext::Quirks::inlineLevelBoxAffectsLineBox(const LineBox::InlineLevelBox& inlineLevelBox, const LineBox& lineBox) const
+{
+    if (inlineLevelBox.isLineBreakBox()) {
+        if (layoutState().inStandardsMode())
+            return true;
+        // In quirks mode linebreak boxes (<br>) stop affecting the line box when (assume <br> is nested e.g. <span style="font-size: 100px"><br></span>)
+        // 1. the root inline box has content <div>content<br>/div>
+        // 2. there's at least one atomic inline level box on the line e.g <div><img><br></div> or <div><span><img></span><br></div>
+        // 3. there's at least one inline box with content e.g. <div><span>content</span><br></div>
+        if (lineBox.rootInlineBox().hasContent())
+            return false;
+        if (lineBox.hasAtomicInlineLevelBox())
+            return false;
+        // At this point we either have only the <br> on the line or inline boxes with or without content.
+        auto& inlineLevelBoxes = lineBox.nonRootInlineLevelBoxes();
+        ASSERT(!inlineLevelBoxes.isEmpty());
+        if (inlineLevelBoxes.size() == 1)
+            return true;
+        for (auto& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
+            // Filter out empty inline boxes e.g. <div><span></span><span></span><br></div>
+            if (inlineLevelBox.isInlineBox() && inlineLevelBox.hasContent())
+                return false;
+        }
+        return true;
+    }
+    if (inlineLevelBox.isInlineBox()) {
+        // Inline boxes (e.g. root inline box or <span>) affects line boxes either through the strut or actual content.
+        auto inlineBoxHasImaginaryStrut = layoutState().inStandardsMode();
+        if (inlineLevelBox.hasContent() || inlineBoxHasImaginaryStrut)
+            return true;
+        if (inlineLevelBox.isRootInlineBox()) {
+            auto shouldRootInlineBoxWithNoContentStretchLineBox = [&] {
+                if (inlineLevelBox.layoutBox().style().lineHeight().isNegative())
+                    return false;
+                // The root inline box with non-initial line height value stretches the line box even when root has no content
+                // but there's at least one inline box with content.
+                // e.g. <div style="line-height: 100px;"><span>content</span></div>
+                if (!lineBox.hasInlineBox())
+                    return false;
+                for (auto& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
+                    if (!inlineLevelBox.isInlineBox())
+                        continue;
+                    if (inlineLevelBox.hasContent())
+                        return true;
+                }
+                return false;
+            };
+            return shouldRootInlineBoxWithNoContentStretchLineBox();
+        }
+        // Non-root inline boxes (e.g. <span>).
+        auto& boxGeometry = formattingContext().geometryForBox(inlineLevelBox.layoutBox());
+        if (boxGeometry.horizontalBorder() || boxGeometry.horizontalPadding().valueOr(0_lu)) {
+            // Horizontal border and padding make the inline box stretch the line (e.g. <span style="padding: 10px;"></span>).
+            return true;
+        }
+        return false;
+    }
+    if (inlineLevelBox.isAtomicInlineLevelBox()) {
+        if (inlineLevelBox.layoutBounds().height())
+            return true;
+        // While in practice when the negative vertical margin makes the layout bounds empty (e.g: height: 100px; margin-top: -100px;), and this inline
+        // level box contributes 0px to the line box height, it still needs to be taken into account while computing line box geometries.
+        auto& boxGeometry = formattingContext().geometryForBox(inlineLevelBox.layoutBox());
+        return boxGeometry.marginBefore() || boxGeometry.marginAfter();
+    }
+    return false;
+}
+
+bool InlineFormattingContext::Quirks::hasSoftWrapOpportunityAtImage() const
+{
+    return !formattingContext().root().isTableCell();
+}
+
+}
+}
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineFormattingStatecppfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingInlineFormattingStatecpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingState.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/InlineFormattingState.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingState.cpp                          (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingState.cpp     2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,49 @@
</span><ins>+/*
+ * Copyright (C) 2018 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 "InlineFormattingState.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include <wtf/IsoMallocInlines.h>
+
+namespace WebCore {
+namespace Layout {
+
+WTF_MAKE_ISO_ALLOCATED_IMPL(InlineFormattingState);
+
+InlineFormattingState::InlineFormattingState(Ref<FloatingState>&& floatingState, LayoutState& layoutState)
+    : FormattingState(WTFMove(floatingState), Type::Inline, layoutState)
+{
+}
+
+InlineFormattingState::~InlineFormattingState()
+{
+}
+
+}
+}
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineFormattingStatehfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingInlineFormattingStateh"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingState.h (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/InlineFormattingState.h) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingState.h                            (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingState.h       2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,109 @@
</span><ins>+/*
+ * Copyright (C) 2018 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 "FormattingState.h"
+#include "InlineItem.h"
+#include "InlineLineBox.h"
+#include "InlineLineGeometry.h"
+#include "InlineLineRun.h"
+#include <wtf/IsoMalloc.h>
+
+namespace WebCore {
+namespace Layout {
+
+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.
+class InlineFormattingState : public FormattingState {
+    WTF_MAKE_ISO_ALLOCATED(InlineFormattingState);
+public:
+    InlineFormattingState(Ref<FloatingState>&&, LayoutState&);
+    ~InlineFormattingState();
+
+    InlineItems& inlineItems() { return m_inlineItems; }
+    const InlineItems& inlineItems() const { return m_inlineItems; }
+    void addInlineItem(InlineItem&& inlineItem) { m_inlineItems.append(WTFMove(inlineItem)); }
+
+    const InlineLines& lines() const { return m_lines; }
+    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(LineRun&& run) { m_lineRuns.append(WTFMove(run)); }
+
+    void setClearGapAfterLastLine(InlineLayoutUnit verticalGap);
+    InlineLayoutUnit clearGapAfterLastLine() const { return m_clearGapAfterLastLine; }
+
+    void clearLineAndRuns();
+    void shrinkToFit();
+
+private:
+    // Cacheable input to line layout.
+    InlineItems m_inlineItems;
+    InlineLines m_lines;
+    InlineLineBoxes m_lineBoxes;
+    InlineLineRuns m_lineRuns;
+    InlineLayoutUnit m_clearGapAfterLastLine { 0 };
+};
+
+inline void InlineFormattingState::setClearGapAfterLastLine(InlineLayoutUnit verticalGap)
+{
+    ASSERT(verticalGap >= 0);
+    m_clearGapAfterLastLine = verticalGap;
+}
+
+inline void InlineFormattingState::clearLineAndRuns()
+{
+    m_lines.clear();
+    m_lineBoxes.clear();
+    m_lineRuns.clear();
+    m_clearGapAfterLastLine = { };
+}
+
+inline void InlineFormattingState::shrinkToFit()
+{
+    m_inlineItems.shrinkToFit();
+    m_lines.shrinkToFit();
+    m_lineBoxes.shrinkToFit();
+    m_lineRuns.shrinkToFit();
+}
+
+}
+}
+
+SPECIALIZE_TYPE_TRAITS_LAYOUT_FORMATTING_STATE(InlineFormattingState, isInlineFormattingState())
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineItemcppfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingInlineItemcpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/InlineItem.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/InlineItem.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineItem.cpp                             (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineItem.cpp        2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,52 @@
</span><ins>+/*
+ * 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 "InlineItem.h"
+
+#include "InlineTextItem.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+namespace WebCore {
+namespace Layout {
+
+struct SameSizeAsInlineItem {
+    void* layoutBox;
+    uint8_t enum1;
+    uint8_t enum2;
+    bool widthBool;
+    bool softHyphenBool;
+    bool isWordSeparator;
+    InlineLayoutUnit width;
+    unsigned start;
+    unsigned length;
+};
+
+static_assert(sizeof(InlineItem) == sizeof(SameSizeAsInlineItem), "");
+
+}
+}
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineItemhfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingInlineItemh"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/InlineItem.h (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/InlineItem.h) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineItem.h                               (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineItem.h  2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,86 @@
</span><ins>+/*
+ * Copyright (C) 2018 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 "LayoutBox.h"
+#include "LayoutUnits.h"
+
+namespace WebCore {
+namespace Layout {
+
+class InlineItem {
+public:
+    enum class Type : uint8_t { Text, HardLineBreak, SoftLineBreak, WordBreakOpportunity, Box, Float, InlineBoxStart, InlineBoxEnd };
+    InlineItem(const Box& layoutBox, Type);
+
+    Type type() const { return m_type; }
+    const Box& layoutBox() const { return *m_layoutBox; }
+    const RenderStyle& style() const { return layoutBox().style(); }
+
+    bool isText() const { return type() == Type::Text; }
+    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 isInlineBoxStart() const { return type() == Type::InlineBoxStart; }
+    bool isInlineBoxEnd() const { return type() == Type::InlineBoxEnd; }
+
+private:
+    const Box* m_layoutBox { nullptr };
+    Type m_type { };
+
+protected:
+    // For InlineTextItem
+    enum class TextItemType  : uint8_t { Undefined, Whitespace, NonWhitespace };
+    TextItemType m_textItemType { TextItemType::Undefined };
+    bool m_hasWidth { false };
+    bool m_hasTrailingSoftHyphen { false };
+    bool m_isWordSeparator { false };
+    InlineLayoutUnit m_width { };
+    unsigned m_length { 0 };
+
+    // For InlineTextItem and InlineSoftLineBreakItem
+    unsigned m_startOrPosition { 0 };
+};
+
+inline InlineItem::InlineItem(const Box& layoutBox, Type type)
+    : m_layoutBox(&layoutBox)
+    , m_type(type)
+{
+}
+
+#define SPECIALIZE_TYPE_TRAITS_INLINE_ITEM(ToValueTypeName, predicate) \
+SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::Layout::ToValueTypeName) \
+    static bool isType(const WebCore::Layout::InlineItem& inlineItem) { return inlineItem.predicate; } \
+SPECIALIZE_TYPE_TRAITS_END()
+
+}
+}
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineLinecppfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingInlineLinecpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/InlineLine.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/InlineLine.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineLine.cpp                             (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineLine.cpp        2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,539 @@
</span><ins>+/*
+ * 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 "FontCascade.h"
+#include "InlineFormattingContext.h"
+#include "InlineSoftLineBreakItem.h"
+#include "LayoutBoxGeometry.h"
+#include "RuntimeEnabledFeatures.h"
+#include "TextFlags.h"
+#include "TextUtil.h"
+#include <wtf/IsoMallocInlines.h>
+
+namespace WebCore {
+namespace Layout {
+
+Line::Line(const InlineFormattingContext& inlineFormattingContext)
+    : m_inlineFormattingContext(inlineFormattingContext)
+    , m_trimmableTrailingContent(m_runs)
+{
+}
+
+Line::~Line()
+{
+}
+
+void Line::initialize()
+{
+    m_nonSpanningInlineLevelBoxCount = 0;
+    m_contentLogicalWidth = { };
+    m_runs.clear();
+    m_trailingSoftHyphenWidth = { };
+    m_trimmableTrailingContent.reset();
+}
+
+void Line::removeCollapsibleContent(InlineLayoutUnit extraHorizontalSpace)
+{
+    removeTrailingTrimmableContent();
+    visuallyCollapsePreWrapOverflowContent(extraHorizontalSpace);
+}
+
+void Line::applyRunExpansion(InlineLayoutUnit extraHorizontalSpace)
+{
+    ASSERT(formattingContext().root().style().textAlign() == TextAlignMode::Justify);
+    // Text is justified according to the method specified by the text-justify property,
+    // in order to exactly fill the line box. Unless otherwise specified by text-align-last,
+    // the last line before a forced break or the end of the block is start-aligned.
+    if (m_runs.isEmpty() || m_runs.last().isLineBreak())
+        return;
+
+    auto expansionOpportunityCount = 0;
+    Run* lastRunWithContent = nullptr;
+    // Collect and distribute the expansion opportunities.
+    for (auto& run : m_runs) {
+        expansionOpportunityCount += run.expansionOpportunityCount();
+        if (run.isText() || run.isBox())
+            lastRunWithContent = &run;
+    }
+    // Need to fix up the last run's trailing expansion.
+    if (lastRunWithContent && lastRunWithContent->hasExpansionOpportunity()) {
+        // Turn off the trailing bits first and add the forbid trailing expansion.
+        auto leadingExpansion = lastRunWithContent->expansionBehavior() & LeftExpansionMask;
+        lastRunWithContent->setExpansionBehavior(leadingExpansion | ForbidRightExpansion);
+    }
+    // Anything to distribute?
+    if (!expansionOpportunityCount || !extraHorizontalSpace)
+        return;
+    // Distribute the extra space.
+    auto expansionToDistribute = extraHorizontalSpace / expansionOpportunityCount;
+    auto accumulatedExpansion = InlineLayoutUnit { };
+    for (auto& run : m_runs) {
+        // Expand and move runs by the accumulated expansion.
+        run.moveHorizontally(accumulatedExpansion);
+        if (!run.hasExpansionOpportunity())
+            continue;
+        ASSERT(run.expansionOpportunityCount());
+        auto computedExpansion = expansionToDistribute * run.expansionOpportunityCount();
+        // FIXME: Check why we need to set both.
+        run.setHorizontalExpansion(computedExpansion);
+        run.shrinkHorizontally(-computedExpansion);
+        accumulatedExpansion += computedExpansion;
+    }
+    // Content grows as runs expand.
+    m_contentLogicalWidth += accumulatedExpansion;
+}
+
+void Line::removeTrailingTrimmableContent()
+{
+    if (m_trimmableTrailingContent.isEmpty() || m_runs.isEmpty())
+        return;
+
+    // Complex line layout quirk: keep the trailing whitespace around when it is followed by a line break, unless the content overflows the line.
+    if (RuntimeEnabledFeatures::sharedFeatures().layoutFormattingContextIntegrationEnabled()) {
+        auto isTextAlignRight = [&] {
+            auto textAlign = formattingContext().root().style().textAlign();
+            return textAlign == TextAlignMode::Right
+                || textAlign == TextAlignMode::WebKitRight
+                || textAlign == TextAlignMode::End;
+            }();
+
+        if (m_runs.last().isLineBreak() && !isTextAlignRight) {
+            m_trimmableTrailingContent.reset();
+            return;
+        }
+    }
+
+    m_contentLogicalWidth -= m_trimmableTrailingContent.remove();
+}
+
+void Line::visuallyCollapsePreWrapOverflowContent(InlineLayoutUnit extraHorizontalSpace)
+{
+    ASSERT(m_trimmableTrailingContent.isEmpty());
+    // If white-space is set to pre-wrap, the UA must
+    // ...
+    // It may also visually collapse the character advance widths of any that would otherwise overflow.
+    auto overflowWidth = -extraHorizontalSpace;
+    if (overflowWidth <= 0)
+        return;
+    // Let's just find the trailing pre-wrap whitespace content for now (e.g check if there are multiple trailing runs with
+    // different set of white-space values and decide if the in-between pre-wrap content should be collapsed as well.)
+    InlineLayoutUnit trimmedContentWidth = 0;
+    for (auto& run : WTF::makeReversedRange(m_runs)) {
+        if (run.style().whiteSpace() != WhiteSpace::PreWrap) {
+            // We are only interested in pre-wrap trailing content.
+            break;
+        }
+        auto preWrapVisuallyCollapsibleInlineItem = run.isInlineBoxStart() || run.isInlineBoxEnd() || run.hasTrailingWhitespace();
+        if (!preWrapVisuallyCollapsibleInlineItem)
+            break;
+        ASSERT(!run.hasCollapsibleTrailingWhitespace());
+        InlineLayoutUnit trimmableWidth = { };
+        if (run.isText()) {
+            // FIXME: We should always collapse the run at a glyph boundary as the spec indicates: "collapse the character advance widths of any that would otherwise overflow"
+            // and the trimmed width should be capped at std::min(run.trailingWhitespaceWidth(), overflowWidth) for text runs. Both FF and Chrome agree.
+            trimmableWidth = run.trailingWhitespaceWidth();
+            run.visuallyCollapseTrailingWhitespace();
+        } else {
+            trimmableWidth = run.logicalWidth();
+            run.shrinkHorizontally(trimmableWidth);
+        }
+        trimmedContentWidth += trimmableWidth;
+        overflowWidth -= trimmableWidth;
+        if (overflowWidth <= 0)
+            break;
+    }
+    m_contentLogicalWidth -= trimmedContentWidth;
+}
+
+void Line::append(const InlineItem& inlineItem, InlineLayoutUnit logicalWidth)
+{
+    if (inlineItem.isText())
+        appendTextContent(downcast<InlineTextItem>(inlineItem), logicalWidth);
+    else if (inlineItem.isLineBreak())
+        appendLineBreak(inlineItem);
+    else if (inlineItem.isWordBreakOpportunity())
+        appendWordBreakOpportunity(inlineItem);
+    else if (inlineItem.isInlineBoxStart())
+        appendInlineBoxStart(inlineItem, logicalWidth);
+    else if (inlineItem.isInlineBoxEnd())
+        appendInlineBoxEnd(inlineItem, logicalWidth);
+    else if (inlineItem.layoutBox().isReplacedBox())
+        appendReplacedInlineLevelBox(inlineItem, logicalWidth);
+    else if (inlineItem.isBox())
+        appendNonReplacedInlineLevelBox(inlineItem, logicalWidth);
+    else
+        ASSERT_NOT_REACHED();
+}
+
+void Line::appendNonBreakableSpace(const InlineItem& inlineItem, InlineLayoutUnit logicalLeft, InlineLayoutUnit logicalWidth)
+{
+    m_runs.append({ inlineItem, logicalLeft, logicalWidth });
+    m_contentLogicalWidth += logicalWidth;
+}
+
+void Line::appendInlineBoxStart(const InlineItem& inlineItem, InlineLayoutUnit logicalWidth)
+{
+    // This is really just a placeholder to mark the start of the inline box <span>.
+    auto& boxGeometry = formattingContext().geometryForBox(inlineItem.layoutBox());
+    auto adjustedRunStart = contentLogicalRight() + std::min(boxGeometry.marginStart(), 0_lu);
+    ++m_nonSpanningInlineLevelBoxCount;
+    appendNonBreakableSpace(inlineItem, adjustedRunStart, logicalWidth);
+}
+
+void Line::appendInlineBoxEnd(const InlineItem& inlineItem, InlineLayoutUnit logicalWidth)
+{
+    // This is really just a placeholder to mark the end of the inline box </span>.
+    auto removeTrailingLetterSpacing = [&] {
+        if (!m_trimmableTrailingContent.isTrailingRunPartiallyTrimmable())
+            return;
+        m_contentLogicalWidth -= m_trimmableTrailingContent.removePartiallyTrimmableContent();
+    };
+    // Prevent trailing letter-spacing from spilling out of the inline box.
+    // https://drafts.csswg.org/css-text-3/#letter-spacing-property See example 21.
+    removeTrailingLetterSpacing();
+    appendNonBreakableSpace(inlineItem, contentLogicalWidth(), logicalWidth);
+}
+
+void Line::appendTextContent(const InlineTextItem& inlineTextItem, InlineLayoutUnit logicalWidth)
+{
+    auto& style = inlineTextItem.style();
+    auto willCollapseCompletely = [&] {
+        if (inlineTextItem.isEmptyContent())
+            return true;
+        if (!inlineTextItem.isWhitespace())
+            return false;
+        if (InlineTextItem::shouldPreserveSpacesAndTabs(inlineTextItem))
+            return false;
+        // Check if the last item is collapsed as well.
+        for (auto& run : WTF::makeReversedRange(m_runs)) {
+            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 box has preserve whitespace style, "<span style="white-space: pre">  </span> " <- this whitespace stays around.
+            if (run.isText())
+                return run.hasCollapsibleTrailingWhitespace();
+            ASSERT(run.isInlineBoxStart() || run.isInlineBoxEnd() || run.isWordBreakOpportunity());
+        }
+        // Leading whitespace.
+        return true;
+    };
+
+    if (willCollapseCompletely())
+        return;
+
+    auto needsNewRun = [&] {
+        if (m_runs.isEmpty())
+            return true;
+        auto& lastRun = m_runs.last();
+        if (&lastRun.layoutBox() != &inlineTextItem.layoutBox())
+            return true;
+        if (!lastRun.isText())
+            return true;
+        if (lastRun.hasCollapsedTrailingWhitespace())
+            return true;
+        if (inlineTextItem.isWordSeparator() && style.fontCascade().wordSpacing())
+            return true;
+        return false;
+    }();
+    auto oldContentLogicalWidth = contentLogicalWidth();
+    if (needsNewRun) {
+        // Note, negative words spacing may cause glyph overlap.
+        auto runLogicalLeft = contentLogicalRight() + (inlineTextItem.isWordSeparator() ? style.fontCascade().wordSpacing() : 0.0f);
+        m_runs.append({ inlineTextItem, runLogicalLeft, logicalWidth });
+        m_contentLogicalWidth = std::max(oldContentLogicalWidth, runLogicalLeft + logicalWidth);
+    } else {
+        m_runs.last().expand(inlineTextItem, logicalWidth);
+        // Do not let negative letter spacing make the content shorter than it already is.
+        m_contentLogicalWidth += std::max(0.0f, logicalWidth);
+    }
+    // Set the trailing trimmable content.
+    if (inlineTextItem.isWhitespace() && !InlineTextItem::shouldPreserveSpacesAndTabs(inlineTextItem)) {
+        m_trimmableTrailingContent.addFullyTrimmableContent(m_runs.size() - 1, contentLogicalWidth() - oldContentLogicalWidth);
+        return;
+    }
+    // Any non-whitespace, no-trimmable content resets the existing trimmable.
+    m_trimmableTrailingContent.reset();
+    if (!formattingContext().layoutState().shouldIgnoreTrailingLetterSpacing() && !inlineTextItem.isWhitespace() && style.letterSpacing() > 0)
+        m_trimmableTrailingContent.addPartiallyTrimmableContent(m_runs.size() - 1, style.letterSpacing());
+    m_trailingSoftHyphenWidth = inlineTextItem.hasTrailingSoftHyphen() ? makeOptional(style.fontCascade().width(TextRun { StringView { style.hyphenString() } })) : WTF::nullopt;
+}
+
+void Line::appendNonReplacedInlineLevelBox(const InlineItem& inlineItem, InlineLayoutUnit marginBoxLogicalWidth)
+{
+    m_trimmableTrailingContent.reset();
+    m_trailingSoftHyphenWidth = { };
+    m_contentLogicalWidth += marginBoxLogicalWidth;
+    ++m_nonSpanningInlineLevelBoxCount;
+    auto marginStart = formattingContext().geometryForBox(inlineItem.layoutBox()).marginStart();
+    if (marginStart >= 0) {
+        m_runs.append({ inlineItem, contentLogicalRight(), marginBoxLogicalWidth });
+        return;
+    }
+    // Negative margin-start pulls the content to the logical left direction.
+    // Negative margin also squeezes the margin box, we need to stretch it to make sure the subsequent content won't overlap.
+    // e.g. <img style="width: 100px; margin-left: -100px;"> pulls the replaced box to -100px with the margin box width of 0px.
+    // Instead we need to position it at -100px and size it to 100px so the subsequent content starts at 0px. 
+    m_runs.append({ inlineItem, contentLogicalRight() + marginStart, marginBoxLogicalWidth - marginStart });
+}
+
+void Line::appendReplacedInlineLevelBox(const InlineItem& inlineItem, InlineLayoutUnit marginBoxLogicalWidth)
+{
+    ASSERT(inlineItem.layoutBox().isReplacedBox());
+    // FIXME: Surely replaced boxes behave differently.
+    appendNonReplacedInlineLevelBox(inlineItem, marginBoxLogicalWidth);
+}
+
+void Line::appendLineBreak(const InlineItem& inlineItem)
+{
+    m_trailingSoftHyphenWidth = { };
+    if (inlineItem.isHardLineBreak()) {
+        ++m_nonSpanningInlineLevelBoxCount;
+        return m_runs.append({ inlineItem, contentLogicalRight(), 0_lu });
+    }
+    // Soft line breaks (preserved new line characters) require inline text boxes for compatibility reasons.
+    ASSERT(inlineItem.isSoftLineBreak());
+    m_runs.append({ downcast<InlineSoftLineBreakItem>(inlineItem), contentLogicalRight() });
+}
+
+void Line::appendWordBreakOpportunity(const InlineItem& inlineItem)
+{
+    m_runs.append({ inlineItem, contentLogicalRight(), 0_lu });
+}
+
+void Line::addTrailingHyphen(InlineLayoutUnit hyphenLogicalWidth)
+{
+    for (auto& run : WTF::makeReversedRange(m_runs)) {
+        if (!run.isText())
+            continue;
+        run.setNeedsHyphen(hyphenLogicalWidth);
+        m_contentLogicalWidth += hyphenLogicalWidth;
+        return;
+    }
+    ASSERT_NOT_REACHED();
+}
+
+const InlineFormattingContext& Line::formattingContext() const
+{
+    return m_inlineFormattingContext;
+}
+
+Line::TrimmableTrailingContent::TrimmableTrailingContent(RunList& runs)
+    : m_runs(runs)
+{
+}
+
+void Line::TrimmableTrailingContent::addFullyTrimmableContent(size_t runIndex, InlineLayoutUnit trimmableWidth)
+{
+    // Any subsequent trimmable whitespace should collapse to zero advanced width and ignored at ::appendTextContent().
+    ASSERT(!m_hasFullyTrimmableContent);
+    m_fullyTrimmableWidth = trimmableWidth;
+    // Note that just because the trimmable width is 0 (font-size: 0px), it does not mean we don't have a trimmable trailing content.
+    m_hasFullyTrimmableContent = true;
+    m_firstTrimmableRunIndex = m_firstTrimmableRunIndex.valueOr(runIndex);
+}
+
+void Line::TrimmableTrailingContent::addPartiallyTrimmableContent(size_t runIndex, InlineLayoutUnit trimmableWidth)
+{
+    // Do not add trimmable letter spacing after a fully trimmable whitespace.
+    ASSERT(!m_firstTrimmableRunIndex);
+    ASSERT(!m_hasFullyTrimmableContent);
+    ASSERT(!m_partiallyTrimmableWidth);
+    ASSERT(trimmableWidth);
+    m_partiallyTrimmableWidth = trimmableWidth;
+    m_firstTrimmableRunIndex = runIndex;
+}
+
+InlineLayoutUnit Line::TrimmableTrailingContent::remove()
+{
+    // Remove trimmable trailing content and move all the subsequent trailing runs.
+    // <span> </span><span></span>
+    // [trailing whitespace][inline box end][inline box start][inline box end]
+    // Trim the whitespace run and move the trailing inline box runs to the logical left.
+    ASSERT(!isEmpty());
+    auto& trimmableRun = m_runs[*m_firstTrimmableRunIndex];
+    ASSERT(trimmableRun.isText());
+
+    if (m_hasFullyTrimmableContent)
+        trimmableRun.removeTrailingWhitespace();
+    if (m_partiallyTrimmableWidth)
+        trimmableRun.removeTrailingLetterSpacing();
+
+    auto trimmableWidth = width();
+    // When the trimmable run is followed by some non-content runs, we need to adjust their horizontal positions.
+    // e.g. <div>text is followed by trimmable content    <span> </span></div>
+    // When the [text...] run is trimmed (trailing whitespace is removed), both "<span>" and "</span>" runs
+    // need to be moved horizontally to catch up with the [text...] run. Note that the whitespace inside the <span> does
+    // not produce a run since in ::appendText() we see it as a fully collapsible run.
+    for (auto index = *m_firstTrimmableRunIndex + 1; index < m_runs.size(); ++index) {
+        auto& run = m_runs[index];
+        ASSERT(run.isWordBreakOpportunity() || run.isInlineBoxStart() || run.isInlineBoxEnd() || run.isLineBreak());
+        run.moveHorizontally(-trimmableWidth);
+    }
+    if (!trimmableRun.textContent()->length()) {
+        // This trimmable run is fully collapsed now (e.g. <div><img>    <span></span></div>).
+        // We don't need to keep it around anymore.
+        m_runs.remove(*m_firstTrimmableRunIndex);
+    }
+    reset();
+    return trimmableWidth;
+}
+
+InlineLayoutUnit Line::TrimmableTrailingContent::removePartiallyTrimmableContent()
+{
+    // Partially trimmable content is always gated by a fully trimmable content.
+    // We can't just trim spacing in the middle.
+    ASSERT(!m_fullyTrimmableWidth);
+    return remove();
+}
+
+Line::Run::Run(const InlineItem& inlineItem, InlineLayoutUnit logicalLeft, InlineLayoutUnit logicalWidth)
+    : m_type(inlineItem.type())
+    , m_layoutBox(&inlineItem.layoutBox())
+    , m_logicalLeft(logicalLeft)
+    , m_logicalWidth(logicalWidth)
+{
+}
+
+Line::Run::Run(const InlineSoftLineBreakItem& softLineBreakItem, InlineLayoutUnit logicalLeft)
+    : m_type(softLineBreakItem.type())
+    , m_layoutBox(&softLineBreakItem.layoutBox())
+    , m_logicalLeft(logicalLeft)
+    , m_textContent({ softLineBreakItem.position(), 1, softLineBreakItem.inlineTextBox().content() })
+{
+}
+
+Line::Run::Run(const InlineTextItem& inlineTextItem, InlineLayoutUnit logicalLeft, InlineLayoutUnit logicalWidth)
+    : m_type(InlineItem::Type::Text)
+    , m_layoutBox(&inlineTextItem.layoutBox())
+    , m_logicalLeft(logicalLeft)
+    , m_logicalWidth(logicalWidth)
+    , m_whitespaceIsExpansionOpportunity(!TextUtil::shouldPreserveSpacesAndTabs(inlineTextItem.layoutBox()))
+    , m_trailingWhitespaceType(trailingWhitespaceType(inlineTextItem))
+    , m_textContent({ inlineTextItem.start(), m_trailingWhitespaceType == TrailingWhitespace::Collapsed ? 1 : inlineTextItem.length(), inlineTextItem.inlineTextBox().content() })
+{
+    if (m_trailingWhitespaceType != TrailingWhitespace::None) {
+        m_trailingWhitespaceWidth = logicalWidth;
+        if (m_whitespaceIsExpansionOpportunity)
+            m_expansionOpportunityCount = 1;
+    }
+}
+
+void Line::Run::expand(const InlineTextItem& inlineTextItem, InlineLayoutUnit logicalWidth)
+{
+    // FIXME: This is a very simple expansion merge. We should eventually switch over to FontCascade::expansionOpportunityCount.
+    ASSERT(!hasCollapsedTrailingWhitespace());
+    ASSERT(isText() && inlineTextItem.isText());
+    ASSERT(m_layoutBox == &inlineTextItem.layoutBox());
+
+    m_logicalWidth += logicalWidth;
+    m_trailingWhitespaceType = trailingWhitespaceType(inlineTextItem);
+
+    if (m_trailingWhitespaceType == TrailingWhitespace::None) {
+        m_trailingWhitespaceWidth = { };
+        setExpansionBehavior(AllowLeftExpansion | AllowRightExpansion);
+        m_textContent->expand(inlineTextItem.length());
+        return;
+    }
+    m_trailingWhitespaceWidth += logicalWidth;
+    if (m_whitespaceIsExpansionOpportunity)
+        ++m_expansionOpportunityCount;
+    setExpansionBehavior(DefaultExpansion);
+    m_textContent->expand(m_trailingWhitespaceType == TrailingWhitespace::Collapsed ? 1 : inlineTextItem.length());
+}
+
+bool Line::Run::hasTrailingLetterSpacing() const
+{
+    return !hasTrailingWhitespace() && style().letterSpacing() > 0;
+}
+
+InlineLayoutUnit Line::Run::trailingLetterSpacing() const
+{
+    if (!hasTrailingLetterSpacing())
+        return { };
+    return InlineLayoutUnit { style().letterSpacing() };
+}
+
+void Line::Run::removeTrailingLetterSpacing()
+{
+    ASSERT(hasTrailingLetterSpacing());
+    shrinkHorizontally(trailingLetterSpacing());
+    ASSERT(logicalWidth() > 0 || (!logicalWidth() && style().letterSpacing() >= static_cast<float>(intMaxForLayoutUnit)));
+}
+
+void Line::Run::removeTrailingWhitespace()
+{
+    // According to https://www.w3.org/TR/css-text-3/#white-space-property matrix
+    // Trimmable whitespace is always collapsible so the length of the trailing trimmable whitespace is always 1 (or non-existent).
+    ASSERT(m_textContent->length());
+    constexpr size_t trailingTrimmableContentLength = 1;
+    m_textContent->shrink(trailingTrimmableContentLength);
+    visuallyCollapseTrailingWhitespace();
+}
+
+void Line::Run::visuallyCollapseTrailingWhitespace()
+{
+    // This is just a visual adjustment, the text length should remain the same.
+    shrinkHorizontally(m_trailingWhitespaceWidth);
+    m_trailingWhitespaceWidth = { };
+    m_trailingWhitespaceType = TrailingWhitespace::None;
+
+    if (m_whitespaceIsExpansionOpportunity) {
+        ASSERT(m_expansionOpportunityCount);
+        m_expansionOpportunityCount--;
+    }
+    setExpansionBehavior(AllowLeftExpansion | AllowRightExpansion);
+}
+
+void Line::Run::setExpansionBehavior(ExpansionBehavior expansionBehavior)
+{
+    ASSERT(isText());
+    m_expansion.behavior = expansionBehavior;
+}
+
+ExpansionBehavior Line::Run::expansionBehavior() const
+{
+    ASSERT(isText());
+    return m_expansion.behavior;
+}
+
+void Line::Run::setHorizontalExpansion(InlineLayoutUnit logicalExpansion)
+{
+    ASSERT(isText());
+    ASSERT(hasExpansionOpportunity());
+    m_expansion.horizontalExpansion = logicalExpansion;
+}
+
+}
+}
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineLinehfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingInlineLineh"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/InlineLine.h (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/InlineLine.h) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineLine.h                               (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineLine.h  2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,206 @@
</span><ins>+/*
+ * 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 "InlineItem.h"
+#include "InlineLineRun.h"
+#include "InlineTextItem.h"
+
+namespace WebCore {
+namespace Layout {
+
+class InlineFormattingContext;
+class InlineSoftLineBreakItem;
+
+class Line {
+public:
+    Line(const InlineFormattingContext&);
+    ~Line();
+
+    void initialize();
+
+    void append(const InlineItem&, InlineLayoutUnit logicalWidth);
+
+    InlineLayoutUnit contentLogicalWidth() const { return m_contentLogicalWidth; }
+    InlineLayoutUnit contentLogicalRight() const { return m_runs.isEmpty() ? 0.0f : m_runs.last().logicalRight(); }
+    size_t nonSpanningInlineLevelBoxCount() const { return m_nonSpanningInlineLevelBoxCount; }
+
+    InlineLayoutUnit trimmableTrailingWidth() const { return m_trimmableTrailingContent.width(); }
+    bool isTrailingRunFullyTrimmable() const { return m_trimmableTrailingContent.isTrailingRunFullyTrimmable(); }
+
+    Optional<InlineLayoutUnit> trailingSoftHyphenWidth() const { return m_trailingSoftHyphenWidth; }
+    void addTrailingHyphen(InlineLayoutUnit hyphenLogicalWidth);
+
+    void removeCollapsibleContent(InlineLayoutUnit extraHorizontalSpace);
+    void applyRunExpansion(InlineLayoutUnit extraHorizontalSpace);
+
+    struct Run {
+        bool isText() const { return m_type == InlineItem::Type::Text; }
+        bool isBox() const { return m_type == InlineItem::Type::Box; }
+        bool isLineBreak() const { return isHardLineBreak() || isSoftLineBreak(); }
+        bool isSoftLineBreak() const  { return m_type == InlineItem::Type::SoftLineBreak; }
+        bool isHardLineBreak() const { return m_type == InlineItem::Type::HardLineBreak; }
+        bool isWordBreakOpportunity() const { return m_type == InlineItem::Type::WordBreakOpportunity; }
+        bool isInlineBoxStart() const { return m_type == InlineItem::Type::InlineBoxStart; }
+        bool isInlineBoxEnd() const { return m_type == InlineItem::Type::InlineBoxEnd; }
+
+        const Box& layoutBox() const { return *m_layoutBox; }
+        const RenderStyle& style() const { return m_layoutBox->style(); }
+        const Optional<LineRun::Text>& textContent() const { return m_textContent; }
+
+        InlineLayoutUnit logicalWidth() const { return m_logicalWidth; }
+        InlineLayoutUnit logicalLeft() const { return m_logicalLeft; }
+        InlineLayoutUnit logicalRight() const { return logicalLeft() + logicalWidth(); }
+
+        const LineRun::Expansion& expansion() const { return m_expansion; }
+        bool hasExpansionOpportunity() const { return m_expansionOpportunityCount; }
+        ExpansionBehavior expansionBehavior() const;
+        unsigned expansionOpportunityCount() const { return m_expansionOpportunityCount; }
+
+        bool hasTrailingWhitespace() const { return m_trailingWhitespaceType != TrailingWhitespace::None; }
+        InlineLayoutUnit trailingWhitespaceWidth() const { return m_trailingWhitespaceWidth; }
+
+    private:
+        friend class Line;
+
+        Run(const InlineTextItem&, InlineLayoutUnit logicalLeft, InlineLayoutUnit logicalWidth);
+        Run(const InlineSoftLineBreakItem&, InlineLayoutUnit logicalLeft);
+        Run(const InlineItem&, InlineLayoutUnit logicalLeft, InlineLayoutUnit logicalWidth);
+
+        void expand(const InlineTextItem&, InlineLayoutUnit logicalWidth);
+        void moveHorizontally(InlineLayoutUnit offset) { m_logicalLeft += offset; }
+        void shrinkHorizontally(InlineLayoutUnit width) { m_logicalWidth -= width; }
+        void setExpansionBehavior(ExpansionBehavior);
+        void setHorizontalExpansion(InlineLayoutUnit logicalExpansion);
+        void setNeedsHyphen(InlineLayoutUnit hyphenLogicalWidth);
+
+        enum class TrailingWhitespace {
+            None,
+            NotCollapsible,
+            Collapsible,
+            Collapsed
+        };
+        bool hasCollapsibleTrailingWhitespace() const { return m_trailingWhitespaceType == TrailingWhitespace::Collapsible || hasCollapsedTrailingWhitespace(); }
+        bool hasCollapsedTrailingWhitespace() const { return m_trailingWhitespaceType == TrailingWhitespace::Collapsed; }
+        TrailingWhitespace trailingWhitespaceType(const InlineTextItem&) const;
+        void removeTrailingWhitespace();
+        void visuallyCollapseTrailingWhitespace();
+
+        bool hasTrailingLetterSpacing() const;
+        InlineLayoutUnit trailingLetterSpacing() const;
+        void removeTrailingLetterSpacing();
+
+        InlineItem::Type m_type { InlineItem::Type::Text };
+        const Box* m_layoutBox { nullptr };
+        InlineLayoutUnit m_logicalLeft { 0 };
+        InlineLayoutUnit m_logicalWidth { 0 };
+        bool m_whitespaceIsExpansionOpportunity { false };
+        TrailingWhitespace m_trailingWhitespaceType { TrailingWhitespace::None };
+        InlineLayoutUnit m_trailingWhitespaceWidth { 0 };
+        Optional<LineRun::Text> m_textContent;
+        LineRun::Expansion m_expansion;
+        unsigned m_expansionOpportunityCount { 0 };
+    };
+    using RunList = Vector<Run, 10>;
+    const RunList& runs() const { return m_runs; }
+
+private:
+    void appendNonBreakableSpace(const InlineItem&, InlineLayoutUnit logicalLeft, InlineLayoutUnit logicalWidth);
+    void appendTextContent(const InlineTextItem&, InlineLayoutUnit logicalWidth);
+    void appendNonReplacedInlineLevelBox(const InlineItem&, InlineLayoutUnit marginBoxLogicalWidth);
+    void appendReplacedInlineLevelBox(const InlineItem&, InlineLayoutUnit marginBoxLogicalWidth);
+    void appendInlineBoxStart(const InlineItem&, InlineLayoutUnit logicalWidth);
+    void appendInlineBoxEnd(const InlineItem&, InlineLayoutUnit logicalWidth);
+    void appendLineBreak(const InlineItem&);
+    void appendWordBreakOpportunity(const InlineItem&);
+
+    void removeTrailingTrimmableContent();
+    void visuallyCollapsePreWrapOverflowContent(InlineLayoutUnit extraHorizontalSpace);
+
+    const InlineFormattingContext& formattingContext() const;
+
+    struct TrimmableTrailingContent {
+        TrimmableTrailingContent(RunList&);
+
+        void addFullyTrimmableContent(size_t runIndex, InlineLayoutUnit trimmableWidth);
+        void addPartiallyTrimmableContent(size_t runIndex, InlineLayoutUnit trimmableWidth);
+        InlineLayoutUnit remove();
+        InlineLayoutUnit removePartiallyTrimmableContent();
+
+        InlineLayoutUnit width() const { return m_fullyTrimmableWidth + m_partiallyTrimmableWidth; }
+        bool isEmpty() const { return !m_firstTrimmableRunIndex.hasValue(); }
+        bool isTrailingRunFullyTrimmable() const { return m_hasFullyTrimmableContent; }
+        bool isTrailingRunPartiallyTrimmable() const { return m_partiallyTrimmableWidth; }
+
+        void reset();
+
+    private:
+        RunList& m_runs;
+        Optional<size_t> m_firstTrimmableRunIndex;
+        bool m_hasFullyTrimmableContent { false };
+        InlineLayoutUnit m_fullyTrimmableWidth { 0 };
+        InlineLayoutUnit m_partiallyTrimmableWidth { 0 };
+    };
+
+    const InlineFormattingContext& m_inlineFormattingContext;
+    RunList m_runs;
+    TrimmableTrailingContent m_trimmableTrailingContent;
+    InlineLayoutUnit m_contentLogicalWidth { 0 };
+    size_t m_nonSpanningInlineLevelBoxCount { 0 };
+    Optional<InlineLayoutUnit> m_trailingSoftHyphenWidth { 0 };
+};
+
+inline void Line::TrimmableTrailingContent::reset()
+{
+    m_hasFullyTrimmableContent = false;
+    m_firstTrimmableRunIndex = { };
+    m_fullyTrimmableWidth = { };
+    m_partiallyTrimmableWidth = { };
+}
+
+inline Line::Run::TrailingWhitespace Line::Run::trailingWhitespaceType(const InlineTextItem& inlineTextItem) const
+{
+    if (!inlineTextItem.isWhitespace())
+        return TrailingWhitespace::None;
+    if (InlineTextItem::shouldPreserveSpacesAndTabs(inlineTextItem))
+        return TrailingWhitespace::NotCollapsible;
+    if (inlineTextItem.length() == 1)
+        return TrailingWhitespace::Collapsible;
+    return TrailingWhitespace::Collapsed;
+}
+
+inline void Line::Run::setNeedsHyphen(InlineLayoutUnit hyphenLogicalWidth)
+{
+    ASSERT(m_textContent);
+    m_textContent->setNeedsHyphen();
+    m_logicalWidth += hyphenLogicalWidth;
+}
+
+}
+}
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineLineBoxcppfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingInlineLineBoxcpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBox.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/InlineLineBox.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBox.cpp                          (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBox.cpp     2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,174 @@
</span><ins>+/*
+ * Copyright (C) 2020 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 "InlineLineBox.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "LayoutBoxGeometry.h"
+#include "LayoutContainerBox.h"
+
+namespace WebCore {
+namespace Layout {
+
+LineBox::InlineLevelBox::InlineLevelBox(const Box& layoutBox, InlineLayoutUnit logicalLeft, InlineLayoutSize logicalSize, Type type)
+    : m_layoutBox(makeWeakPtr(layoutBox))
+    , m_logicalRect({ }, logicalLeft, logicalSize.width(), logicalSize.height())
+    , m_type(type)
+{
+}
+
+void LineBox::InlineLevelBox::setBaseline(InlineLayoutUnit baseline)
+{
+    // FIXME: Remove legacy rounding.
+    m_baseline = roundToInt(baseline);
+}
+
+void LineBox::InlineLevelBox::setDescent(InlineLayoutUnit descent)
+{
+    // FIXME: Remove legacy rounding.
+    m_descent = roundToInt(descent);
+}
+
+void LineBox::InlineLevelBox::setLayoutBounds(const LayoutBounds& layoutBounds)
+{
+    // FIXME: Remove legacy rounding.
+    m_layoutBounds = { InlineLayoutUnit(roundToInt(layoutBounds.ascent)), InlineLayoutUnit(roundToInt(layoutBounds.descent)) };
+}
+
+void LineBox::InlineLevelBox::setLogicalTop(InlineLayoutUnit logicalTop)
+{
+    // FIXME: Remove legacy rounding.
+    m_logicalRect.setTop(roundToInt(logicalTop));
+}
+
+void LineBox::InlineLevelBox::setLogicalHeight(InlineLayoutUnit logicalHeight)
+{
+    // FIXME: Remove legacy rounding.
+    m_logicalRect.setHeight(roundToInt(logicalHeight));
+}
+
+void LineBox::InlineLevelBox::setHasContent()
+{
+    ASSERT(isInlineBox());
+    m_hasContent = true;
+}
+
+bool LineBox::InlineLevelBox::hasLineBoxRelativeAlignment() const
+{
+    auto verticalAlignment = layoutBox().style().verticalAlign();
+    return verticalAlignment == VerticalAlign::Top || verticalAlignment == VerticalAlign::Bottom;
+}
+
+LineBox::LineBox(const Box& rootLayoutBox, const InlineLayoutPoint& logicalTopleft, InlineLayoutUnit lineLogicalWidth, InlineLayoutUnit contentLogicalLeft, InlineLayoutUnit contentLogicalWidth, size_t nonSpanningInlineLevelBoxCount)
+    : m_logicalRect(logicalTopleft, InlineLayoutSize { lineLogicalWidth, { } })
+    , m_contentLogicalWidth(contentLogicalWidth)
+    , m_rootInlineBox(rootLayoutBox, contentLogicalLeft, InlineLayoutSize { contentLogicalWidth, { } }, InlineLevelBox::Type::RootInlineBox)
+{
+    m_nonRootInlineLevelBoxList.reserveInitialCapacity(nonSpanningInlineLevelBoxCount);
+    m_nonRootInlineLevelBoxMap.reserveInitialCapacity(nonSpanningInlineLevelBoxCount);
+}
+
+void LineBox::addInlineLevelBox(InlineLevelBox&& inlineLevelBox)
+{
+    m_boxTypes.add(inlineLevelBox.type());
+    m_nonRootInlineLevelBoxMap.set(&inlineLevelBox.layoutBox(), m_nonRootInlineLevelBoxList.size());
+    m_nonRootInlineLevelBoxList.append(WTFMove(inlineLevelBox));
+}
+
+InlineRect LineBox::logicalRectForTextRun(const Line::Run& run) const
+{
+    ASSERT(run.isText() || run.isSoftLineBreak());
+    auto* parentInlineBox = &inlineLevelBoxForLayoutBox(run.layoutBox().parent());
+    ASSERT(parentInlineBox->isInlineBox());
+    auto& fontMetrics = parentInlineBox->style().fontMetrics();
+    auto runlogicalTop = parentInlineBox->logicalTop() + parentInlineBox->baseline() - fontMetrics.ascent();
+
+    while (parentInlineBox != &m_rootInlineBox && !parentInlineBox->hasLineBoxRelativeAlignment()) {
+        parentInlineBox = &inlineLevelBoxForLayoutBox(parentInlineBox->layoutBox().parent());
+        ASSERT(parentInlineBox->isInlineBox());
+        runlogicalTop += parentInlineBox->logicalTop();
+    }
+    InlineLayoutUnit logicalHeight = fontMetrics.height();
+    return { runlogicalTop, m_rootInlineBox.logicalLeft() + run.logicalLeft(), run.logicalWidth(), logicalHeight };
+}
+
+InlineRect LineBox::logicalRectForLineBreakBox(const Box& layoutBox) const
+{
+    ASSERT(layoutBox.isLineBreakBox());
+    return logicalRectForInlineLevelBox(layoutBox);
+}
+
+InlineRect LineBox::logicalRectForInlineLevelBox(const Box& layoutBox) const
+{
+    ASSERT(layoutBox.isInlineLevelBox() || layoutBox.isLineBreakBox());
+    // Inline level boxes are relative to their parent unless the vertical alignment makes them relative to the line box (e.g. top, bottom).
+    auto* inlineBox = &inlineLevelBoxForLayoutBox(layoutBox);
+    auto inlineBoxLogicalRect = inlineBox->logicalRect();
+    if (inlineBox->hasLineBoxRelativeAlignment())
+        return inlineBoxLogicalRect;
+
+    // Fast path for inline level boxes on the root inline box (e.g <div><img></div>).
+    if (&layoutBox.parent() == &m_rootInlineBox.layoutBox()) {
+        inlineBoxLogicalRect.moveVertically(m_rootInlineBox.logicalTop());
+        return inlineBoxLogicalRect;
+    }
+
+    // e.g <div><span><img></span></div>
+    auto inlineBoxAbsolutelogicalTop = inlineBoxLogicalRect.top();
+    while (inlineBox != &m_rootInlineBox && !inlineBox->hasLineBoxRelativeAlignment()) {
+        inlineBox = &inlineLevelBoxForLayoutBox(inlineBox->layoutBox().parent());
+        ASSERT(inlineBox->isInlineBox());
+        inlineBoxAbsolutelogicalTop += inlineBox->logicalTop();
+    }
+    return InlineRect { inlineBoxAbsolutelogicalTop, inlineBoxLogicalRect.left(), inlineBoxLogicalRect.width(), inlineBoxLogicalRect.height() };
+}
+
+InlineRect LineBox::logicalBorderBoxForAtomicInlineLevelBox(const Box& layoutBox, const BoxGeometry& boxGeometry) const
+{
+    ASSERT(layoutBox.isAtomicInlineLevelBox());
+    auto logicalRect = logicalRectForInlineLevelBox(layoutBox);
+    // Inline level boxes use their margin box for vertical alignment. Let's covert them to border boxes.
+    logicalRect.moveVertically(boxGeometry.marginBefore());
+    auto verticalMargin = boxGeometry.marginBefore() + boxGeometry.marginAfter();
+    logicalRect.expandVertically(-verticalMargin);
+    return logicalRect;
+}
+
+InlineRect LineBox::logicalBorderBoxForInlineBox(const Box& layoutBox, const BoxGeometry& boxGeometry) const
+{
+    auto logicalRect = logicalRectForInlineLevelBox(layoutBox);
+    // This logical rect is as tall as the "text" content is. Let's adjust with vertical border and padding.
+    auto verticalBorderAndPadding = boxGeometry.verticalBorder() + boxGeometry.verticalPadding().valueOr(0_lu);
+    logicalRect.expandVertically(verticalBorderAndPadding);
+    logicalRect.moveVertically(-(boxGeometry.borderTop() + boxGeometry.paddingTop().valueOr(0_lu)));
+    return logicalRect;
+}
+
+} // namespace Layout
+} // namespace WebCore
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineLineBoxhfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingInlineLineBoxh"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBox.h (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/InlineLineBox.h) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBox.h                            (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBox.h       2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,215 @@
</span><ins>+/*
+ * Copyright (C) 2020 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 "InlineLine.h"
+#include "InlineRect.h"
+#include <wtf/IsoMallocInlines.h>
+#include <wtf/UniqueRef.h>
+#include <wtf/WeakPtr.h>
+
+namespace WebCore {
+namespace Layout {
+
+class BoxGeometry;
+class InlineFormattingContext;
+class LineBoxBuilder;
+struct SimplifiedVerticalAlignment;
+
+//   ____________________________________________________________ Line Box
+// |                                    --------------------
+// |                                   |                    |
+// | ----------------------------------|--------------------|---------- Root Inline Box
+// ||   _____    ___      ___          |                    |
+// ||  |        /   \    /   \         |  Inline Level Box  |
+// ||  |_____  |     |  |     |        |                    |    ascent
+// ||  |       |     |  |     |        |                    |
+// ||__|________\___/____\___/_________|____________________|_______ alignment_baseline
+// ||
+// ||                                                      descent
+// ||_______________________________________________________________
+// |________________________________________________________________
+// The resulting rectangular area that contains the boxes that form a single line of inline-level content is called a line box.
+// https://www.w3.org/TR/css-inline-3/#model
+class LineBox {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    struct InlineLevelBox {
+    public:
+        static LineBox::InlineLevelBox createInlineBox(const Box&, InlineLayoutUnit logicalLeft, InlineLayoutUnit logicalWidth);
+        static LineBox::InlineLevelBox createAtomicInlineLevelBox(const Box&, InlineLayoutUnit logicalLeft, InlineLayoutSize);
+        static LineBox::InlineLevelBox createLineBreakBox(const Box&, InlineLayoutUnit logicalLeft);
+        static LineBox::InlineLevelBox createGenericInlineLevelBox(const Box&, InlineLayoutUnit logicalLeft);
+
+        InlineLayoutUnit baseline() const { return m_baseline; }
+        Optional<InlineLayoutUnit> descent() const { return m_descent; }
+        // See https://www.w3.org/TR/css-inline-3/#layout-bounds
+        struct LayoutBounds {
+            InlineLayoutUnit height() const { return ascent + descent; }
+            bool operator==(const LayoutBounds& other) const { return ascent == other.ascent && descent == other.descent; }
+
+            InlineLayoutUnit ascent { 0 };
+            InlineLayoutUnit descent { 0 };
+        };
+        LayoutBounds layoutBounds() const { return m_layoutBounds; }
+
+        bool hasContent() const { return m_hasContent; }
+        void setHasContent();
+
+        VerticalAlign verticalAlign() const { return layoutBox().style().verticalAlign(); }
+        const Box& layoutBox() const { return *m_layoutBox; }
+        const RenderStyle& style() const { return m_layoutBox->style(); }
+
+        bool isInlineBox() const { return m_type == Type::InlineBox || m_type == Type::RootInlineBox; }
+        bool isRootInlineBox() const { return m_type == Type::RootInlineBox; }
+        bool isAtomicInlineLevelBox() const { return m_type == Type::AtomicInlineLevelBox; }
+        bool isLineBreakBox() const { return m_type == Type::LineBreakBox; }
+        bool hasLineBoxRelativeAlignment() const;
+
+        enum class Type : uint8_t {
+            InlineBox             = 1 << 0,
+            RootInlineBox         = 1 << 1,
+            AtomicInlineLevelBox  = 1 << 2,
+            LineBreakBox          = 1 << 3,
+            GenericInlineLevelBox = 1 << 4
+        };
+        Type type() const { return m_type; }
+
+        InlineLevelBox(const Box&, InlineLayoutUnit logicalLeft, InlineLayoutSize, Type);
+        InlineLevelBox() = default;
+
+    private:
+        friend struct SimplifiedVerticalAlignment;
+        friend class LineBoxBuilder;
+        friend class LineBox;
+
+        const InlineRect& logicalRect() const { return m_logicalRect; }
+        InlineLayoutUnit logicalTop() const { return m_logicalRect.top(); }
+        InlineLayoutUnit logicalBottom() const { return m_logicalRect.bottom(); }
+        InlineLayoutUnit logicalLeft() const { return m_logicalRect.left(); }
+        InlineLayoutUnit logicalWidth() const { return m_logicalRect.width(); }
+        InlineLayoutUnit logicalHeight() const { return m_logicalRect.height(); }
+
+        void setLogicalWidth(InlineLayoutUnit logicalWidth) { m_logicalRect.setWidth(logicalWidth); }
+        void setLogicalHeight(InlineLayoutUnit);
+        void setLogicalTop(InlineLayoutUnit);
+        void setBaseline(InlineLayoutUnit);
+        void setDescent(InlineLayoutUnit);
+        void setLayoutBounds(const LayoutBounds&);
+
+    private:
+        WeakPtr<const Box> m_layoutBox;
+        // This is the combination of margin and border boxes. Inline level boxes are vertically aligned using their margin boxes.
+        InlineRect m_logicalRect;
+        LayoutBounds m_layoutBounds;
+        InlineLayoutUnit m_baseline { 0 };
+        Optional<InlineLayoutUnit> m_descent;
+        bool m_hasContent { false };
+        Type m_type { Type::InlineBox };
+    };
+
+    LineBox(const Box& rootLayoutBox, const InlineLayoutPoint& logicalTopLeft, InlineLayoutUnit contentLogicalLeft, InlineLayoutUnit lineLogicalWidth, InlineLayoutUnit contentLogicalWidth, size_t nonSpanningInlineLevelBoxCount);
+
+    const InlineRect& logicalRect() const { return m_logicalRect; }
+    InlineLayoutUnit logicalWidth() const { return logicalSize().width(); }
+    InlineLayoutUnit logicalHeight() const { return logicalSize().height(); }
+    InlineLayoutPoint logicalTopLeft() const { return logicalRect().topLeft(); }
+    InlineLayoutSize logicalSize() const { return logicalRect().size(); }
+    InlineLayoutUnit contentLogicalWidth() const { return m_contentLogicalWidth; }
+
+    // Note that the line can have many inline boxes and be "empty" the same time e.g. <div><span></span><span></span></div>
+    bool hasContent() const { return m_hasContent; }
+    bool hasInlineBox() const { return m_boxTypes.contains(InlineLevelBox::Type::InlineBox); }
+    bool hasNonInlineBox() const { return m_boxTypes.containsAny({ InlineLevelBox::Type::AtomicInlineLevelBox, InlineLevelBox::Type::LineBreakBox, InlineLevelBox::Type::GenericInlineLevelBox }); }
+    bool hasAtomicInlineLevelBox() const { return m_boxTypes.contains(InlineLevelBox::Type::AtomicInlineLevelBox); }
+
+    const InlineLevelBox& inlineLevelBoxForLayoutBox(const Box& layoutBox) const { return const_cast<LineBox&>(*this).inlineLevelBoxForLayoutBox(layoutBox); }
+
+    InlineRect logicalRectForTextRun(const Line::Run&) const;
+    InlineRect logicalRectForLineBreakBox(const Box&) const;
+    InlineRect logicalRectForRootInlineBox() const { return m_rootInlineBox.logicalRect(); }
+    InlineRect logicalBorderBoxForAtomicInlineLevelBox(const Box&, const BoxGeometry&) const;
+    InlineRect logicalBorderBoxForInlineBox(const Box&, const BoxGeometry&) const;
+
+    const InlineLevelBox& rootInlineBox() const { return m_rootInlineBox; }
+    using InlineLevelBoxList = Vector<InlineLevelBox>;
+    const InlineLevelBoxList& nonRootInlineLevelBoxes() const { return m_nonRootInlineLevelBoxList; }
+
+    InlineLayoutUnit alignmentBaseline() const { return m_rootInlineBox.logicalTop() + m_rootInlineBox.baseline(); }
+
+private:
+    friend class LineBoxBuilder;
+
+    void setLogicalHeight(InlineLayoutUnit logicalHeight) { m_logicalRect.setHeight(logicalHeight); }
+
+    void addInlineLevelBox(InlineLevelBox&&);
+    InlineLevelBoxList& nonRootInlineLevelBoxes() { return m_nonRootInlineLevelBoxList; }
+
+    InlineLevelBox& rootInlineBox() { return m_rootInlineBox; }
+
+    InlineLevelBox& inlineLevelBoxForLayoutBox(const Box& layoutBox) { return &layoutBox == &m_rootInlineBox.layoutBox() ? m_rootInlineBox : m_nonRootInlineLevelBoxList[m_nonRootInlineLevelBoxMap.get(&layoutBox)]; }
+    InlineRect logicalRectForInlineLevelBox(const Box& layoutBox) const;
+
+    void setHasContent(bool hasContent) { m_hasContent = hasContent; }
+
+private:
+    InlineRect m_logicalRect;
+    InlineLayoutUnit m_contentLogicalWidth { 0 };
+    bool m_hasContent { false };
+    OptionSet<InlineLevelBox::Type> m_boxTypes;
+
+    InlineLevelBox m_rootInlineBox;
+    InlineLevelBoxList m_nonRootInlineLevelBoxList;
+
+    HashMap<const Box*, size_t> m_nonRootInlineLevelBoxMap;
+};
+
+inline LineBox::InlineLevelBox LineBox::InlineLevelBox::createAtomicInlineLevelBox(const Box& layoutBox, InlineLayoutUnit logicalLeft, InlineLayoutSize logicalSize)
+{
+    return LineBox::InlineLevelBox { layoutBox, logicalLeft, logicalSize, Type::AtomicInlineLevelBox };
+}
+
+inline LineBox::InlineLevelBox LineBox::InlineLevelBox::createInlineBox(const Box& layoutBox, InlineLayoutUnit logicalLeft, InlineLayoutUnit logicalWidth)
+{
+    return LineBox::InlineLevelBox { layoutBox, logicalLeft, InlineLayoutSize { logicalWidth, { } }, Type::InlineBox };
+}
+
+inline LineBox::InlineLevelBox LineBox::InlineLevelBox::createLineBreakBox(const Box& layoutBox, InlineLayoutUnit logicalLeft)
+{
+    return LineBox::InlineLevelBox { layoutBox, logicalLeft, InlineLayoutSize { }, Type::LineBreakBox };
+}
+
+inline LineBox::InlineLevelBox LineBox::InlineLevelBox::createGenericInlineLevelBox(const Box& layoutBox, InlineLayoutUnit logicalLeft)
+{
+    return LineBox::InlineLevelBox { layoutBox, logicalLeft, InlineLayoutSize { }, Type::GenericInlineLevelBox };
+}
+
+}
+}
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineLineBuildercppfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingInlineLineBuildercpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBuilder.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/InlineLineBuilder.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBuilder.cpp                              (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBuilder.cpp 2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,857 @@
</span><ins>+/*
+ * 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 "FloatingContext.h"
+#include "InlineFormattingContext.h"
+#include "LayoutBox.h"
+#include "LayoutBoxGeometry.h"
+#include "LayoutState.h"
+#include "TextUtil.h"
+#include <wtf/unicode/CharacterNames.h>
+
+namespace WebCore {
+namespace Layout {
+
+static inline bool endsWithSoftWrapOpportunity(const InlineTextItem& currentTextItem, const InlineTextItem& nextInlineTextItem)
+{
+    ASSERT(!nextInlineTextItem.isWhitespace());
+    // We are at the position after a whitespace.
+    if (currentTextItem.isWhitespace())
+        return true;
+    // When both these non-whitespace runs belong to the same layout box, it's guaranteed that
+    // they are split at a soft breaking opportunity. See InlineTextItem::moveToNextBreakablePosition.
+    if (&currentTextItem.inlineTextBox() == &nextInlineTextItem.inlineTextBox())
+        return true;
+    // Now we need to collect at least 3 adjacent characters to be able to make a decision whether the previous text item ends with breaking opportunity.
+    // [ex-][ample] <- second to last[x] last[-] current[a]
+    // We need at least 1 character in the current inline text item and 2 more from previous inline items.
+    auto previousContent = currentTextItem.inlineTextBox().content();
+    auto lineBreakIterator = LazyLineBreakIterator { nextInlineTextItem.inlineTextBox().content() };
+    auto previousContentLength = previousContent.length();
+    // FIXME: We should look into the entire uncommitted content for more text context.
+    UChar lastCharacter = previousContentLength ? previousContent[previousContentLength - 1] : 0;
+    if (lastCharacter == softHyphen && currentTextItem.style().hyphens() == Hyphens::None)
+        return false;
+    UChar secondToLastCharacter = previousContentLength > 1 ? previousContent[previousContentLength - 2] : 0;
+    lineBreakIterator.setPriorContext(lastCharacter, secondToLastCharacter);
+    // Now check if we can break right at the inline item boundary.
+    // With the [ex-ample], findNextBreakablePosition should return the startPosition (0).
+    // FIXME: Check if there's a more correct way of finding breaking opportunities.
+    return !TextUtil::findNextBreakablePosition(lineBreakIterator, 0, nextInlineTextItem.style());
+}
+
+static inline bool isAtSoftWrapOpportunity(const InlineFormattingContext& inlineFormattingContext, const InlineItem& current, const InlineItem& next)
+{
+    // "is at" simple means that there's a soft wrap opportunity right after the [current].
+    // [text][ ][text][inline box start]... (<div>text content<span>..</div>)
+    // soft wrap indexes: 0 and 1 definitely, 2 depends on the content after the [inline box start].
+
+    // https://drafts.csswg.org/css-text-3/#line-break-details
+    // Figure out if the new incoming content puts the uncommitted content on a soft wrap opportunity.
+    // e.g. [inline box start][prior_continuous_content][inline box end] (<span>prior_continuous_content</span>)
+    // An incoming <img> box would enable us to commit the "<span>prior_continuous_content</span>" content
+    // but an incoming text content would not necessarily.
+    ASSERT(current.isText() || current.isBox() || current.isFloat());
+    ASSERT(next.isText() || next.isBox() || next.isFloat());
+    if (current.isText() && next.isText()) {
+        auto& currentInlineTextItem = downcast<InlineTextItem>(current);
+        auto& nextInlineTextItem = downcast<InlineTextItem>(next);
+        if (currentInlineTextItem.isWhitespace()) {
+            // [ ][text] : after [whitespace] position is a soft wrap opportunity.
+            return true;
+        }
+        if (nextInlineTextItem.isWhitespace()) {
+            // [text][ ] (<span>text</span> )
+            // white-space: break-spaces: line breaking opportunity exists after every preserved white space character, but not before.
+            return nextInlineTextItem.style().whiteSpace() != WhiteSpace::BreakSpaces;
+        }
+        if (current.style().lineBreak() == LineBreak::Anywhere || next.style().lineBreak() == LineBreak::Anywhere) {
+            // There is a soft wrap opportunity around every typographic character unit, including around any punctuation character
+            // or preserved white spaces, or in the middle of words.
+            return true;
+        }
+        // Both current and next items are non-whitespace text.
+        // [text][text] : is a continuous content.
+        // [text-][text] : after [hyphen] position is a soft wrap opportunity.
+        return endsWithSoftWrapOpportunity(currentInlineTextItem, nextInlineTextItem);
+    }
+    if (current.isFloat() || next.isFloat()) {
+        // While floats are not part of the inline content and they are not supposed to introduce soft wrap opportunities,
+        // e.g. [text][float box][float box][text][float box][text] is essentially just [text][text][text]
+        // figuring out whether a float (or set of floats) should stay on the line or not (and handle potentially out of order inline items)
+        // brings in unnecessary complexity.
+        return true;
+    }
+    if (current.isBox() || next.isBox()) {
+        auto isImageContent = current.layoutBox().isImage() || next.layoutBox().isImage();
+        if (isImageContent)
+            return inlineFormattingContext.quirks().hasSoftWrapOpportunityAtImage();
+        // [text][inline box start][inline box end][inline box] (text<span></span><img>) : there's a soft wrap opportunity between the [text] and [img].
+        // The line breaking behavior of a replaced element or other atomic inline is equivalent to an ideographic character.
+        return true;
+    }
+    ASSERT_NOT_REACHED();
+    return true;
+}
+
+struct LineCandidate {
+    LineCandidate(bool ignoreTrailingLetterSpacing);
+
+    void reset();
+
+    struct InlineContent {
+        InlineContent(bool ignoreTrailingLetterSpacing);
+
+        const InlineContentBreaker::ContinuousContent& continuousContent() const { return m_continuousContent; }
+        const InlineItem* trailingLineBreak() const { return m_trailingLineBreak; }
+        const InlineItem* trailingWordBreakOpportunity() const { return m_trailingWordBreakOpportunity; }
+
+        void appendInlineItem(const InlineItem&, InlineLayoutUnit logicalWidth);
+        void appendTrailingLineBreak(const InlineItem& lineBreakItem) { m_trailingLineBreak = &lineBreakItem; }
+        void appendtrailingWordBreakOpportunity(const InlineItem& wordBreakItem) { m_trailingWordBreakOpportunity = &wordBreakItem; }
+        void reset();
+        bool isEmpty() const { return m_continuousContent.runs().isEmpty() && !trailingWordBreakOpportunity() && !trailingLineBreak(); }
+        bool hasInlineLevelBox() const { return m_hasInlineLevelBox; }
+
+        void setHasTrailingSoftWrapOpportunity(bool hasTrailingSoftWrapOpportunity) { m_hasTrailingSoftWrapOpportunity = hasTrailingSoftWrapOpportunity; }
+        bool hasTrailingSoftWrapOpportunity() const { return m_hasTrailingSoftWrapOpportunity; }
+
+    private:
+        bool m_ignoreTrailingLetterSpacing { false };
+
+        InlineContentBreaker::ContinuousContent m_continuousContent;
+        const InlineItem* m_trailingLineBreak { nullptr };
+        const InlineItem* m_trailingWordBreakOpportunity { nullptr };
+        bool m_hasInlineLevelBox { false };
+        bool m_hasTrailingSoftWrapOpportunity { false };
+    };
+
+    // Candidate content is a collection of inline content or a float box.
+    InlineContent inlineContent;
+    const InlineItem* floatItem { nullptr };
+};
+
+LineCandidate::LineCandidate(bool ignoreTrailingLetterSpacing)
+    : inlineContent(ignoreTrailingLetterSpacing)
+{
+}
+
+LineCandidate::InlineContent::InlineContent(bool ignoreTrailingLetterSpacing)
+    : m_ignoreTrailingLetterSpacing(ignoreTrailingLetterSpacing)
+{
+}
+
+inline void LineCandidate::InlineContent::appendInlineItem(const InlineItem& inlineItem, InlineLayoutUnit logicalWidth)
+{
+    ASSERT(inlineItem.isText() || inlineItem.isBox() || inlineItem.isInlineBoxStart() || inlineItem.isInlineBoxEnd());
+    auto collapsibleWidth = [&]() -> Optional<InlineLayoutUnit> {
+        if (!inlineItem.isText())
+            return { };
+        auto& inlineTextItem = downcast<InlineTextItem>(inlineItem);
+        if (inlineTextItem.isWhitespace() && !InlineTextItem::shouldPreserveSpacesAndTabs(inlineTextItem)) {
+            // Fully collapsible trailing content.
+            return logicalWidth;
+        }
+        // Check for partially collapsible content.
+        if (m_ignoreTrailingLetterSpacing)
+            return { };
+        auto letterSpacing = inlineItem.style().letterSpacing();
+        if (letterSpacing <= 0)
+            return { };
+        ASSERT(logicalWidth > letterSpacing);
+        return letterSpacing;
+    };
+    m_continuousContent.append(inlineItem, logicalWidth, collapsibleWidth());
+    m_hasInlineLevelBox = m_hasInlineLevelBox || inlineItem.isBox() || inlineItem.isInlineBoxStart();
+}
+
+inline void LineCandidate::InlineContent::reset()
+{
+    m_continuousContent.reset();
+    m_trailingLineBreak = { };
+    m_trailingWordBreakOpportunity = { };
+    m_hasInlineLevelBox = false;
+}
+
+inline void LineCandidate::reset()
+{
+    floatItem = nullptr;
+    inlineContent.reset();
+}
+
+InlineLayoutUnit LineBuilder::inlineItemWidth(const InlineItem& inlineItem, InlineLayoutUnit contentLogicalLeft) const
+{
+    if (is<InlineTextItem>(inlineItem)) {
+        auto& inlineTextItem = downcast<InlineTextItem>(inlineItem);
+        if (auto contentWidth = inlineTextItem.width())
+            return *contentWidth;
+        if (!inlineTextItem.isWhitespace() || InlineTextItem::shouldPreserveSpacesAndTabs(inlineTextItem))
+            return TextUtil::width(inlineTextItem, contentLogicalLeft);
+        return TextUtil::width(inlineTextItem, inlineTextItem.start(), inlineTextItem.start() + 1, contentLogicalLeft);
+    }
+
+    if (inlineItem.isLineBreak() || inlineItem.isWordBreakOpportunity())
+        return { };
+
+    auto& layoutBox = inlineItem.layoutBox();
+    auto& boxGeometry = m_inlineFormattingContext.geometryForBox(layoutBox);
+
+    if (layoutBox.isFloatingPositioned())
+        return boxGeometry.marginBoxWidth();
+
+    if (layoutBox.isReplacedBox())
+        return boxGeometry.marginBoxWidth();
+
+    if (inlineItem.isInlineBoxStart())
+        return boxGeometry.marginStart() + boxGeometry.borderLeft() + boxGeometry.paddingLeft().valueOr(0);
+
+    if (inlineItem.isInlineBoxEnd())
+        return boxGeometry.marginEnd() + boxGeometry.borderRight() + boxGeometry.paddingRight().valueOr(0);
+
+    // Non-replaced inline box (e.g. inline-block)
+    return boxGeometry.marginBoxWidth();
+}
+
+LineBuilder::LineBuilder(InlineFormattingContext& inlineFormattingContext, FloatingState& floatingState, HorizontalConstraints rootHorizontalConstraints, const InlineItems& inlineItems)
+    : m_inlineFormattingContext(inlineFormattingContext)
+    , m_inlineFormattingState(&inlineFormattingContext.formattingState())
+    , m_floatingState(&floatingState)
+    , m_rootHorizontalConstraints(rootHorizontalConstraints)
+    , m_line(inlineFormattingContext)
+    , m_inlineItems(inlineItems)
+{
+}
+
+LineBuilder::LineBuilder(const InlineFormattingContext& inlineFormattingContext, const InlineItems& inlineItems)
+    : m_inlineFormattingContext(inlineFormattingContext)
+    , m_line(inlineFormattingContext)
+    , m_inlineItems(inlineItems)
+{
+}
+
+LineBuilder::LineContent LineBuilder::layoutInlineContent(const InlineItemRange& needsLayoutRange, size_t partialLeadingContentLength, Optional<InlineLayoutUnit> overflowLogicalWidth, const InlineRect& initialLineLogicalRect, bool isFirstLine)
+{
+    initialize(initialConstraintsForLine(initialLineLogicalRect, isFirstLine));
+
+    auto committedContent = placeInlineContent(needsLayoutRange, partialLeadingContentLength, overflowLogicalWidth);
+    auto committedRange = close(needsLayoutRange, committedContent);
+
+    auto isLastLine = isLastLineWithInlineContent(committedRange, needsLayoutRange.end, committedContent.partialTrailingContentLength);
+    return LineContent { committedRange, committedContent.partialTrailingContentLength, committedContent.overflowLogicalWidth, m_floats, m_contentIsConstrainedByFloat
+        , m_lineLogicalRect.topLeft()
+        , m_lineLogicalRect.width()
+        , m_line.contentLogicalWidth()
+        , isLastLine
+        , m_line.nonSpanningInlineLevelBoxCount()
+        , m_line.runs()};
+}
+
+LineBuilder::IntrinsicContent LineBuilder::computedIntrinsicWidth(const InlineItemRange& needsLayoutRange, InlineLayoutUnit availableWidth)
+{
+    initialize({ { { }, { availableWidth, maxInlineLayoutUnit() } }, false });
+    auto committedContent = placeInlineContent(needsLayoutRange, { }, { });
+    auto committedRange = close(needsLayoutRange, committedContent);
+    return { committedRange, m_line.contentLogicalWidth(), m_floats };
+}
+
+void LineBuilder::initialize(const UsedConstraints& lineConstraints)
+{
+    m_floats.clear();
+    m_partialLeadingTextItem = { };
+    m_wrapOpportunityList.clear();
+
+    m_line.initialize();
+    m_lineLogicalRect = lineConstraints.logicalRect;
+    m_contentIsConstrainedByFloat = lineConstraints.isConstrainedByFloat;
+}
+
+LineBuilder::CommittedContent LineBuilder::placeInlineContent(const InlineItemRange& needsLayoutRange, size_t partialLeadingContentLength, Optional<InlineLayoutUnit> leadingLogicalWidth)
+{
+    auto lineCandidate = LineCandidate { layoutState().shouldIgnoreTrailingLetterSpacing() };
+    auto inlineContentBreaker = InlineContentBreaker { };
+
+    auto currentItemIndex = needsLayoutRange.start;
+    size_t committedInlineItemCount = 0;
+    while (currentItemIndex < needsLayoutRange.end) {
+        // 1. Collect the set of runs that we can commit to the line as one entity e.g. <span>text_and_span_start_span_end</span>.
+        // 2. Apply floats and shrink the available horizontal space e.g. <span>intru_<div style="float: left"></div>sive_float</span>.
+        // 3. Check if the content fits the line and commit the content accordingly (full, partial or not commit at all).
+        // 4. Return if we are at the end of the line either by not being able to fit more content or because of an explicit line break.
+        candidateContentForLine(lineCandidate, currentItemIndex, needsLayoutRange, partialLeadingContentLength, std::exchange(leadingLogicalWidth, WTF::nullopt), m_line.contentLogicalRight());
+        // Now check if we can put this content on the current line.
+        auto result = Result { };
+        if (lineCandidate.floatItem) {
+            ASSERT(lineCandidate.inlineContent.isEmpty());
+            handleFloatContent(*lineCandidate.floatItem);
+            // Floats never terminate the line.
+        } else
+            result = handleInlineContent(inlineContentBreaker, needsLayoutRange, lineCandidate);
+        auto isEndOfLine = result.isEndOfLine == InlineContentBreaker::IsEndOfLine::Yes;
+        if (!result.committedCount.isRevert) {
+            committedInlineItemCount += result.committedCount.value;
+            auto& inlineContent = lineCandidate.inlineContent;
+            auto inlineContentIsFullyCommitted = inlineContent.continuousContent().runs().size() == result.committedCount.value && !result.partialTrailingContentLength;
+            if (inlineContentIsFullyCommitted) {
+                if (auto* wordBreakOpportunity = inlineContent.trailingWordBreakOpportunity()) {
+                    // <wbr> needs to be on the line as an empty run so that we can construct an inline box and compute basic geometry.
+                    ++committedInlineItemCount;
+                    m_line.append(*wordBreakOpportunity, { });
+                }
+                if (inlineContent.trailingLineBreak()) {
+                    // Fully committed (or empty) content followed by a line break means "end of line".
+                    // FIXME: This will put the line break box at the end of the line while in case of some inline boxes, the line break
+                    // could very well be at an earlier position. This has no visual implications at this point though (only geometry correctness on the line break box).
+                    // e.g. <span style="border-right: 10px solid green">text<br></span> where the <br>'s horizontal position is before the right border and not after.
+                    m_line.append(*inlineContent.trailingLineBreak(), { });
+                    ++committedInlineItemCount;
+                    isEndOfLine = true;
+                }
+            }
+        } else
+            committedInlineItemCount = result.committedCount.value;
+
+        if (isEndOfLine) {
+            // We can't place any more items on the current line.
+            return { committedInlineItemCount, result.partialTrailingContentLength, result.overflowLogicalWidth };
+        }
+        currentItemIndex = needsLayoutRange.start + committedInlineItemCount + m_floats.size();
+        partialLeadingContentLength = { };
+    }
+    // Looks like we've run out of runs.
+    return { committedInlineItemCount, { } };
+}
+
+LineBuilder::InlineItemRange LineBuilder::close(const InlineItemRange& needsLayoutRange, const CommittedContent& committedContent)
+{
+    ASSERT(committedContent.inlineItemCount || !m_floats.isEmpty() || m_contentIsConstrainedByFloat);
+    auto numberOfCommittedItems = committedContent.inlineItemCount + m_floats.size();
+    auto trailingInlineItemIndex = needsLayoutRange.start + numberOfCommittedItems - 1;
+    auto lineRange = InlineItemRange { needsLayoutRange.start, trailingInlineItemIndex + 1 };
+    ASSERT(lineRange.end <= needsLayoutRange.end);
+    if (!committedContent.inlineItemCount) {
+        // Line is empty, we only managed to place float boxes.
+        return lineRange;
+    }
+    auto availableWidth = m_lineLogicalRect.width() - m_line.contentLogicalRight();
+    m_line.removeCollapsibleContent(availableWidth);
+    auto horizontalAlignment = root().style().textAlign();
+    auto runsExpandHorizontally = horizontalAlignment == TextAlignMode::Justify && !isLastLineWithInlineContent(lineRange, needsLayoutRange.end, committedContent.partialTrailingContentLength);
+    if (runsExpandHorizontally)
+        m_line.applyRunExpansion(m_lineLogicalRect.width() - m_line.contentLogicalRight());
+    auto lineEndsWithHyphen = false;
+    if (!m_line.runs().isEmpty()) {
+        auto& lastTextContent = m_line.runs().last().textContent();
+        lineEndsWithHyphen = lastTextContent && lastTextContent->needsHyphen();
+    }
+    m_successiveHyphenatedLineCount = lineEndsWithHyphen ? m_successiveHyphenatedLineCount + 1 : 0;
+    return lineRange;
+}
+
+Optional<HorizontalConstraints> LineBuilder::floatConstraints(const InlineRect& lineLogicalRect) const
+{
+    auto* floatingState = this->floatingState();
+    if (!floatingState || floatingState->floats().isEmpty())
+        return { };
+
+    // Check for intruding floats and adjust logical left/available width for this line accordingly.
+    auto floatingContext = FloatingContext { formattingContext(), *floatingState };
+    auto constraints = floatingContext.constraints(toLayoutUnit(lineLogicalRect.top()), toLayoutUnit(lineLogicalRect.bottom()));
+    // Check if these values actually constrain the line.
+    if (constraints.left && constraints.left->x <= lineLogicalRect.left())
+        constraints.left = { };
+
+    if (constraints.right && constraints.right->x >= lineLogicalRect.right())
+        constraints.right = { };
+
+    if (!constraints.left && !constraints.right)
+        return { };
+
+    auto lineLogicalLeft = lineLogicalRect.left();
+    auto lineLogicalRight = lineLogicalRect.right();
+    if (constraints.left && constraints.right) {
+        lineLogicalRight = constraints.right->x;
+        lineLogicalLeft = constraints.left->x;
+    } else if (constraints.left) {
+        ASSERT(constraints.left->x >= lineLogicalLeft);
+        lineLogicalLeft = constraints.left->x;
+    } else if (constraints.right) {
+        // Right float boxes may overflow the containing block on the left.
+        lineLogicalRight = std::max<InlineLayoutUnit>(lineLogicalLeft, constraints.right->x);
+    }
+    return HorizontalConstraints { toLayoutUnit(lineLogicalLeft), toLayoutUnit(lineLogicalRight - lineLogicalLeft) };
+}
+
+LineBuilder::UsedConstraints LineBuilder::initialConstraintsForLine(const InlineRect& initialLineLogicalRect, bool isFirstLine) const
+{
+    auto lineLogicalLeft = initialLineLogicalRect.left();
+    auto lineLogicalRight = initialLineLogicalRect.right();
+    auto lineIsConstrainedByFloat = false;
+
+    if (auto lineConstraints = floatConstraints(initialLineLogicalRect)) {
+        lineLogicalLeft = lineConstraints->logicalLeft;
+        lineLogicalRight = lineConstraints->logicalRight();
+        lineIsConstrainedByFloat = true;
+    }
+
+    auto computedTextIndent = [&]() -> InlineLayoutUnit {
+        // text-indent property specifies the indentation applied to lines of inline content in a block.
+        // The indent is treated as a margin applied to the start edge of the line box.
+        // Unless otherwise specified, only lines that are the first formatted line of an element are affected.
+        // For example, the first line of an anonymous block box is only affected if it is the first child of its parent element.
+        // FIXME: Add support for each-line.
+        // [Integration] root()->parent() would normally produce a valid layout box.
+        auto& root = this->root();
+        auto isFormattingContextRootCandidateToTextIndent = !root.isAnonymous();
+        if (root.isAnonymous()) {
+            // Unless otherwise specified by the each-line and/or hanging keywords, only lines that are the first formatted line
+            // of an element are affected.
+            // For example, the first line of an anonymous block box is only affected if it is the first child of its parent element.
+            auto isIntegratedRootBoxFirstChild = layoutState().isIntegratedRootBoxFirstChild();
+            if (isIntegratedRootBoxFirstChild == LayoutState::IsIntegratedRootBoxFirstChild::NotApplicable)
+                isFormattingContextRootCandidateToTextIndent = root.parent().firstInFlowChild() == &root;
+            else
+                isFormattingContextRootCandidateToTextIndent = isIntegratedRootBoxFirstChild == LayoutState::IsIntegratedRootBoxFirstChild::Yes;
+        }
+        if (!isFormattingContextRootCandidateToTextIndent)
+            return { };
+        auto invertLineRange = root.style().textIndentType() == TextIndentType::Hanging;
+        // text-indent: hanging inverts which lines are affected.
+        // inverted line range -> all the lines except the first one.
+        // !inverted line range -> first line gets the indent.
+        auto shouldIndent = invertLineRange != isFirstLine;
+        if (!shouldIndent)
+            return { };
+
+        auto textIndent = root.style().textIndent();
+        if (textIndent == RenderStyle::initialTextIndent())
+            return { };
+        return { minimumValueForLength(textIndent, initialLineLogicalRect.width()) };
+    };
+    lineLogicalLeft += computedTextIndent();
+    return UsedConstraints { { initialLineLogicalRect.top(), lineLogicalLeft, lineLogicalRight - lineLogicalLeft, initialLineLogicalRect.height() }, lineIsConstrainedByFloat };
+}
+
+void LineBuilder::candidateContentForLine(LineCandidate& lineCandidate, size_t currentInlineItemIndex, const InlineItemRange& layoutRange, size_t partialLeadingContentLength, Optional<InlineLayoutUnit> leadingLogicalWidth, InlineLayoutUnit currentLogicalRight)
+{
+    ASSERT(currentInlineItemIndex < layoutRange.end);
+    lineCandidate.reset();
+    // 1. Simply add any overflow content from the previous line to the candidate content. It's always a text content.
+    // 2. Find the next soft wrap position or explicit line break.
+    // 3. Collect floats between the inline content.
+    auto softWrapOpportunityIndex = nextWrapOpportunity(currentInlineItemIndex, layoutRange);
+    // softWrapOpportunityIndex == layoutRange.end means we don't have any wrap opportunity in this content.
+    ASSERT(softWrapOpportunityIndex <= layoutRange.end);
+
+    if (partialLeadingContentLength) {
+        // Handle leading partial content first (overflowing text from the previous line).
+        // Construct a partial leading inline item.
+        m_partialLeadingTextItem = downcast<InlineTextItem>(m_inlineItems[currentInlineItemIndex]).right(partialLeadingContentLength);
+        auto itemWidth = leadingLogicalWidth ? *std::exchange(leadingLogicalWidth, WTF::nullopt) : inlineItemWidth(*m_partialLeadingTextItem, currentLogicalRight);
+        lineCandidate.inlineContent.appendInlineItem(*m_partialLeadingTextItem, itemWidth);
+        currentLogicalRight += itemWidth;
+        ++currentInlineItemIndex;
+    }
+
+    for (auto index = currentInlineItemIndex; index < softWrapOpportunityIndex; ++index) {
+        auto& inlineItem = m_inlineItems[index];
+        if (inlineItem.isFloat()) {
+            lineCandidate.floatItem = &inlineItem;
+            // This is a soft wrap opportunity, must be the only item in the list.
+            ASSERT(currentInlineItemIndex + 1 == softWrapOpportunityIndex);
+            continue;
+        }
+        if (inlineItem.isText() || inlineItem.isInlineBoxStart() || inlineItem.isInlineBoxEnd() || inlineItem.isBox()) {
+            ASSERT(!leadingLogicalWidth || inlineItem.isText());
+            auto logicalWidth = leadingLogicalWidth ? *std::exchange(leadingLogicalWidth, WTF::nullopt) : inlineItemWidth(inlineItem, currentLogicalRight);
+            lineCandidate.inlineContent.appendInlineItem(inlineItem, logicalWidth);
+            currentLogicalRight += logicalWidth;
+            continue;
+        }
+        if (inlineItem.isLineBreak() || inlineItem.isWordBreakOpportunity()) {
+            // Since both <br> and <wbr> are explicit word break opportunities they have to be trailing items in this candidate run list unless they are embedded in inline boxes.
+            // e.g. <span><wbr></span>
+#if ASSERT_ENABLED
+            for (auto i = index + 1; i < softWrapOpportunityIndex; ++i)
+                ASSERT(m_inlineItems[i].isInlineBoxEnd());
+#endif
+            inlineItem.isLineBreak() ? lineCandidate.inlineContent.appendTrailingLineBreak(inlineItem) : lineCandidate.inlineContent.appendtrailingWordBreakOpportunity(inlineItem);
+            continue;
+        }
+        ASSERT_NOT_REACHED();
+    }
+    auto inlineContentEndsInSoftWrapOpportunity = [&] {
+        if (!softWrapOpportunityIndex || softWrapOpportunityIndex == layoutRange.end) {
+            // This candidate inline content ends because the entire content ends and not because there's a soft wrap opportunity.
+            return false;
+        }
+        if (m_inlineItems[softWrapOpportunityIndex - 1].isFloat()) {
+            // While we stop at floats, they are not considered real soft wrap opportunities. 
+            return false;
+        }
+        // See https://www.w3.org/TR/css-text-3/#line-break-details
+        auto& trailingInlineItem = m_inlineItems[softWrapOpportunityIndex - 1];
+        if (trailingInlineItem.isBox() || trailingInlineItem.isLineBreak() || trailingInlineItem.isWordBreakOpportunity() || trailingInlineItem.isInlineBoxEnd()) {
+            // For Web-compatibility there is a soft wrap opportunity before and after each replaced element or other atomic inline.
+            return true;
+        }
+        if (trailingInlineItem.isText()) {
+            auto& inlineTextItem = downcast<InlineTextItem>(trailingInlineItem);
+            if (inlineTextItem.isWhitespace())
+                return true;
+            // Now in case of non-whitespace trailing content, we need to check if the actual soft wrap opportunity belongs to the next set.
+            // e.g. "this_is_the_trailing_run<span> <-but_this_space_here_is_the_soft_wrap_opportunity"
+            // When there's an inline box start(<span>)/end(</span>) between the trailing and the (next)leading run, while we break before the inline box start (<span>)
+            // the actual soft wrap position is after the inline box start (<span>) but in terms of line breaking continuity the inline box start (<span>) and the whitespace run belong together.
+            RELEASE_ASSERT(layoutRange.end <= m_inlineItems.size());
+            for (auto index = softWrapOpportunityIndex; index < layoutRange.end; ++index) {
+                if (m_inlineItems[index].isInlineBoxStart() || m_inlineItems[index].isInlineBoxEnd())
+                    continue;
+                // FIXME: Check if [non-whitespace][inline-box][no-whitespace] content has rules about it.
+                // For now let's say the soft wrap position belongs to the next set of runs when [non-whitespace][inline-box][whitespace], [non-whitespace][inline-box][box] etc.
+                return m_inlineItems[index].isText() && !downcast<InlineTextItem>(m_inlineItems[index]).isWhitespace();
+            }
+            return true;
+        }
+        ASSERT_NOT_REACHED();
+        return true;
+    };
+    lineCandidate.inlineContent.setHasTrailingSoftWrapOpportunity(inlineContentEndsInSoftWrapOpportunity());
+}
+
+size_t LineBuilder::nextWrapOpportunity(size_t startIndex, const LineBuilder::InlineItemRange& layoutRange) const
+{
+    // 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/explicit wrap content inbetween is considered as wrap opportunity.
+
+    // [ex-][inline box start][inline box end][float][ample] (ex-<span></span><div style="float:left"></div>ample). Wrap index is at [ex-].
+    // [ex][inline box start][amp-][inline box start][le] (ex<span>amp-<span>ample). Wrap index is at [amp-].
+    // [ex-][inline box 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 = m_inlineItems[index];
+        if (inlineItem.isLineBreak() || inlineItem.isWordBreakOpportunity()) {
+            // We always stop at explicit wrapping opportunities e.g. <br>. However the wrap position may be at later position.
+            // e.g. <span><span><br></span></span> <- wrap position is after the second </span>
+            // but in case of <span><br><span></span></span> <- wrap position is right after <br>.
+            for (++index; index < layoutRange.end && m_inlineItems[index].isInlineBoxEnd(); ++index) { }
+            return index;
+        }
+        if (inlineItem.isInlineBoxStart() || inlineItem.isInlineBoxEnd()) {
+            // Need to see what comes next to decide.
+            continue;
+        }
+        ASSERT(inlineItem.isText() || inlineItem.isBox() || inlineItem.isFloat());
+        if (!previousInlineItemIndex) {
+            previousInlineItemIndex = index;
+            continue;
+        }
+        // At this point previous and current items are not necessarily adjacent items e.g "previous<span>current</span>"
+        auto& previousItem = m_inlineItems[*previousInlineItemIndex];
+        auto& currentItem = m_inlineItems[index];
+        if (isAtSoftWrapOpportunity(m_inlineFormattingContext, previousItem, currentItem)) {
+            if (*previousInlineItemIndex + 1 == index && (!previousItem.isText() || !currentItem.isText())) {
+                // We only know the exact soft wrap opportunity index when the previous and current items are next to each other.
+                return 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-][inline box start][inline box end][ample]
+            // where [ex-] is startContent and [ample] is the nextContent.
+            for (auto candidateIndex = *previousInlineItemIndex + 1; candidateIndex < index; ++candidateIndex) {
+                if (m_inlineItems[candidateIndex].isInlineBoxStart()) {
+                    // inline content and [inline box start] and [inline box end] form unbreakable content.
+                    // ex-<span></span>ample  : wrap opportunity is after "ex-".
+                    // ex-</span></span>ample : wrap opportunity is after "ex-</span></span>".
+                    // ex-</span><span>ample</span> : wrap opportunity is after "ex-</span>".
+                    // ex-<span><span>ample</span></span> : wrap opportunity is after "ex-".
+                    return candidateIndex;
+                }
+            }
+            return index;
+        }
+        previousInlineItemIndex = index;
+    }
+    return layoutRange.end;
+}
+
+void LineBuilder::handleFloatContent(const InlineItem& floatItem)
+{
+    auto& floatBox = floatItem.layoutBox();
+    m_floats.append(&floatBox);
+
+    auto* floatingState = this->floatingState();
+    if (!floatingState)
+        return;
+
+    ASSERT(formattingState());
+    auto& boxGeometry = formattingState()->boxGeometry(floatBox);
+    // Set static position first.
+    boxGeometry.setLogicalTopLeft(LayoutPoint { m_lineLogicalRect.topLeft() });
+    // Float it.
+    ASSERT(m_rootHorizontalConstraints);
+    auto floatingContext = FloatingContext { formattingContext(), *floatingState };
+    auto floatingPosition = floatingContext.positionForFloat(floatBox, *m_rootHorizontalConstraints);
+    boxGeometry.setLogicalTopLeft(floatingPosition);
+    floatingState->append(floatingContext.toFloatItem(floatBox));
+    // Check if this float shrinks the line (they don't get positioned higher than the line).
+    if (floatingPosition.y() > m_lineLogicalRect.bottom())
+        return;
+
+    m_contentIsConstrainedByFloat = true;
+    auto floatBoxWidth = inlineItemWidth(floatItem, { });
+    if (floatBox.isLeftFloatingPositioned())
+        m_lineLogicalRect.setLeft(m_lineLogicalRect.left() + floatBoxWidth);    
+    m_lineLogicalRect.expandHorizontally(-floatBoxWidth);
+}
+
+LineBuilder::Result LineBuilder::handleInlineContent(InlineContentBreaker& inlineContentBreaker, const InlineItemRange& layoutRange, const LineCandidate& lineCandidate)
+{
+    auto& inlineContent = lineCandidate.inlineContent;
+    if (inlineContent.continuousContent().runs().isEmpty()) {
+        ASSERT(inlineContent.trailingLineBreak() || inlineContent.trailingWordBreakOpportunity());
+        return { inlineContent.trailingLineBreak() ? InlineContentBreaker::IsEndOfLine::Yes : InlineContentBreaker::IsEndOfLine::No };
+    }
+    auto shouldDisableHyphenation = [&] {
+        auto& style = root().style();
+        unsigned limitLines = style.hyphenationLimitLines() == RenderStyle::initialHyphenationLimitLines() ? std::numeric_limits<unsigned>::max() : style.hyphenationLimitLines();
+        return m_successiveHyphenatedLineCount >= limitLines;
+    };
+    if (shouldDisableHyphenation())
+        inlineContentBreaker.setHyphenationDisabled();
+
+    auto& continuousInlineContent = lineCandidate.inlineContent.continuousContent();
+    auto lineLogicalRectForCandidateContent = [&] {
+        // Check if the candidate content would stretch the line and whether additional floats are getting in the way.
+        if (!floatingState() || !inlineContent.hasInlineLevelBox())
+            return m_lineLogicalRect;
+        auto maximumLineLogicalHeight = m_lineLogicalRect.height();
+        for (auto& run : continuousInlineContent.runs()) {
+            // FIXME: Add support for inline boxes too.
+            if (!run.inlineItem.isBox())
+                continue;
+            maximumLineLogicalHeight = std::max(maximumLineLogicalHeight, InlineLayoutUnit { formattingContext().geometryForBox(run.inlineItem.layoutBox()).marginBoxHeight() });
+        }
+        if (maximumLineLogicalHeight == m_lineLogicalRect.height())
+            return m_lineLogicalRect;
+        auto adjustedLineLogicalRect = InlineRect { m_lineLogicalRect.top(), m_lineLogicalRect.left(), m_lineLogicalRect.width(), maximumLineLogicalHeight };
+        if (auto horizontalConstraints = floatConstraints(adjustedLineLogicalRect)) {
+            adjustedLineLogicalRect.setLeft(horizontalConstraints->logicalLeft);
+            adjustedLineLogicalRect.setWidth(horizontalConstraints->logicalWidth);
+        }
+        return adjustedLineLogicalRect;
+    }();
+    auto availableWidth = [&] {
+        auto availableWidthForContent = lineLogicalRectForCandidateContent.width() - m_line.contentLogicalRight();
+        return std::isnan(availableWidthForContent) ? maxInlineLayoutUnit() : availableWidthForContent;
+    }();
+    // While the floats are not considered to be on the line, they make the line contentful for line breaking.
+    auto lineHasContent = !m_line.runs().isEmpty() || m_contentIsConstrainedByFloat;
+    auto lineStatus = InlineContentBreaker::LineStatus { m_line.contentLogicalRight(), availableWidth, m_line.trimmableTrailingWidth(), m_line.trailingSoftHyphenWidth(), m_line.isTrailingRunFullyTrimmable(), lineHasContent, !m_wrapOpportunityList.isEmpty() };
+    auto result = inlineContentBreaker.processInlineContent(continuousInlineContent, lineStatus);
+    auto& candidateRuns = continuousInlineContent.runs();
+    if (result.action == InlineContentBreaker::Result::Action::Keep) {
+        // This continuous content can be fully placed on the current line.
+        m_lineLogicalRect = lineLogicalRectForCandidateContent;
+        for (auto& run : candidateRuns)
+            m_line.append(run.inlineItem, run.logicalWidth);
+        if (lineCandidate.inlineContent.hasTrailingSoftWrapOpportunity()) {
+            // Check if we are allowed to wrap at this position.
+            auto& trailingItem = candidateRuns.last().inlineItem;
+            // FIXME: There must be a way to decide if the trailing run actually ended up on the line.
+            // Let's just deal with collapsed leading whitespace for now.
+            if (!m_line.runs().isEmpty() && InlineContentBreaker::isWrappingAllowed(trailingItem))
+                m_wrapOpportunityList.append(&trailingItem);
+        }
+        return { result.isEndOfLine, { candidateRuns.size(), false } };
+    }
+
+    auto eligibleOverflowWidthAsLeading = [&] () -> Optional<InlineLayoutUnit> {
+        // FIXME: Add support for other types of continuous content.
+        ASSERT(result.action == InlineContentBreaker::Result::Action::Wrap || result.action == InlineContentBreaker::Result::Action::Break);
+        if (candidateRuns.size() != 1 || !candidateRuns.first().inlineItem.isText())
+            return { };
+        auto& inlineTextItem = downcast<InlineTextItem>(candidateRuns.first().inlineItem);
+        if (inlineTextItem.isWhitespace())
+            return { };
+        if (result.action == InlineContentBreaker::Result::Action::Wrap)
+            return candidateRuns.first().logicalWidth;
+        if (result.action == InlineContentBreaker::Result::Action::Break && result.partialTrailingContent->partialRun)
+            return candidateRuns.first().logicalWidth - result.partialTrailingContent->partialRun->logicalWidth;
+        return { };
+    };
+
+    if (result.action == InlineContentBreaker::Result::Action::Wrap) {
+        ASSERT(result.isEndOfLine == InlineContentBreaker::IsEndOfLine::Yes);
+        // This continuous content can't be placed on the current line. Nothing to commit at this time.
+        return { InlineContentBreaker::IsEndOfLine::Yes, { }, { }, eligibleOverflowWidthAsLeading() };
+    }
+    if (result.action == InlineContentBreaker::Result::Action::WrapWithHyphen) {
+        ASSERT(result.isEndOfLine == InlineContentBreaker::IsEndOfLine::Yes);
+        // This continuous content can't be placed on the current line, nothing to commit.
+        // However we need to make sure that the current line gains a trailing hyphen.
+        ASSERT(m_line.trailingSoftHyphenWidth());
+        m_line.addTrailingHyphen(*m_line.trailingSoftHyphenWidth());
+        return { InlineContentBreaker::IsEndOfLine::Yes };
+    }
+    if (result.action == InlineContentBreaker::Result::Action::RevertToLastWrapOpportunity) {
+        ASSERT(result.isEndOfLine == InlineContentBreaker::IsEndOfLine::Yes);
+        // Not only this content can't be placed on the current line, but we even need to revert the line back to an earlier position.
+        ASSERT(!m_wrapOpportunityList.isEmpty());
+        return { InlineContentBreaker::IsEndOfLine::Yes, { rebuildLine(layoutRange, *m_wrapOpportunityList.last()), true } };
+    }
+    if (result.action == InlineContentBreaker::Result::Action::RevertToLastNonOverflowingWrapOpportunity) {
+        ASSERT(result.isEndOfLine == InlineContentBreaker::IsEndOfLine::Yes);
+        ASSERT(!m_wrapOpportunityList.isEmpty());
+        return { InlineContentBreaker::IsEndOfLine::Yes, { rebuildLineForTrailingSoftHyphen(layoutRange), true } };
+    }
+    if (result.action == InlineContentBreaker::Result::Action::Break) {
+        ASSERT(result.isEndOfLine == InlineContentBreaker::IsEndOfLine::Yes);
+        // Commit the combination of full and partial content on the current line.
+        ASSERT(result.partialTrailingContent);
+        commitPartialContent(candidateRuns, *result.partialTrailingContent);
+        // When breaking multiple runs <span style="word-break: break-all">text</span><span>content</span>, we might end up breaking them at run boundary.
+        // It simply means we don't really have a partial run. Partial content yes, but not partial run.
+        auto trailingRunIndex = result.partialTrailingContent->trailingRunIndex;
+        auto committedInlineItemCount = trailingRunIndex + 1;
+        if (!result.partialTrailingContent->partialRun)
+            return { InlineContentBreaker::IsEndOfLine::Yes, { committedInlineItemCount, false } };
+
+        auto partialRun = *result.partialTrailingContent->partialRun;
+        auto& trailingInlineTextItem = downcast<InlineTextItem>(candidateRuns[trailingRunIndex].inlineItem);
+        ASSERT(partialRun.length < trailingInlineTextItem.length());
+        auto overflowLength = trailingInlineTextItem.length() - partialRun.length;
+        return { InlineContentBreaker::IsEndOfLine::Yes, { committedInlineItemCount, false }, overflowLength, eligibleOverflowWidthAsLeading() };
+    }
+    ASSERT_NOT_REACHED();
+    return { InlineContentBreaker::IsEndOfLine::No };
+}
+
+void LineBuilder::commitPartialContent(const InlineContentBreaker::ContinuousContent::RunList& runs, const InlineContentBreaker::Result::PartialTrailingContent& partialTrailingContent)
+{
+    for (size_t index = 0; index < runs.size(); ++index) {
+        auto& run = runs[index];
+        if (partialTrailingContent.trailingRunIndex == index) {
+            // Create and commit partial trailing item.
+            if (auto partialRun = partialTrailingContent.partialRun) {
+                ASSERT(run.inlineItem.isText());
+                auto& trailingInlineTextItem = downcast<InlineTextItem>(runs[partialTrailingContent.trailingRunIndex].inlineItem);
+                auto partialTrailingTextItem = trailingInlineTextItem.left(partialRun->length);
+                m_line.append(partialTrailingTextItem, partialRun->logicalWidth);
+                if (auto hyphenWidth = partialRun->hyphenWidth)
+                    m_line.addTrailingHyphen(*hyphenWidth);
+                return;
+            }
+            // The partial run is the last content to commit.
+            m_line.append(run.inlineItem, run.logicalWidth);
+            return;
+        }
+        m_line.append(run.inlineItem, run.logicalWidth);
+    }
+}
+
+size_t LineBuilder::rebuildLine(const InlineItemRange& layoutRange, const InlineItem& lastInlineItemToAdd)
+{
+    ASSERT(!m_wrapOpportunityList.isEmpty());
+    // We might already have added floats. They shrink the available horizontal space for the line.
+    // Let's just reuse what the line has at this point.
+    m_line.initialize();
+    auto currentItemIndex = layoutRange.start;
+    if (m_partialLeadingTextItem) {
+        m_line.append(*m_partialLeadingTextItem, inlineItemWidth(*m_partialLeadingTextItem, { }));
+        if (&m_partialLeadingTextItem.value() == &lastInlineItemToAdd)
+            return 1;
+        ++currentItemIndex;
+    }
+    for (; currentItemIndex < layoutRange.end; ++currentItemIndex) {
+        auto& inlineItem = m_inlineItems[currentItemIndex];
+        m_line.append(inlineItem, inlineItemWidth(inlineItem, m_line.contentLogicalRight()));
+        if (&inlineItem == &lastInlineItemToAdd)
+            return currentItemIndex - layoutRange.start + 1;
+    }
+    return layoutRange.size();
+}
+
+size_t LineBuilder::rebuildLineForTrailingSoftHyphen(const InlineItemRange& layoutRange)
+{
+    ASSERT(!m_wrapOpportunityList.isEmpty());
+    // Revert all the way back to a wrap opportunity when either a soft hyphen fits or no hyphen is required.
+    for (auto i = m_wrapOpportunityList.size(); i-- > 1;) {
+        auto& softWrapOpportunityItem = *m_wrapOpportunityList[i];
+        // FIXME: If this turns out to be a perf issue, we could also traverse the wrap list and keep adding the items
+        // while watching the available width very closely.
+        auto committedCount = rebuildLine(layoutRange, softWrapOpportunityItem);
+        auto availableWidth = m_lineLogicalRect.width() - m_line.contentLogicalRight();
+        auto trailingSoftHyphenWidth = m_line.trailingSoftHyphenWidth();
+        // Check if the trailing hyphen now fits the line (or we don't need hyhen anymore).
+        if (!trailingSoftHyphenWidth || trailingSoftHyphenWidth <= availableWidth) {
+            if (trailingSoftHyphenWidth)
+                m_line.addTrailingHyphen(*trailingSoftHyphenWidth);
+            return committedCount;
+        }
+    }
+    // Have at least some content on the line.
+    auto committedCount = rebuildLine(layoutRange, *m_wrapOpportunityList.first());
+    if (auto trailingSoftHyphenWidth = m_line.trailingSoftHyphenWidth())
+        m_line.addTrailingHyphen(*trailingSoftHyphenWidth);
+    return committedCount;
+}
+
+bool LineBuilder::isLastLineWithInlineContent(const InlineItemRange& lineRange, size_t lastInlineItemIndex, bool hasPartialTrailingContent) const
+{
+    if (hasPartialTrailingContent)
+        return false;
+    if (lineRange.end == lastInlineItemIndex)
+        return true;
+    // Omit floats to see if this is the last line with inline content.
+    for (auto i = lastInlineItemIndex; i--;) {
+        if (!m_inlineItems[i].isFloat())
+            return i == lineRange.end - 1;
+    }
+    // There has to be at least one non-float item.
+    ASSERT_NOT_REACHED();
+    return false;
+}
+
+const ContainerBox& LineBuilder::root() const
+{
+    return formattingContext().root();
+}
+
+const LayoutState& LineBuilder::layoutState() const
+{
+    return formattingContext().layoutState();
+}
+
+}
+}
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineLineBuilderhfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingInlineLineBuilderh"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBuilder.h (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/InlineLineBuilder.h) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBuilder.h                                (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBuilder.h   2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,136 @@
</span><ins>+/*
+ * 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 "InlineContentBreaker.h"
+#include "InlineFormattingState.h"
+#include "InlineLine.h"
+
+namespace WebCore {
+namespace Layout {
+
+class FloatingContext;
+struct LineCandidate;
+
+class LineBuilder {
+public:
+    LineBuilder(InlineFormattingContext&, FloatingState&, HorizontalConstraints rootHorizontalConstraints, const InlineItems&);
+    LineBuilder(const InlineFormattingContext&, const InlineItems&);
+
+    struct InlineItemRange {
+        bool isEmpty() const { return start == end; }
+        size_t size() const { return end - start; }
+        size_t start { 0 };
+        size_t end { 0 };
+    };
+    using FloatList = Vector<const Box*>;
+    struct LineContent {
+        InlineItemRange inlineItemRange;
+        size_t partialTrailingContentLength { 0 };
+        Optional<InlineLayoutUnit> overflowLogicalWidth;
+        const FloatList& floats;
+        bool hasIntrusiveFloat { false };
+        InlineLayoutPoint logicalTopLeft;
+        InlineLayoutUnit lineLogicalWidth;
+        InlineLayoutUnit contentLogicalWidth;
+        bool isLastLineWithInlineContent { true };
+        size_t nonSpanningInlineLevelBoxCount { 0 };
+        const Line::RunList& runs;
+    };
+    LineContent layoutInlineContent(const InlineItemRange&, size_t partialLeadingContentLength, Optional<InlineLayoutUnit> leadingLogicalWidth, const InlineRect& initialLineLogicalRect, bool isFirstLine);
+
+    struct IntrinsicContent {
+        InlineItemRange inlineItemRange;
+        InlineLayoutUnit logicalWidth { 0 };
+        const FloatList& floats;
+    };
+    IntrinsicContent computedIntrinsicWidth(const InlineItemRange&, InlineLayoutUnit availableWidth);
+
+private:
+    void candidateContentForLine(LineCandidate&, size_t inlineItemIndex, const InlineItemRange& needsLayoutRange, size_t overflowLength, Optional<InlineLayoutUnit> leadingLogicalWidth, InlineLayoutUnit currentLogicalRight);
+    size_t nextWrapOpportunity(size_t startIndex, const LineBuilder::InlineItemRange& layoutRange) const;
+
+    struct Result {
+        InlineContentBreaker::IsEndOfLine isEndOfLine { InlineContentBreaker::IsEndOfLine::No };
+        struct CommittedContentCount {
+            size_t value { 0 };
+            bool isRevert { false };
+        };
+        CommittedContentCount committedCount { };
+        size_t partialTrailingContentLength { 0 };
+        Optional<InlineLayoutUnit> overflowLogicalWidth { };
+    };
+    struct UsedConstraints {
+        InlineRect logicalRect;
+        bool isConstrainedByFloat { false };
+    };
+    UsedConstraints initialConstraintsForLine(const InlineRect& initialLineLogicalRect, bool isFirstLine) const;
+    Optional<HorizontalConstraints> floatConstraints(const InlineRect& lineLogicalRect) const;
+
+    void handleFloatContent(const InlineItem&);
+    Result handleInlineContent(InlineContentBreaker&, const InlineItemRange& needsLayoutRange, const LineCandidate&);
+    size_t rebuildLine(const InlineItemRange& needsLayoutRange, const InlineItem& lastInlineItemToAdd);
+    size_t rebuildLineForTrailingSoftHyphen(const InlineItemRange& layoutRange);
+    void commitPartialContent(const InlineContentBreaker::ContinuousContent::RunList&, const InlineContentBreaker::Result::PartialTrailingContent&);
+    void initialize(const UsedConstraints&);
+    struct CommittedContent {
+        size_t inlineItemCount { 0 };
+        size_t partialTrailingContentLength { 0 };
+        Optional<InlineLayoutUnit> overflowLogicalWidth { };
+    };
+    CommittedContent placeInlineContent(const InlineItemRange&, size_t partialLeadingContentLength, Optional<InlineLayoutUnit> overflowLogicalWidth);
+    InlineItemRange close(const InlineItemRange& needsLayoutRange, const CommittedContent&);
+
+    InlineLayoutUnit inlineItemWidth(const InlineItem&, InlineLayoutUnit contentLogicalLeft) const;
+    bool isLastLineWithInlineContent(const InlineItemRange& lineRange, size_t lastInlineItemIndex, bool hasPartialTrailingContent) const;
+
+    const InlineFormattingContext& formattingContext() const { return m_inlineFormattingContext; }
+    InlineFormattingState* formattingState() { return m_inlineFormattingState; }
+    FloatingState* floatingState() { return m_floatingState; }
+    const FloatingState* floatingState() const { return m_floatingState; }
+    const ContainerBox& root() const;
+    const LayoutState& layoutState() const;
+
+    const InlineFormattingContext& m_inlineFormattingContext;
+    InlineFormattingState* m_inlineFormattingState { nullptr };
+    FloatingState* m_floatingState { nullptr };
+    Optional<HorizontalConstraints> m_rootHorizontalConstraints;
+
+    Line m_line;
+    InlineRect m_lineLogicalRect;
+    const InlineItems& m_inlineItems;
+    FloatList m_floats;
+    Optional<InlineTextItem> m_partialLeadingTextItem;
+    Vector<const InlineItem*> m_wrapOpportunityList;
+    unsigned m_successiveHyphenatedLineCount { 0 };
+    bool m_contentIsConstrainedByFloat { false };
+};
+
+}
+}
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineLineGeometryhfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingInlineLineGeometryh"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineGeometry.h (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/InlineLineGeometry.h) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineGeometry.h                               (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineGeometry.h  2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,80 @@
</span><ins>+/*
+ * Copyright (C) 2020 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 "InlineRect.h"
+
+namespace WebCore {
+namespace Layout {
+
+class InlineLineGeometry {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    struct EnclosingTopAndBottom {
+        // This values encloses the root inline box and any other inline level box's border box.
+        InlineLayoutUnit top { 0 };
+        InlineLayoutUnit bottom { 0 };
+    };
+    InlineLineGeometry(const InlineRect& lineBoxLogicalRect, EnclosingTopAndBottom, InlineLayoutUnit aligmentBaseline, InlineLayoutUnit contentLogicalLeft, InlineLayoutUnit contentLogicalWidth);
+
+    const InlineRect& lineBoxLogicalRect() const { return m_lineBoxLogicalRect; }
+
+    EnclosingTopAndBottom enclosingTopAndBottom() const { return m_enclosingTopAndBottom; }
+
+    InlineLayoutUnit baseline() const { return m_aligmentBaseline; }
+
+    InlineLayoutUnit contentLogicalLeft() const { return m_contentLogicalLeft; }
+    InlineLayoutUnit contentLogicalWidth() const { return m_contentLogicalWidth; }
+
+    void moveVertically(InlineLayoutUnit offset) { m_lineBoxLogicalRect.moveVertically(offset); }
+
+private:
+    // This is line box geometry (see https://www.w3.org/TR/css-inline-3/#line-box).
+    InlineRect m_lineBoxLogicalRect;
+    // Enclosing top and bottom includes all inline level boxes (border box) vertically.
+    // While the line box usually enclose them as well, its vertical geometry is based on
+    // the layout bounds of the inline level boxes which may be different when line-height is present.
+    EnclosingTopAndBottom m_enclosingTopAndBottom;
+    InlineLayoutUnit m_aligmentBaseline { 0 };
+    InlineLayoutUnit m_contentLogicalLeft { 0 };
+    InlineLayoutUnit m_contentLogicalWidth { 0 };
+};
+
+inline InlineLineGeometry::InlineLineGeometry(const InlineRect& lineBoxLogicalRect, EnclosingTopAndBottom enclosingTopAndBottom, InlineLayoutUnit aligmentBaseline, InlineLayoutUnit contentLogicalLeft, InlineLayoutUnit contentLogicalWidth)
+    : m_lineBoxLogicalRect(lineBoxLogicalRect)
+    , m_enclosingTopAndBottom(enclosingTopAndBottom)
+    , m_aligmentBaseline(aligmentBaseline)
+    , m_contentLogicalLeft(contentLogicalLeft)
+    , m_contentLogicalWidth(contentLogicalWidth)
+{
+}
+
+}
+}
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineLineRunhfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingInlineLineRunh"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineRun.h (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/InlineLineRun.h) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineRun.h                            (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineRun.h       2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,114 @@
</span><ins>+/*
+ * Copyright (C) 2020 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 "InlineRect.h"
+#include "LayoutBox.h"
+#include "TextFlags.h"
+
+namespace WebCore {
+namespace Layout {
+
+struct LineRun {
+    WTF_MAKE_STRUCT_FAST_ALLOCATED;
+    struct Text {
+        WTF_MAKE_STRUCT_FAST_ALLOCATED;
+    public:
+        Text(size_t position, size_t length, const String&);
+
+        size_t start() const { return m_start; }
+        size_t end() const { return start() + length(); }
+        size_t length() const { return m_length; }
+        String content() const { return m_contentString; }
+
+        bool needsHyphen() const { return m_needsHyphen; }
+        void setNeedsHyphen() { m_needsHyphen = true; }
+
+        void expand(size_t delta) { m_length += delta; }
+        void shrink(size_t delta) { m_length -= delta; }
+
+    private:
+        size_t m_start { 0 };
+        size_t m_length { 0 };
+        bool m_needsHyphen { false };
+        String m_contentString;
+    };
+
+    struct Expansion;
+    LineRun(size_t lineIndex, const Box&, const InlineRect&, Expansion, Optional<Text> = WTF::nullopt);
+
+    const InlineRect& logicalRect() const { return m_logicalRect; }
+
+    InlineLayoutUnit logicalTop() const { return logicalRect().top(); }
+    InlineLayoutUnit logicalBottom() const { return logicalRect().bottom(); }
+    InlineLayoutUnit logicalLeft() const { return logicalRect().left(); }
+    InlineLayoutUnit logicalRight() const { return logicalRect().right(); }
+
+    InlineLayoutUnit logicalWidth() const { return logicalRect().width(); }
+    InlineLayoutUnit logicalHeight() const { return logicalRect().height(); }
+
+    void moveVertically(InlineLayoutUnit offset) { m_logicalRect.moveVertically(offset); }
+    Optional<Text>& text() { return m_text; }
+    const Optional<Text>& text() const { return m_text; }
+
+    struct Expansion {
+        ExpansionBehavior behavior { DefaultExpansion };
+        InlineLayoutUnit horizontalExpansion { 0 };
+    };
+    Expansion expansion() const { return m_expansion; }
+
+    const Box& layoutBox() const { return *m_layoutBox; }
+    size_t lineIndex() const { return m_lineIndex; }
+
+private:
+    const size_t m_lineIndex;
+    WeakPtr<const Layout::Box> m_layoutBox;
+    InlineRect m_logicalRect;
+    Expansion m_expansion;
+    Optional<Text> m_text;
+};
+
+inline LineRun::LineRun(size_t lineIndex, const Layout::Box& layoutBox, const InlineRect& logicalRect, Expansion expansion, Optional<Text> text)
+    : m_lineIndex(lineIndex)
+    , m_layoutBox(makeWeakPtr(layoutBox))
+    , m_logicalRect(logicalRect)
+    , m_expansion(expansion)
+    , m_text(text)
+{
+}
+
+inline LineRun::Text::Text(size_t start, size_t length, const String& contentString)
+    : m_start(start)
+    , m_length(length)
+    , m_contentString(contentString)
+{
+}
+
+}
+}
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineRecthfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingInlineRecth"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/InlineRect.h (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/InlineRect.h) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineRect.h                               (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineRect.h  2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,293 @@
</span><ins>+/*
+ * 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 "LayoutUnits.h"
+
+namespace WebCore {
+namespace Layout {
+
+class InlineRect {
+public:
+    InlineRect() = default;
+    InlineRect(InlineLayoutUnit top, InlineLayoutUnit left, InlineLayoutUnit width, InlineLayoutUnit height);
+    InlineRect(const InlineLayoutPoint& topLeft, InlineLayoutUnit width, InlineLayoutUnit height);
+    InlineRect(const InlineLayoutPoint& topLeft, const InlineLayoutSize&);
+    
+    InlineLayoutUnit top() const;
+    InlineLayoutUnit left() const;
+    InlineLayoutPoint topLeft() const;
+
+    InlineLayoutUnit bottom() const;
+    InlineLayoutUnit right() const;        
+
+    InlineLayoutUnit width() const;
+    InlineLayoutUnit height() const;
+    InlineLayoutSize size() const;
+
+    void setTop(InlineLayoutUnit);
+    void setBottom(InlineLayoutUnit);
+    void setLeft(InlineLayoutUnit);
+    void setTopLeft(const InlineLayoutPoint&);
+    void setWidth(InlineLayoutUnit);
+    void setHeight(InlineLayoutUnit);
+
+    void moveHorizontally(InlineLayoutUnit);
+    void moveVertically(InlineLayoutUnit);
+    void moveBy(InlineLayoutPoint);
+
+    void expand(Optional<InlineLayoutUnit>, Optional<InlineLayoutUnit>);
+    void expandToContain(const InlineRect&);
+    void expandHorizontally(InlineLayoutUnit delta) { expand(delta, { }); }
+    void expandVertically(InlineLayoutUnit delta) { expand({ }, delta); }
+    void expandVerticallyToContain(const InlineRect&);
+    void inflate(InlineLayoutUnit);
+
+    operator InlineLayoutRect() const;
+
+private:
+#if ASSERT_ENABLED
+    void invalidateTop() { m_hasValidTop = false; }
+    void invalidateLeft() { m_hasValidLeft = false; }
+    void invalidateWidth() { m_hasValidWidth = false; }
+    void invalidateHeight() { m_hasValidHeight = false; }
+    void invalidatePosition();
+
+    bool hasValidPosition() const { return m_hasValidTop && m_hasValidLeft; }
+    bool hasValidSize() const { return m_hasValidWidth && m_hasValidHeight; }
+    bool hasValidGeometry() const { return hasValidPosition() && hasValidSize(); }
+
+    void setHasValidPosition();
+    void setHasValidSize();
+
+    bool m_hasValidTop { false };
+    bool m_hasValidLeft { false };
+    bool m_hasValidWidth { false };
+    bool m_hasValidHeight { false };
+#endif // ASSERT_ENABLED
+    InlineLayoutRect m_rect;
+};
+
+inline InlineRect::InlineRect(InlineLayoutUnit top, InlineLayoutUnit left, InlineLayoutUnit width, InlineLayoutUnit height)
+    : m_rect(left, top, width, height)
+{
+#if ASSERT_ENABLED
+    m_hasValidTop = true;
+    m_hasValidLeft = true;
+    m_hasValidWidth = true;
+    m_hasValidHeight = true;
+#endif
+}
+
+inline InlineRect::InlineRect(const InlineLayoutPoint& topLeft, InlineLayoutUnit width, InlineLayoutUnit height)
+    : InlineRect(topLeft.y(), topLeft.x(), width, height)
+{
+}
+
+inline InlineRect::InlineRect(const InlineLayoutPoint& topLeft, const InlineLayoutSize& size)
+    : InlineRect(topLeft.y(), topLeft.x(), size.width(), size.height())
+{
+}
+
+#if ASSERT_ENABLED
+inline void InlineRect::invalidatePosition()
+{
+    invalidateTop();
+    invalidateLeft();
+}
+
+inline void InlineRect::setHasValidPosition()
+{
+    m_hasValidTop = true;
+    m_hasValidLeft = true;
+}
+
+inline void InlineRect::setHasValidSize()
+{
+    m_hasValidWidth = true;
+    m_hasValidHeight = true;
+}
+#endif // ASSERT_ENABLED
+
+inline InlineLayoutUnit InlineRect::top() const
+{
+    ASSERT(m_hasValidTop);
+    return m_rect.y();
+}
+
+inline InlineLayoutUnit InlineRect::left() const
+{
+    ASSERT(m_hasValidLeft);
+    return m_rect.x();
+}
+
+inline InlineLayoutUnit InlineRect::bottom() const
+{
+    ASSERT(m_hasValidTop && m_hasValidHeight);
+    return m_rect.maxY();
+}
+
+inline InlineLayoutUnit InlineRect::right() const
+{
+    ASSERT(m_hasValidLeft && m_hasValidWidth);
+    return m_rect.maxX();
+}
+
+inline InlineLayoutPoint InlineRect::topLeft() const
+{
+    ASSERT(hasValidPosition());
+    return m_rect.minXMinYCorner();
+}
+
+inline InlineLayoutSize InlineRect::size() const
+{
+    ASSERT(hasValidSize());
+    return m_rect.size();
+}
+
+inline InlineLayoutUnit InlineRect::width() const
+{
+    ASSERT(m_hasValidWidth);
+    return m_rect.width();
+}
+
+inline InlineLayoutUnit InlineRect::height() const
+{
+    ASSERT(m_hasValidHeight);
+    return m_rect.height();
+}
+
+inline void InlineRect::setTopLeft(const InlineLayoutPoint& topLeft)
+{
+#if ASSERT_ENABLED
+    setHasValidPosition();
+#endif
+    m_rect.setLocation(topLeft);
+}
+
+inline void InlineRect::setTop(InlineLayoutUnit top)
+{
+#if ASSERT_ENABLED
+    m_hasValidTop = true;
+#endif
+    m_rect.setY(top);
+}
+
+inline void InlineRect::setBottom(InlineLayoutUnit bottom)
+{
+#if ASSERT_ENABLED
+    m_hasValidTop = true;
+    m_hasValidHeight = true;
+#endif
+    m_rect.shiftMaxYEdgeTo(bottom);
+}
+
+inline void InlineRect::setLeft(InlineLayoutUnit left)
+{
+#if ASSERT_ENABLED
+    m_hasValidLeft = true;
+#endif
+    m_rect.setX(left);
+}
+
+inline void InlineRect::setWidth(InlineLayoutUnit width)
+{
+#if ASSERT_ENABLED
+    m_hasValidWidth = true;
+#endif
+    m_rect.setWidth(width);
+}
+
+inline void InlineRect::setHeight(InlineLayoutUnit height)
+{
+#if ASSERT_ENABLED
+    m_hasValidHeight = true;
+#endif
+    m_rect.setHeight(height);
+}
+
+inline void InlineRect::moveHorizontally(InlineLayoutUnit offset)
+{
+    ASSERT(m_hasValidLeft);
+    m_rect.move(InlineLayoutSize { offset, 0 });
+}
+
+inline void InlineRect::moveVertically(InlineLayoutUnit offset)
+{
+    ASSERT(m_hasValidTop);
+    m_rect.move(InlineLayoutSize { 0, offset });
+}
+
+inline void InlineRect::moveBy(InlineLayoutPoint offset)
+{
+    ASSERT(m_hasValidTop);
+    ASSERT(m_hasValidLeft);
+    m_rect.moveBy(offset);
+}
+
+inline void InlineRect::expand(Optional<InlineLayoutUnit> width, Optional<InlineLayoutUnit> height)
+{
+    ASSERT(!width || m_hasValidWidth);
+    ASSERT(!height || m_hasValidHeight);
+    m_rect.expand(width.valueOr(0), height.valueOr(0));
+}
+
+inline void InlineRect::expandToContain(const InlineRect& other)
+{
+#if ASSERT_ENABLED
+    m_hasValidTop = true;
+    m_hasValidLeft = true;
+    m_hasValidWidth = true;
+    m_hasValidHeight = true;
+#endif
+    m_rect.uniteEvenIfEmpty(other);
+}
+
+inline void InlineRect::expandVerticallyToContain(const InlineRect& other)
+{
+    auto containTop = std::min(top(), other.top());
+    auto containBottom = std::max(bottom(), other.bottom());
+    setTop(containTop);
+    setBottom(containBottom);
+}
+
+inline void InlineRect::inflate(InlineLayoutUnit inflate)
+{
+    ASSERT(hasValidGeometry());
+    m_rect.inflate(inflate);
+}
+
+inline InlineRect::operator InlineLayoutRect() const
+{
+    ASSERT(hasValidGeometry()); 
+    return m_rect;
+}
+
+}
+}
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineSoftLineBreakItemhfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingInlineSoftLineBreakItemh"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/InlineSoftLineBreakItem.h (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/InlineSoftLineBreakItem.h) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineSoftLineBreakItem.h                          (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineSoftLineBreakItem.h     2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,61 @@
</span><ins>+/*
+ * 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 "InlineItem.h"
+
+namespace WebCore {
+namespace Layout {
+
+class InlineSoftLineBreakItem : public InlineItem {
+public:
+    static InlineSoftLineBreakItem createSoftLineBreakItem(const InlineTextBox&, unsigned position);
+
+    unsigned position() const { return m_startOrPosition; }
+    const InlineTextBox& inlineTextBox() const { return downcast<InlineTextBox>(layoutBox()); }
+
+    InlineSoftLineBreakItem(const InlineTextBox&, unsigned position);
+};
+
+inline InlineSoftLineBreakItem InlineSoftLineBreakItem::createSoftLineBreakItem(const InlineTextBox& inlineTextBox, unsigned position)
+{
+    return { inlineTextBox, position };
+}
+
+inline InlineSoftLineBreakItem::InlineSoftLineBreakItem(const InlineTextBox& inlineTextBox, unsigned position)
+    : InlineItem(inlineTextBox, Type::SoftLineBreak)
+{
+    m_startOrPosition = position;
+}
+
+}
+}
+
+SPECIALIZE_TYPE_TRAITS_INLINE_ITEM(InlineSoftLineBreakItem, isSoftLineBreak())
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineTextItemcppfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingInlineTextItemcpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/InlineTextItem.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/InlineTextItem.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineTextItem.cpp                         (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineTextItem.cpp    2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,165 @@
</span><ins>+/*
+ * Copyright (C) 2018 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 "InlineTextItem.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "FontCascade.h"
+#include "InlineSoftLineBreakItem.h"
+#include "TextUtil.h"
+#include <wtf/unicode/CharacterNames.h>
+
+namespace WebCore {
+namespace Layout {
+
+static_assert(sizeof(InlineItem) == sizeof(InlineTextItem), "");
+
+struct WhitespaceContent {
+    size_t length { 0 };
+    bool isWordSeparator { true };
+};
+static Optional<WhitespaceContent> moveToNextNonWhitespacePosition(const StringView& textContent, size_t startPosition, bool preserveNewline, bool preserveTab)
+{
+    auto hasWordSeparatorCharacter = false;
+    auto isWhitespaceCharacter = [&](auto character) {
+        // white space processing in CSS affects only the document white space characters: spaces (U+0020), tabs (U+0009), and segment breaks.
+        auto isTreatedAsSpaceCharacter = character == space || (character == newlineCharacter && !preserveNewline) || (character == tabCharacter && !preserveTab);
+        hasWordSeparatorCharacter = hasWordSeparatorCharacter || isTreatedAsSpaceCharacter;
+        return isTreatedAsSpaceCharacter || character == tabCharacter;
+    };
+    auto nextNonWhiteSpacePosition = startPosition;
+    while (nextNonWhiteSpacePosition < textContent.length() && isWhitespaceCharacter(textContent[nextNonWhiteSpacePosition]))
+        ++nextNonWhiteSpacePosition;
+    return nextNonWhiteSpacePosition == startPosition ? WTF::nullopt : makeOptional(WhitespaceContent { nextNonWhiteSpacePosition - startPosition, hasWordSeparatorCharacter });
+}
+
+static unsigned moveToNextBreakablePosition(unsigned startPosition, LazyLineBreakIterator& lineBreakIterator, const RenderStyle& style)
+{
+    auto textLength = lineBreakIterator.stringView().length();
+    auto startPositionForNextBreakablePosition = startPosition;
+    while (startPositionForNextBreakablePosition < textLength) {
+        auto nextBreakablePosition = TextUtil::findNextBreakablePosition(lineBreakIterator, startPositionForNextBreakablePosition, style);
+        // Oftentimes the next breakable position comes back as the start position (most notably hyphens).
+        if (nextBreakablePosition != startPosition)
+            return nextBreakablePosition - startPosition;
+        ++startPositionForNextBreakablePosition;
+    }
+    return textLength - startPosition;
+}
+
+void InlineTextItem::createAndAppendTextItems(InlineItems& inlineContent, const InlineTextBox& inlineTextBox)
+{
+    auto text = inlineTextBox.content();
+    if (!text.length())
+        return inlineContent.append(InlineTextItem::createEmptyItem(inlineTextBox));
+
+    auto& style = inlineTextBox.style();
+    auto& font = style.fontCascade();
+    auto whitespaceContentIsTreatedAsSingleSpace = !TextUtil::shouldPreserveSpacesAndTabs(inlineTextBox);
+    auto shouldPreserveNewline = TextUtil::shouldPreserveNewline(inlineTextBox);
+    auto lineBreakIterator = LazyLineBreakIterator { text, style.computedLocale(), TextUtil::lineBreakIteratorMode(style.lineBreak()) };
+    unsigned currentPosition = 0;
+
+    auto inlineItemWidth = [&](auto startPosition, auto length) -> Optional<InlineLayoutUnit> {
+        if (!inlineTextBox.canUseSimplifiedContentMeasuring())
+            return { };
+        return TextUtil::width(inlineTextBox, startPosition, startPosition + length, { });
+    };
+
+    while (currentPosition < text.length()) {
+        auto isSegmentBreakCandidate = [](auto character) {
+            return character == '\n';
+        };
+
+        // Segment breaks with preserve new line style (white-space: pre, pre-wrap, break-spaces and pre-line) compute to forced line break.
+        if (isSegmentBreakCandidate(text[currentPosition]) && shouldPreserveNewline) {
+            inlineContent.append(InlineSoftLineBreakItem::createSoftLineBreakItem(inlineTextBox, currentPosition));
+            ++currentPosition;
+            continue;
+        }
+
+        if (auto whitespaceContent = moveToNextNonWhitespacePosition(text, currentPosition, shouldPreserveNewline, !whitespaceContentIsTreatedAsSingleSpace)) {
+            ASSERT(whitespaceContent->length);
+            auto appendWhitespaceItem = [&] (auto startPosition, auto itemLength) {
+                auto simpleSingleWhitespaceContent = inlineTextBox.canUseSimplifiedContentMeasuring() && (itemLength == 1 || whitespaceContentIsTreatedAsSingleSpace);
+                auto width = simpleSingleWhitespaceContent ? makeOptional(InlineLayoutUnit { font.spaceWidth() }) : inlineItemWidth(startPosition, itemLength);
+                inlineContent.append(InlineTextItem::createWhitespaceItem(inlineTextBox, startPosition, itemLength, whitespaceContent->isWordSeparator, width));
+            };
+            if (style.whiteSpace() == WhiteSpace::BreakSpaces) {
+                // https://www.w3.org/TR/css-text-3/#white-space-phase-1
+                // For break-spaces, a soft wrap opportunity exists after every space and every tab.
+                // FIXME: if this turns out to be a perf hit with too many individual whitespace inline items, we should transition this logic to line breaking.
+                for (size_t i = 0; i < whitespaceContent->length; ++i)
+                    appendWhitespaceItem(currentPosition + i, 1);
+            } else
+                appendWhitespaceItem(currentPosition, whitespaceContent->length);
+            currentPosition += whitespaceContent->length;
+            continue;
+        }
+
+        auto hasTrailingSoftHyphen = false;
+        auto initialNonWhitespacePosition = currentPosition;
+        auto isAtSoftHyphen = [&](auto position) {
+            return text[position] == softHyphen;
+        };
+        if (style.hyphens() == Hyphens::None) {
+            // Let's merge candidate InlineTextItems separated by soft hyphen when the style says so.
+            while (currentPosition < text.length()) {
+                auto nonWhiteSpaceLength = moveToNextBreakablePosition(currentPosition, lineBreakIterator, style);
+                ASSERT(nonWhiteSpaceLength);
+                currentPosition += nonWhiteSpaceLength;
+                if (!isAtSoftHyphen(currentPosition - 1))
+                    break;
+            }
+        } else {
+            auto nonWhiteSpaceLength = moveToNextBreakablePosition(initialNonWhitespacePosition, lineBreakIterator, style);
+            ASSERT(nonWhiteSpaceLength);
+            currentPosition += nonWhiteSpaceLength;
+            hasTrailingSoftHyphen = isAtSoftHyphen(currentPosition - 1);
+        }
+        ASSERT(initialNonWhitespacePosition < currentPosition);
+        ASSERT_IMPLIES(style.hyphens() == Hyphens::None, !hasTrailingSoftHyphen);
+        auto length = currentPosition - initialNonWhitespacePosition;
+        inlineContent.append(InlineTextItem::createNonWhitespaceItem(inlineTextBox, initialNonWhitespacePosition, length, hasTrailingSoftHyphen, inlineItemWidth(initialNonWhitespacePosition, length)));
+    }
+}
+
+bool InlineTextItem::isEmptyContent() const
+{
+    // FIXME: We should check for more zero width content and not just U+200B.
+    return !m_length || (m_length == 1 && inlineTextBox().content()[start()] == zeroWidthSpace); 
+}
+
+bool InlineTextItem::shouldPreserveSpacesAndTabs(const InlineTextItem& inlineTextItem)
+{
+    ASSERT(inlineTextItem.isWhitespace());
+    return TextUtil::shouldPreserveSpacesAndTabs(inlineTextItem.layoutBox());
+}
+
+}
+}
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineTextItemhfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingInlineTextItemh"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/InlineTextItem.h (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/InlineTextItem.h) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineTextItem.h                           (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineTextItem.h      2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,124 @@
</span><ins>+/*
+ * Copyright (C) 2018 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 "InlineItem.h"
+#include "LayoutInlineTextBox.h"
+
+namespace WebCore {
+namespace Layout {
+
+using InlineItems = Vector<InlineItem>;
+
+class InlineTextItem : public InlineItem {
+public:
+    static void createAndAppendTextItems(InlineItems&, const InlineTextBox&);
+
+    unsigned start() const { return m_startOrPosition; }
+    unsigned end() const { return start() + length(); }
+    unsigned length() const { return m_length; }
+
+    bool isWhitespace() const { return m_textItemType == TextItemType::Whitespace; }
+    bool isWordSeparator() const { return m_isWordSeparator; }
+    bool hasTrailingSoftHyphen() const { return m_hasTrailingSoftHyphen; }
+    Optional<InlineLayoutUnit> width() const { return m_hasWidth ? makeOptional(m_width) : Optional<InlineLayoutUnit> { }; }
+    bool isEmptyContent() const;
+
+    const InlineTextBox& inlineTextBox() const { return downcast<InlineTextBox>(layoutBox()); }
+
+    InlineTextItem left(unsigned length) const;
+    InlineTextItem right(unsigned length) const;
+
+    static bool shouldPreserveSpacesAndTabs(const InlineTextItem&);
+
+private:
+    using InlineItem::TextItemType;
+
+    InlineTextItem(const InlineTextBox&, unsigned start, unsigned length, bool hasTrailingSoftHyphen, bool isWordSeparator, Optional<InlineLayoutUnit> width, TextItemType);
+    explicit InlineTextItem(const InlineTextBox&);
+
+    static InlineTextItem createWhitespaceItem(const InlineTextBox&, unsigned start, unsigned length, bool isWordSeparator, Optional<InlineLayoutUnit> width);
+    static InlineTextItem createNonWhitespaceItem(const InlineTextBox&, unsigned start, unsigned length, bool hasTrailingSoftHyphen, Optional<InlineLayoutUnit> width);
+    static InlineTextItem createEmptyItem(const InlineTextBox&);
+};
+
+inline InlineTextItem InlineTextItem::createWhitespaceItem(const InlineTextBox& inlineTextBox, unsigned start, unsigned length, bool isWordSeparator, Optional<InlineLayoutUnit> width)
+{
+    return { inlineTextBox, start, length, false, isWordSeparator, width, TextItemType::Whitespace };
+}
+
+inline InlineTextItem InlineTextItem::createNonWhitespaceItem(const InlineTextBox& inlineTextBox, unsigned start, unsigned length, bool hasTrailingSoftHyphen, Optional<InlineLayoutUnit> width)
+{
+    // FIXME: Use the following list of non-whitespace characters to set the "isWordSeparator" bit: noBreakSpace, ethiopicWordspace, aegeanWordSeparatorLine aegeanWordSeparatorDot ugariticWordDivider.
+    return { inlineTextBox, start, length, hasTrailingSoftHyphen, false, width, TextItemType::NonWhitespace };
+}
+
+inline InlineTextItem InlineTextItem::createEmptyItem(const InlineTextBox& inlineTextBox)
+{
+    return InlineTextItem { inlineTextBox };
+}
+
+inline InlineTextItem::InlineTextItem(const InlineTextBox& inlineTextBox, unsigned start, unsigned length, bool hasTrailingSoftHyphen, bool isWordSeparator, Optional<InlineLayoutUnit> width, TextItemType textItemType)
+    : InlineItem(inlineTextBox, Type::Text)
+{
+    m_startOrPosition = start;
+    m_length = length;
+    m_hasWidth = !!width;
+    m_hasTrailingSoftHyphen = hasTrailingSoftHyphen;
+    m_isWordSeparator = isWordSeparator;
+    m_width = width.valueOr(0);
+    m_textItemType = textItemType;
+}
+
+inline InlineTextItem::InlineTextItem(const InlineTextBox& inlineTextBox)
+    : InlineItem(inlineTextBox, Type::Text)
+{
+}
+
+inline InlineTextItem InlineTextItem::left(unsigned length) const
+{
+    RELEASE_ASSERT(length <= this->length());
+    ASSERT(m_textItemType != TextItemType::Undefined);
+    ASSERT(length);
+    return { inlineTextBox(), start(), length, false, isWordSeparator(), WTF::nullopt, m_textItemType };
+}
+
+inline InlineTextItem InlineTextItem::right(unsigned length) const
+{
+    RELEASE_ASSERT(length <= this->length());
+    ASSERT(m_textItemType != TextItemType::Undefined);
+    ASSERT(length);
+    return { inlineTextBox(), end() - length, length, hasTrailingSoftHyphen(), isWordSeparator(), WTF::nullopt, m_textItemType };
+}
+
+}
+}
+
+SPECIALIZE_TYPE_TRAITS_INLINE_ITEM(InlineTextItem, isText())
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlinetextTextUtilcppfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingtextTextUtilcpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/text/TextUtil.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/text/TextUtil.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/text/TextUtil.cpp                          (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/text/TextUtil.cpp     2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,175 @@
</span><ins>+/*
+ * Copyright (C) 2018 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 "TextUtil.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "BreakLines.h"
+#include "FontCascade.h"
+#include "InlineTextItem.h"
+#include "LayoutInlineTextBox.h"
+#include "RenderBox.h"
+#include "RenderStyle.h"
+
+namespace WebCore {
+namespace Layout {
+
+InlineLayoutUnit TextUtil::width(const InlineTextItem& inlineTextItem, InlineLayoutUnit contentLogicalLeft)
+{
+    return TextUtil::width(inlineTextItem, inlineTextItem.start(), inlineTextItem.end(), contentLogicalLeft);
+}
+
+InlineLayoutUnit TextUtil::width(const InlineTextItem& inlineTextItem, unsigned from, unsigned to, InlineLayoutUnit contentLogicalLeft)
+{
+    RELEASE_ASSERT(from >= inlineTextItem.start());
+    RELEASE_ASSERT(to <= inlineTextItem.end());
+    if (inlineTextItem.isWhitespace() && !InlineTextItem::shouldPreserveSpacesAndTabs(inlineTextItem)) {
+        auto spaceWidth = inlineTextItem.style().fontCascade().spaceWidth();
+        return std::isnan(spaceWidth) ? 0.0f : std::isinf(spaceWidth) ? maxInlineLayoutUnit() : spaceWidth;
+    }
+    return TextUtil::width(inlineTextItem.inlineTextBox(), from, to, contentLogicalLeft);
+}
+
+InlineLayoutUnit TextUtil::width(const InlineTextBox& inlineTextBox, unsigned from, unsigned to, InlineLayoutUnit contentLogicalLeft)
+{
+    if (from == to)
+        return 0;
+
+    auto& style = inlineTextBox.style();
+    auto& font = style.fontCascade();
+    auto text = inlineTextBox.content();
+    ASSERT(to <= text.length());
+    auto hasKerningOrLigatures = font.enableKerning() || font.requiresShaping();
+    auto measureWithEndSpace = hasKerningOrLigatures && to < text.length() && text[to] == ' ';
+    if (measureWithEndSpace)
+        ++to;
+    float width = 0;
+    if (inlineTextBox.canUseSimplifiedContentMeasuring())
+        width = font.widthForSimpleText(StringView(text).substring(from, to - from));
+    else {
+        auto tabWidth = style.collapseWhiteSpace() ? TabSize(0) : style.tabSize();
+        WebCore::TextRun run(StringView(text).substring(from, to - from), contentLogicalLeft);
+        if (tabWidth)
+            run.setTabSize(true, tabWidth);
+        width = font.width(run);
+    }
+
+    if (measureWithEndSpace)
+        width -= (font.spaceWidth() + font.wordSpacing());
+
+    return std::isnan(width) ? 0.0f : std::isinf(width) ? maxInlineLayoutUnit() : width;
+}
+
+TextUtil::SplitData TextUtil::split(const InlineTextItem& inlineTextItem, InlineLayoutUnit textWidth, InlineLayoutUnit availableWidth, InlineLayoutUnit contentLogicalLeft)
+{
+    ASSERT(availableWidth >= 0);
+    auto startPosition = inlineTextItem.start();
+    auto length = inlineTextItem.length();
+    ASSERT(length);
+
+    auto left = startPosition;
+    // Pathological case of (extremely)long string and narrow lines.
+    // Adjust the range so that we can pick a reasonable midpoint.
+    InlineLayoutUnit averageCharacterWidth = textWidth / length;
+    unsigned offset = toLayoutUnit(2 * availableWidth / averageCharacterWidth).toUnsigned();
+    auto right = std::min<unsigned>(left + offset, (startPosition + length - 1));
+    // Preserve the left width for the final split position so that we don't need to remeasure the left side again.
+    InlineLayoutUnit leftSideWidth = 0;
+    while (left < right) {
+        auto middle = (left + right) / 2;
+        auto width = TextUtil::width(inlineTextItem, startPosition, middle + 1, contentLogicalLeft);
+        if (width < availableWidth) {
+            left = middle + 1;
+            leftSideWidth = width;
+        } else if (width > availableWidth)
+            right = middle;
+        else {
+            right = middle + 1;
+            leftSideWidth = width;
+            break;
+        }
+    }
+    return { startPosition, right - startPosition, leftSideWidth };
+}
+
+unsigned TextUtil::findNextBreakablePosition(LazyLineBreakIterator& lineBreakIterator, unsigned startPosition, const RenderStyle& style)
+{
+    auto keepAllWordsForCJK = style.wordBreak() == WordBreak::KeepAll;
+    auto breakNBSP = style.autoWrap() && style.nbspMode() == NBSPMode::Space;
+
+    if (keepAllWordsForCJK) {
+        if (breakNBSP)
+            return nextBreakablePositionKeepingAllWords(lineBreakIterator, startPosition);
+        return nextBreakablePositionKeepingAllWordsIgnoringNBSP(lineBreakIterator, startPosition);
+    }
+
+    if (lineBreakIterator.mode() == LineBreakIteratorMode::Default) {
+        if (breakNBSP)
+            return WebCore::nextBreakablePosition(lineBreakIterator, startPosition);
+        return nextBreakablePositionIgnoringNBSP(lineBreakIterator, startPosition);
+    }
+
+    if (breakNBSP)
+        return nextBreakablePositionWithoutShortcut(lineBreakIterator, startPosition);
+    return nextBreakablePositionIgnoringNBSPWithoutShortcut(lineBreakIterator, startPosition);
+}
+
+bool TextUtil::shouldPreserveSpacesAndTabs(const Box& layoutBox)
+{
+    // https://www.w3.org/TR/css-text-3/#white-space-property
+    auto whitespace = layoutBox.style().whiteSpace();
+    return whitespace == WhiteSpace::Pre || whitespace == WhiteSpace::PreWrap || whitespace == WhiteSpace::BreakSpaces;
+}
+
+bool TextUtil::shouldPreserveNewline(const Box& layoutBox)
+{
+    auto whitespace = layoutBox.style().whiteSpace();
+    // https://www.w3.org/TR/css-text-3/#white-space-property
+    return whitespace == WhiteSpace::Pre || whitespace == WhiteSpace::PreWrap || whitespace == WhiteSpace::BreakSpaces || whitespace == WhiteSpace::PreLine; 
+}
+
+LineBreakIteratorMode TextUtil::lineBreakIteratorMode(LineBreak lineBreak)
+{
+    switch (lineBreak) {
+    case LineBreak::Auto:
+    case LineBreak::AfterWhiteSpace:
+    case LineBreak::Anywhere:
+        return LineBreakIteratorMode::Default;
+    case LineBreak::Loose:
+        return LineBreakIteratorMode::Loose;
+    case LineBreak::Normal:
+        return LineBreakIteratorMode::Normal;
+    case LineBreak::Strict:
+        return LineBreakIteratorMode::Strict;
+    }
+    ASSERT_NOT_REACHED();
+    return LineBreakIteratorMode::Default;
+}
+
+}
+}
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlinetextTextUtilhfromrev276885trunkSourceWebCorelayoutformattingContextsinlineformattingtextTextUtilh"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/text/TextUtil.h (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/inlineformatting/text/TextUtil.h) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/text/TextUtil.h                            (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/text/TextUtil.h       2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,65 @@
</span><ins>+/*
+ * Copyright (C) 2018 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 "InlineItem.h"
+#include "LayoutUnits.h"
+#include <wtf/text/TextBreakIterator.h>
+
+namespace WebCore {
+
+class RenderStyle;
+
+namespace Layout {
+
+class InlineTextBox;
+class InlineTextItem;
+
+class TextUtil {
+public:
+    static InlineLayoutUnit width(const InlineTextItem&, InlineLayoutUnit contentLogicalLeft);
+    static InlineLayoutUnit width(const InlineTextItem&, unsigned from, unsigned to, InlineLayoutUnit contentLogicalLeft);
+    static InlineLayoutUnit width(const InlineTextBox&, unsigned from, unsigned to, InlineLayoutUnit contentLogicalLeft);
+
+    struct SplitData {
+        unsigned start { 0 };
+        unsigned length { 0 };
+        InlineLayoutUnit logicalWidth { 0 };
+    };
+    static SplitData split(const InlineTextItem&, InlineLayoutUnit textWidth, InlineLayoutUnit availableWidth, InlineLayoutUnit contentLogicalLeft);
+
+    static unsigned findNextBreakablePosition(LazyLineBreakIterator&, unsigned startPosition, const RenderStyle&);
+    static LineBreakIteratorMode lineBreakIteratorMode(LineBreak);
+
+    static bool shouldPreserveSpacesAndTabs(const Box&);
+    static bool shouldPreserveNewline(const Box&);
+};
+
+}
+}
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextstableTableFormattingContextcppfromrev276885trunkSourceWebCorelayoutformattingContextstableformattingTableFormattingContextcpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingContext.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/tableformatting/TableFormattingContext.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingContext.cpp                          (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingContext.cpp     2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,520 @@
</span><ins>+/*
+ * 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 "TableFormattingContext.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "BlockFormattingState.h"
+#include "FloatingState.h"
+#include "InlineFormattingState.h"
+#include "InvalidationState.h"
+#include "LayoutBox.h"
+#include "LayoutBoxGeometry.h"
+#include "LayoutChildIterator.h"
+#include "LayoutContext.h"
+#include "LayoutInitialContainingBlock.h"
+#include "TableFormattingState.h"
+#include <wtf/IsoMallocInlines.h>
+
+namespace WebCore {
+namespace Layout {
+
+WTF_MAKE_ISO_ALLOCATED_IMPL(TableFormattingContext);
+
+// https://www.w3.org/TR/css-tables-3/#table-layout-algorithm
+TableFormattingContext::TableFormattingContext(const ContainerBox& formattingContextRoot, TableFormattingState& formattingState)
+    : FormattingContext(formattingContextRoot, formattingState)
+{
+}
+
+void TableFormattingContext::layoutInFlowContent(InvalidationState&, const ConstraintsForInFlowContent& constraints)
+{
+    auto availableHorizontalSpace = constraints.horizontal.logicalWidth;
+    auto availableVerticalSpace = constraints.vertical.logicalHeight;
+    // 1. Compute width and height for the grid.
+    computeAndDistributeExtraSpace(availableHorizontalSpace, availableVerticalSpace);
+    // 2. Finalize cells.
+    setUsedGeometryForCells(availableHorizontalSpace);
+    // 3. Finalize rows.
+    setUsedGeometryForRows(availableHorizontalSpace);
+    // 4. Finalize sections.
+    setUsedGeometryForSections(constraints);
+}
+
+LayoutUnit TableFormattingContext::usedContentHeight() const
+{
+    // Table has to have some section content, at least one <tbody>.
+    auto top = BoxGeometry::marginBoxRect(geometryForBox(*root().firstInFlowChild())).top();
+    auto bottom = BoxGeometry::marginBoxRect(geometryForBox(*root().lastInFlowChild())).bottom();
+    return bottom - top;
+}
+
+void TableFormattingContext::setUsedGeometryForCells(LayoutUnit availableHorizontalSpace)
+{
+    auto& grid = formattingState().tableGrid();
+    auto& columnList = grid.columns().list();
+    auto& rowList = grid.rows().list();
+    // Final table cell layout. At this point all percentage values can be resolved.
+    auto sectionOffset = LayoutUnit { };
+    auto* currentSection = &rowList.first().box().parent();
+    for (auto& cell : grid.cells()) {
+        auto& cellBox = cell->box();
+        auto& cellBoxGeometry = formattingState().boxGeometry(cellBox);
+        auto& section = rowList[cell->startRow()].box().parent();
+        if (&section != currentSection) {
+            currentSection = &section;
+            // While the grid is a continuous flow of rows, in the display tree they are relative to their sections.
+            sectionOffset = rowList[cell->startRow()].logicalTop();
+        }
+        cellBoxGeometry.setLogicalTop(rowList[cell->startRow()].logicalTop() - sectionOffset);
+        cellBoxGeometry.setLogicalLeft(columnList[cell->startColumn()].logicalLeft());
+        auto availableVerticalSpace = rowList[cell->startRow()].logicalHeight();
+        for (size_t rowIndex = cell->startRow() + 1; rowIndex < cell->endRow(); ++rowIndex)
+            availableVerticalSpace += rowList[rowIndex].logicalHeight();
+        availableVerticalSpace += (cell->rowSpan() - 1) * grid.verticalSpacing();
+        layoutCell(*cell, availableHorizontalSpace, availableVerticalSpace);
+        // FIXME: Find out if it is ok to use the regular padding here to align the content box inside a tall cell or we need to 
+        // use some kind of intrinsic padding similar to RenderTableCell.
+        auto paddingTop = cellBoxGeometry.paddingTop().valueOr(LayoutUnit { });
+        auto paddingBottom = cellBoxGeometry.paddingBottom().valueOr(LayoutUnit { });
+        auto intrinsicPaddingTop = LayoutUnit { };
+        auto intrinsicPaddingBottom = LayoutUnit { };
+
+        switch (cellBox.style().verticalAlign()) {
+        case VerticalAlign::Middle: {
+            auto intrinsicVerticalPadding = std::max(0_lu, availableVerticalSpace - cellBoxGeometry.verticalMarginBorderAndPadding() - cellBoxGeometry.contentBoxHeight());
+            intrinsicPaddingTop = intrinsicVerticalPadding / 2;
+            intrinsicPaddingBottom = intrinsicVerticalPadding / 2;
+            break;
+        }
+        case VerticalAlign::Baseline: {
+            auto rowBaseline = LayoutUnit { rowList[cell->startRow()].baseline() };
+            auto cellBaseline = LayoutUnit { cell->baseline() };
+            intrinsicPaddingTop = std::max(0_lu, rowBaseline - cellBaseline - cellBoxGeometry.borderTop());
+            intrinsicPaddingBottom = std::max(0_lu, availableVerticalSpace - cellBoxGeometry.verticalMarginBorderAndPadding() - intrinsicPaddingTop - cellBoxGeometry.contentBoxHeight());
+            break;
+        }
+        default:
+            ASSERT_NOT_IMPLEMENTED_YET();
+            break;
+        }
+        if (intrinsicPaddingTop && cellBox.hasInFlowOrFloatingChild()) {
+            auto adjustCellContentWithInstrinsicPaddingBefore = [&] {
+                // Child boxes (and runs) are always in the coordinate system of the containing block's border box.
+                // The content box (where the child content lives) is inside the padding box, which is inside the border box.
+                // In order to compute the child box top/left position, we need to know both the padding and the border offsets.  
+                // Normally by the time we start positioning the child content, we already have computed borders and padding for the containing block.
+                // This is different with table cells where the final padding offset depends on the content height as we use
+                // the padding box to vertically align the table cell content.
+                auto& formattingState = layoutState().establishedFormattingState(cellBox);
+                for (auto* child = cellBox.firstInFlowOrFloatingChild(); child; child = child->nextInFlowOrFloatingSibling()) {
+                    if (child->isInlineTextBox())
+                        continue;
+                    formattingState.boxGeometry(*child).moveVertically(intrinsicPaddingTop);
+                }
+                if (cellBox.establishesInlineFormattingContext()) {
+                    auto& inlineFormattingStatee = layoutState().establishedInlineFormattingState(cellBox);
+                    for (auto& run : inlineFormattingStatee.lineRuns())
+                        run.moveVertically(intrinsicPaddingTop);
+                    for (auto& line : inlineFormattingStatee.lines())
+                        line.moveVertically(intrinsicPaddingTop);
+                }
+            };
+            adjustCellContentWithInstrinsicPaddingBefore();
+        }
+        cellBoxGeometry.setVerticalPadding({ paddingTop + intrinsicPaddingTop, paddingBottom + intrinsicPaddingBottom });
+    }
+}
+
+void TableFormattingContext::setUsedGeometryForRows(LayoutUnit availableHorizontalSpace)
+{
+    auto& grid = formattingState().tableGrid();
+    auto& rows = grid.rows().list();
+
+    auto rowLogicalTop = grid.verticalSpacing();
+    const ContainerBox* previousRow = nullptr;
+    for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex) {
+        auto& row = rows[rowIndex];
+        auto& rowBox = row.box();
+        auto& rowBoxGeometry = formattingState().boxGeometry(rowBox);
+
+        rowBoxGeometry.setPadding(geometry().computedPadding(rowBox, availableHorizontalSpace));
+        // Internal table elements do not have margins.
+        rowBoxGeometry.setHorizontalMargin({ });
+        rowBoxGeometry.setVerticalMargin({ });
+
+        auto computedRowBorder = [&] {
+            auto border = geometry().computedBorder(rowBox);
+            if (!grid.collapsedBorder())
+                return border;
+            // Border collapsing delegates borders to table/cells.
+            border.horizontal = { };
+            if (!rowIndex)
+                border.vertical.top = { };
+            if (rowIndex == rows.size() - 1)
+                border.vertical.bottom = { };
+            return border;
+        }();
+        if (computedRowBorder.height() > row.logicalHeight()) {
+            // FIXME: This is an odd quirk when the row border overflows the row.
+            // We don't paint row borders so it does not matter too much, but if we don't
+            // set this fake border value, than we either end up with a negative content box
+            // or with a wide frame box.
+            // If it happens to cause issues in the display tree, we could also consider
+            // a special frame box override, where padding box + border != frame box.
+            computedRowBorder.vertical.top = { };
+            computedRowBorder.vertical.bottom = { };
+        }
+        rowBoxGeometry.setContentBoxHeight(row.logicalHeight() - computedRowBorder.height());
+
+        auto rowLogicalWidth = grid.columns().logicalWidth() + 2 * grid.horizontalSpacing();
+        if (computedRowBorder.width() > rowLogicalWidth) {
+            // See comment above.
+            computedRowBorder.horizontal.left = { };
+            computedRowBorder.horizontal.right = { };
+        }
+        rowBoxGeometry.setContentBoxWidth(rowLogicalWidth - computedRowBorder.width());
+        rowBoxGeometry.setBorder(computedRowBorder);
+
+        if (previousRow && &previousRow->parent() != &rowBox.parent()) {
+            // This row is in a different section.
+            rowLogicalTop = { };
+        }
+        rowBoxGeometry.setLogicalTop(rowLogicalTop);
+        rowBoxGeometry.setLogicalLeft({ });
+
+        rowLogicalTop += row.logicalHeight() + grid.verticalSpacing();
+        previousRow = &rowBox;
+    }
+
+    auto& columns = grid.columns();
+    Vector<InlineLayoutUnit> rowBaselines(rows.size(), 0);
+    // Now that cells are laid out, let's compute the row baselines.
+    for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex) {
+        for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
+            auto& slot = *grid.slot({ columnIndex, rowIndex });
+            if (slot.isRowSpanned())
+                continue;
+            if (slot.hasRowSpan())
+                continue;
+            auto& cell = slot.cell();
+            rowBaselines[rowIndex] = std::max(rowBaselines[rowIndex], cell.baseline());
+        }
+    }
+    for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex)
+        rows[rowIndex].setBaseline(rowBaselines[rowIndex]);
+}
+
+void TableFormattingContext::setUsedGeometryForSections(const ConstraintsForInFlowContent& constraints)
+{
+    auto& grid = formattingState().tableGrid();
+    auto& tableBox = root();
+    auto sectionWidth = grid.columns().logicalWidth() + 2 * grid.horizontalSpacing();
+    auto logicalTop = constraints.vertical.logicalTop;
+    auto verticalSpacing = grid.verticalSpacing();
+    auto paddingBefore = Optional<LayoutUnit> { verticalSpacing };
+    auto paddingAfter = verticalSpacing;
+    for (auto& sectionBox : childrenOfType<ContainerBox>(tableBox)) {
+        auto& sectionBoxGeometry = formattingState().boxGeometry(sectionBox);
+        // Section borders are either collapsed or ignored.
+        sectionBoxGeometry.setBorder({ });
+        // Use fake vertical padding to space out the sections.
+        sectionBoxGeometry.setPadding(Edges { { }, { paddingBefore.valueOr(0_lu), paddingAfter } });
+        paddingBefore = WTF::nullopt;
+        // Internal table elements do not have margins.
+        sectionBoxGeometry.setHorizontalMargin({ });
+        sectionBoxGeometry.setVerticalMargin({ });
+
+        sectionBoxGeometry.setContentBoxWidth(sectionWidth);
+        auto sectionContentHeight = LayoutUnit { };
+        size_t rowCount = 0;
+        for (auto& rowBox : childrenOfType<ContainerBox>(sectionBox)) {
+            sectionContentHeight += geometryForBox(rowBox).borderBoxHeight();
+            ++rowCount;
+        }
+        sectionContentHeight += verticalSpacing * (rowCount - 1);
+        sectionBoxGeometry.setContentBoxHeight(sectionContentHeight);
+        sectionBoxGeometry.setLogicalLeft(constraints.horizontal.logicalLeft);
+        sectionBoxGeometry.setLogicalTop(logicalTop);
+
+        logicalTop += sectionBoxGeometry.borderBoxHeight();
+    }
+}
+
+void TableFormattingContext::layoutCell(const TableGrid::Cell& cell, LayoutUnit availableHorizontalSpace, Optional<LayoutUnit> usedCellHeight)
+{
+    ASSERT(cell.box().establishesBlockFormattingContext());
+
+    auto& grid = formattingState().tableGrid();
+    auto& cellBox = cell.box();
+    auto& cellBoxGeometry = formattingState().boxGeometry(cellBox);
+
+    cellBoxGeometry.setBorder(geometry().computedCellBorder(cell));
+    cellBoxGeometry.setPadding(geometry().computedPadding(cellBox, availableHorizontalSpace));
+    // Internal table elements do not have margins.
+    cellBoxGeometry.setHorizontalMargin({ });
+    cellBoxGeometry.setVerticalMargin({ });
+
+    auto availableSpaceForContent = [&] {
+        auto& columnList = grid.columns().list();
+        auto logicalWidth = LayoutUnit { };
+        for (auto columnIndex = cell.startColumn(); columnIndex < cell.endColumn(); ++columnIndex)
+            logicalWidth += columnList.at(columnIndex).logicalWidth();
+        // No column spacing when spanning.
+        logicalWidth += (cell.columnSpan() - 1) * grid.horizontalSpacing();
+        return logicalWidth - cellBoxGeometry.horizontalMarginBorderAndPadding();
+    }();
+    cellBoxGeometry.setContentBoxWidth(availableSpaceForContent);
+
+    if (cellBox.hasInFlowOrFloatingChild()) {
+        auto constraintsForCellContent = geometry().constraintsForInFlowContent(cellBox);
+        constraintsForCellContent.vertical.logicalHeight = usedCellHeight;
+        auto invalidationState = InvalidationState { };
+        // FIXME: This should probably be part of the invalidation state to indicate when we re-layout the cell
+        // multiple times as part of the multi-pass table algorithm.
+        auto& floatingStateForCellContent = layoutState().ensureBlockFormattingState(cellBox).floatingState();
+        floatingStateForCellContent.clear();
+        LayoutContext::createFormattingContext(cellBox, layoutState())->layoutInFlowContent(invalidationState, constraintsForCellContent);
+    }
+    cellBoxGeometry.setContentBoxHeight(geometry().cellHeigh(cellBox));
+}
+
+FormattingContext::IntrinsicWidthConstraints TableFormattingContext::computedIntrinsicWidthConstraints()
+{
+    ASSERT(!root().isSizeContainmentBox());
+    // Tables have a slighty different concept of shrink to fit. It's really only different with non-auto "width" values, where
+    // a generic shrink-to fit block level box like a float box would be just sized to the computed value of "width", tables
+    // can actually be streched way over.
+    auto& grid = formattingState().tableGrid();
+    if (auto computedWidthConstraints = grid.widthConstraints())
+        return *computedWidthConstraints;
+
+    // Compute the minimum/maximum width of each column.
+    auto computedWidthConstraints = computedPreferredWidthForColumns();
+    grid.setWidthConstraints(computedWidthConstraints);
+    return computedWidthConstraints;
+}
+
+UniqueRef<TableGrid> TableFormattingContext::ensureTableGrid(const ContainerBox& tableBox)
+{
+    auto tableGrid = makeUniqueRef<TableGrid>();
+    auto& tableStyle = tableBox.style();
+    auto shouldApplyBorderSpacing = tableStyle.borderCollapse() == BorderCollapse::Separate;
+    tableGrid->setHorizontalSpacing(LayoutUnit { shouldApplyBorderSpacing ? tableStyle.horizontalBorderSpacing() : 0 });
+    tableGrid->setVerticalSpacing(LayoutUnit { shouldApplyBorderSpacing ? tableStyle.verticalBorderSpacing() : 0 });
+
+    auto* firstChild = tableBox.firstChild();
+    if (!firstChild) {
+        // The rare case of empty table.
+        return tableGrid;
+    }
+
+    const Box* tableCaption = nullptr;
+    const Box* colgroup = nullptr;
+    // Table caption is an optional element; if used, it is always the first child of a <table>.
+    if (firstChild->isTableCaption())
+        tableCaption = firstChild;
+    // The <colgroup> must appear after any optional <caption> element but before any <thead>, <th>, <tbody>, <tfoot> and <tr> element.
+    auto* colgroupCandidate = firstChild;
+    if (tableCaption)
+        colgroupCandidate = tableCaption->nextSibling();
+    if (colgroupCandidate->isTableColumnGroup())
+        colgroup = colgroupCandidate;
+
+    if (colgroup) {
+        auto& columns = tableGrid->columns();
+        for (auto* column = downcast<ContainerBox>(*colgroup).firstChild(); column; column = column->nextSibling()) {
+            ASSERT(column->isTableColumn());
+            auto columnSpanCount = column->columnSpan();
+            ASSERT(columnSpanCount > 0);
+            while (columnSpanCount--)
+                columns.addColumn(downcast<ContainerBox>(*column));
+        }
+    }
+
+    auto* firstSection = colgroup ? colgroup->nextSibling() : tableCaption ? tableCaption->nextSibling() : firstChild;
+    for (auto* section = firstSection; section; section = section->nextSibling()) {
+        ASSERT(section->isTableHeader() || section->isTableBody() || section->isTableFooter());
+        for (auto* row = downcast<ContainerBox>(*section).firstChild(); row; row = row->nextSibling()) {
+            ASSERT(row->isTableRow());
+            for (auto* cell = downcast<ContainerBox>(*row).firstChild(); cell; cell = cell->nextSibling()) {
+                ASSERT(cell->isTableCell());
+                tableGrid->appendCell(downcast<ContainerBox>(*cell));
+            }
+        }
+    }
+    return tableGrid;
+}
+
+FormattingContext::IntrinsicWidthConstraints TableFormattingContext::computedPreferredWidthForColumns()
+{
+    auto& formattingState = this->formattingState();
+    auto& grid = formattingState.tableGrid();
+    ASSERT(!grid.widthConstraints());
+
+    // Column preferred width computation as follows:
+    // 1. Collect each cells' width constraints
+    // 2. Collect fixed column widths set by <colgroup>'s and <col>s
+    // 3. Find the min/max width for each columns using the cell constraints and the <col> fixed widths but ignore column spans.
+    // 4. Distribute column spanning cells min/max widths.
+    // 5. Add them all up and return the computed min/max widths.
+    for (auto& cell : grid.cells()) {
+        auto& cellBox = cell->box();
+        ASSERT(cellBox.establishesBlockFormattingContext());
+
+        auto intrinsicWidth = formattingState.intrinsicWidthConstraintsForBox(cellBox);
+        if (!intrinsicWidth) {
+            intrinsicWidth = geometry().intrinsicWidthConstraintsForCell(*cell);
+            formattingState.setIntrinsicWidthConstraintsForBox(cellBox, *intrinsicWidth);
+        }
+        // Spanner cells put their intrinsic widths on the initial slots.
+        grid.slot(cell->position())->setWidthConstraints(*intrinsicWidth);
+    }
+
+    // 2. Collect the fixed width <col>s.
+    auto& columnList = grid.columns().list();
+    Vector<Optional<LayoutUnit>> fixedWidthColumns;
+    for (auto& column : columnList) {
+        auto fixedWidth = [&] () -> Optional<LayoutUnit> {
+            auto* columnBox = column.box();
+            if (!columnBox) {
+                // Anoynmous columns don't have associated layout boxes and can't have fixed col size.
+                return { };
+            }
+            if (auto width = columnBox->columnWidth())
+                return width;
+            return geometry().computedColumnWidth(*columnBox);
+        };
+        fixedWidthColumns.append(fixedWidth());
+    }
+
+    Vector<FormattingContext::IntrinsicWidthConstraints> columnIntrinsicWidths(columnList.size());
+    // 3. Collect he min/max width for each column but ignore column spans for now.
+    Vector<SlotPosition> spanningCellPositionList;
+    size_t numberOfActualColumns = 0;
+    for (size_t columnIndex = 0; columnIndex < columnList.size(); ++columnIndex) {
+        auto columnHasNonSpannedCell = false;
+        for (size_t rowIndex = 0; rowIndex < grid.rows().size(); ++rowIndex) {
+            auto& slot = *grid.slot({ columnIndex, rowIndex });
+            if (slot.isColumnSpanned())
+                continue;
+            columnHasNonSpannedCell = true;
+            if (slot.hasColumnSpan()) {
+                spanningCellPositionList.append({ columnIndex, rowIndex });
+                continue;
+            }
+            auto columnFixedWidth = fixedWidthColumns[columnIndex];
+            auto widthConstraints = !columnFixedWidth ? slot.widthConstraints() : FormattingContext::IntrinsicWidthConstraints { *columnFixedWidth, *columnFixedWidth };
+            columnIntrinsicWidths[columnIndex].minimum = std::max(widthConstraints.minimum, columnIntrinsicWidths[columnIndex].minimum);
+            columnIntrinsicWidths[columnIndex].maximum = std::max(widthConstraints.maximum, columnIntrinsicWidths[columnIndex].maximum);
+        }
+        if (columnHasNonSpannedCell)
+            ++numberOfActualColumns;
+    }
+
+    // 4. Distribute the spanning min/max widths.
+    for (auto spanningCellPosition : spanningCellPositionList) {
+        auto& slot = *grid.slot(spanningCellPosition);
+        auto& cell = slot.cell();
+        ASSERT(slot.hasColumnSpan());
+        auto widthConstraintsToDistribute = slot.widthConstraints();
+        for (size_t columnSpanIndex = cell.startColumn(); columnSpanIndex < cell.endColumn(); ++columnSpanIndex)
+            widthConstraintsToDistribute -= columnIntrinsicWidths[columnSpanIndex];
+        // <table style="border-spacing: 50px"><tr><td colspan=2>long long text</td></tr><tr><td>lo</td><td>xt</td><tr></table>
+        // [long long text]
+        // [lo]        [xt]
+        // While it looks like the spanning cell has to distribute all its spanning width, the border-spacing takes most of the space and
+        // no distribution is needed at all.
+        widthConstraintsToDistribute -= (cell.columnSpan() - 1) * grid.horizontalSpacing();
+        // FIXME: Check if fixed width columns should be skipped here.
+        widthConstraintsToDistribute.minimum = std::max(LayoutUnit { }, widthConstraintsToDistribute.minimum / cell.columnSpan());
+        widthConstraintsToDistribute.maximum = std::max(LayoutUnit { }, widthConstraintsToDistribute.maximum / cell.columnSpan());
+        if (widthConstraintsToDistribute.minimum || widthConstraintsToDistribute.maximum) {
+            for (size_t columnSpanIndex = cell.startColumn(); columnSpanIndex < cell.endColumn(); ++columnSpanIndex)
+                columnIntrinsicWidths[columnSpanIndex] += widthConstraintsToDistribute;
+        }
+    }
+
+    // 5. The final table min/max widths is just the accumulated column constraints.
+    auto tableWidthConstraints = IntrinsicWidthConstraints { };
+    for (auto& columnIntrinsicWidth : columnIntrinsicWidths)
+        tableWidthConstraints += columnIntrinsicWidth;
+    // Exapand the preferred width with leading and trailing cell spacing (note that column spanners count as one cell).
+    tableWidthConstraints += (numberOfActualColumns + 1) * grid.horizontalSpacing();
+    return tableWidthConstraints;
+}
+
+void TableFormattingContext::computeAndDistributeExtraSpace(LayoutUnit availableHorizontalSpace, Optional<LayoutUnit> availableVerticalSpace)
+{
+    // Compute and balance the column and row spaces.
+    auto& grid = formattingState().tableGrid();
+    auto& columns = grid.columns().list();
+    auto tableLayout = this->tableLayout();
+
+    // Columns first.
+    auto distributedHorizontalSpaces = tableLayout.distributedHorizontalSpace(availableHorizontalSpace);
+    ASSERT(distributedHorizontalSpaces.size() == columns.size());
+    auto columnLogicalLeft = grid.horizontalSpacing();
+    for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
+        auto& column = columns[columnIndex];
+        column.setLogicalLeft(columnLogicalLeft);
+        column.setLogicalWidth(distributedHorizontalSpaces[columnIndex]);
+        columnLogicalLeft += distributedHorizontalSpaces[columnIndex] + grid.horizontalSpacing();
+    }
+
+    // Rows second.
+    auto& rows = grid.rows().list();
+    for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex) {
+        for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
+            auto& slot = *grid.slot({ columnIndex, rowIndex });
+            if (slot.isRowSpanned())
+                continue;
+            layoutCell(slot.cell(), availableHorizontalSpace);
+            if (slot.hasRowSpan())
+                continue;
+            // The minimum height of a row (without spanning-related height distribution) is defined as the height of an hypothetical
+            // linebox containing the cells originating in the row.
+            auto& cell = slot.cell();
+            cell.setBaseline(geometry().usedBaselineForCell(cell.box()));
+        }
+    }
+
+    auto distributedVerticalSpaces = tableLayout.distributedVerticalSpace(availableVerticalSpace);
+    ASSERT(distributedVerticalSpaces.size() == rows.size());
+    auto rowLogicalTop = grid.verticalSpacing();
+    for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex) {
+        auto& row = rows[rowIndex];
+        row.setLogicalHeight(distributedVerticalSpaces[rowIndex]);
+        row.setLogicalTop(rowLogicalTop);
+        rowLogicalTop += distributedVerticalSpaces[rowIndex] + grid.verticalSpacing();
+    }
+}
+
+}
+}
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextstableTableFormattingContexthfromrev276885trunkSourceWebCorelayoutformattingContextstableformattingTableFormattingContexth"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingContext.h (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/tableformatting/TableFormattingContext.h) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingContext.h                            (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingContext.h       2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,126 @@
</span><ins>+/*
+ * 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 "FormattingContext.h"
+#include "TableFormattingState.h"
+#include "TableGrid.h"
+#include <wtf/IsoMalloc.h>
+#include <wtf/UniqueRef.h>
+
+namespace WebCore {
+namespace Layout {
+
+class InvalidationState;
+// This class implements the layout logic for table formatting contexts.
+// https://www.w3.org/TR/CSS22/tables.html
+class TableFormattingContext final : public FormattingContext {
+    WTF_MAKE_ISO_ALLOCATED(TableFormattingContext);
+public:
+    TableFormattingContext(const ContainerBox& formattingContextRoot, TableFormattingState&);
+    void layoutInFlowContent(InvalidationState&, const ConstraintsForInFlowContent&) override;
+    LayoutUnit usedContentHeight() const override;
+
+    static UniqueRef<TableGrid> ensureTableGrid(const ContainerBox& tableBox);
+
+private:
+    class TableLayout {
+    public:
+        TableLayout(const TableFormattingContext&, const TableGrid&);
+
+        using DistributedSpaces = Vector<LayoutUnit>;
+        DistributedSpaces distributedHorizontalSpace(LayoutUnit availableHorizontalSpace);
+        DistributedSpaces distributedVerticalSpace(Optional<LayoutUnit> availableVerticalSpace);
+
+    private:
+        const TableFormattingContext& formattingContext() const { return m_formattingContext; }
+
+        const TableFormattingContext& m_formattingContext;
+        const TableGrid& m_grid;
+    };
+
+    class Geometry : public FormattingContext::Geometry {
+    public:
+        LayoutUnit cellHeigh(const ContainerBox&) const;
+        Edges computedCellBorder(const TableGrid::Cell&) const;
+        Optional<LayoutUnit> computedColumnWidth(const ContainerBox& columnBox);
+        FormattingContext::IntrinsicWidthConstraints intrinsicWidthConstraintsForCell(const TableGrid::Cell&);
+        InlineLayoutUnit usedBaselineForCell(const ContainerBox& cellBox);
+
+    private:
+        friend class TableFormattingContext;
+        Geometry(const TableFormattingContext&, const TableGrid&);
+
+        const TableFormattingContext& formattingContext() const { return downcast<TableFormattingContext>(FormattingContext::Geometry::formattingContext()); }
+        const TableGrid& m_grid;
+    };
+    TableFormattingContext::Geometry geometry() const { return Geometry(*this, formattingState().tableGrid()); }
+
+    class Quirks : public FormattingContext::Quirks {
+    public:
+        Quirks(const TableFormattingContext&);
+
+        bool shouldIgnoreChildContentVerticalMargin(const ContainerBox&) const;
+
+        const TableFormattingContext& formattingContext() const { return downcast<TableFormattingContext>(FormattingContext::Quirks::formattingContext()); }
+        TableFormattingContext::Geometry geometry() const { return formattingContext().geometry(); }
+    };
+    TableFormattingContext::Quirks quirks() const { return Quirks(*this); }
+
+    TableFormattingContext::TableLayout tableLayout() const { return TableLayout(*this, formattingState().tableGrid()); }
+
+    IntrinsicWidthConstraints computedIntrinsicWidthConstraints() override;
+    void layoutCell(const TableGrid::Cell&, LayoutUnit availableHorizontalSpace, Optional<LayoutUnit> usedCellHeight = WTF::nullopt);
+    void setUsedGeometryForCells(LayoutUnit availableHorizontalSpace);
+    void setUsedGeometryForRows(LayoutUnit availableHorizontalSpace);
+    void setUsedGeometryForSections(const ConstraintsForInFlowContent&);
+
+    IntrinsicWidthConstraints computedPreferredWidthForColumns();
+    void computeAndDistributeExtraSpace(LayoutUnit availableHorizontalSpace, Optional<LayoutUnit> availableVerticalSpace);
+
+    const TableFormattingState& formattingState() const { return downcast<TableFormattingState>(FormattingContext::formattingState()); }
+    TableFormattingState& formattingState() { return downcast<TableFormattingState>(FormattingContext::formattingState()); }
+};
+
+inline TableFormattingContext::Geometry::Geometry(const TableFormattingContext& tableFormattingContext, const TableGrid& grid)
+    : FormattingContext::Geometry(tableFormattingContext)
+    , m_grid(grid)
+{
+}
+
+inline TableFormattingContext::Quirks::Quirks(const TableFormattingContext& tableFormattingContext)
+    : FormattingContext::Quirks(tableFormattingContext)
+{
+}
+
+}
+}
+
+SPECIALIZE_TYPE_TRAITS_LAYOUT_FORMATTING_CONTEXT(TableFormattingContext, isTableFormattingContext())
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextstableTableFormattingContextGeometrycppfromrev276885trunkSourceWebCorelayoutformattingContextstableformattingTableFormattingContextGeometrycpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingContextGeometry.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/tableformatting/TableFormattingContextGeometry.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingContextGeometry.cpp                          (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingContextGeometry.cpp     2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,165 @@
</span><ins>+/*
+ * 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 "TableFormattingContext.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "InlineFormattingState.h"
+#include "LayoutBoxGeometry.h"
+#include "LayoutContext.h"
+#include "LayoutDescendantIterator.h"
+#include "LayoutInitialContainingBlock.h"
+#include "TableFormattingState.h"
+
+namespace WebCore {
+namespace Layout {
+
+LayoutUnit TableFormattingContext::Geometry::cellHeigh(const ContainerBox& cellBox) const
+{
+    ASSERT(cellBox.isInFlow());
+    auto contentHeight = LayoutUnit { };
+    if (formattingContext().quirks().shouldIgnoreChildContentVerticalMargin(cellBox)) {
+        ASSERT(cellBox.firstInFlowChild());
+        auto formattingContext = this->formattingContext();
+        auto& firstInFlowChild = *cellBox.firstInFlowChild();
+        auto& lastInFlowChild = *cellBox.lastInFlowChild();
+        auto& firstInFlowChildGeometry = formattingContext.geometryForBox(firstInFlowChild, EscapeReason::TableQuirkNeedsGeometryFromEstablishedFormattingContext);
+        auto& lastInFlowChildGeometry = formattingContext.geometryForBox(lastInFlowChild, EscapeReason::TableQuirkNeedsGeometryFromEstablishedFormattingContext);
+
+        auto top = firstInFlowChild.style().hasMarginBeforeQuirk() ? BoxGeometry::borderBoxRect(firstInFlowChildGeometry).top() : BoxGeometry::marginBoxRect(firstInFlowChildGeometry).top();
+        auto bottom = lastInFlowChild.style().hasMarginAfterQuirk() ? BoxGeometry::borderBoxRect(lastInFlowChildGeometry).bottom() : BoxGeometry::marginBoxRect(lastInFlowChildGeometry).bottom();
+        contentHeight = bottom - top;
+    } else
+        contentHeight = contentHeightForFormattingContextRoot(cellBox);
+    return std::max(computedHeight(cellBox).valueOr(0_lu), contentHeight);
+}
+
+Edges TableFormattingContext::Geometry::computedCellBorder(const TableGrid::Cell& cell) const
+{
+    auto& cellBox = cell.box();
+    auto border = computedBorder(cellBox);
+    auto collapsedBorder = m_grid.collapsedBorder();
+    if (!collapsedBorder)
+        return border;
+
+    // We might want to cache these collapsed borders on the grid.
+    auto cellPosition = cell.position();
+    // Collapsed border left from table and adjacent cells.
+    if (!cellPosition.column)
+        border.horizontal.left = collapsedBorder->horizontal.left / 2;
+    else {
+        auto adjacentBorderRight = computedBorder(m_grid.slot({ cellPosition.column - 1, cellPosition.row })->cell().box()).horizontal.right;
+        border.horizontal.left = std::max(border.horizontal.left, adjacentBorderRight) / 2;
+    }
+    // Collapsed border right from table and adjacent cells.
+    if (cellPosition.column == m_grid.columns().size() - 1)
+        border.horizontal.right = collapsedBorder->horizontal.right / 2;
+    else {
+        auto adjacentBorderLeft = computedBorder(m_grid.slot({ cellPosition.column + 1, cellPosition.row })->cell().box()).horizontal.left;
+        border.horizontal.right = std::max(border.horizontal.right, adjacentBorderLeft) / 2;
+    }
+    // Collapsed border top from table, row and adjacent cells.
+    auto& rows = m_grid.rows().list();
+    if (!cellPosition.row)
+        border.vertical.top = collapsedBorder->vertical.top / 2;
+    else {
+        auto adjacentBorderBottom = computedBorder(m_grid.slot({ cellPosition.column, cellPosition.row - 1 })->cell().box()).vertical.bottom;
+        auto adjacentRowBottom = computedBorder(rows[cellPosition.row - 1].box()).vertical.bottom;
+        auto adjacentCollapsedBorder = std::max(adjacentBorderBottom, adjacentRowBottom);
+        border.vertical.top = std::max(border.vertical.top, adjacentCollapsedBorder) / 2;
+    }
+    // Collapsed border bottom from table, row and adjacent cells.
+    if (cellPosition.row == m_grid.rows().size() - 1)
+        border.vertical.bottom = collapsedBorder->vertical.bottom / 2;
+    else {
+        auto adjacentBorderTop = computedBorder(m_grid.slot({ cellPosition.column, cellPosition.row + 1 })->cell().box()).vertical.top;
+        auto adjacentRowTop = computedBorder(rows[cellPosition.row + 1].box()).vertical.top;
+        auto adjacentCollapsedBorder = std::max(adjacentBorderTop, adjacentRowTop);
+        border.vertical.bottom = std::max(border.vertical.bottom, adjacentCollapsedBorder) / 2;
+    }
+    return border;
+}
+
+Optional<LayoutUnit> TableFormattingContext::Geometry::computedColumnWidth(const ContainerBox& columnBox)
+{
+    // Check both style and <col>'s width attribute.
+    // FIXME: Figure out what to do with calculated values, like <col style="width: 10%">.
+    if (auto computedWidthValue = computedWidth(columnBox, { }))
+        return computedWidthValue;
+    return columnBox.columnWidth();
+}
+
+FormattingContext::IntrinsicWidthConstraints TableFormattingContext::Geometry::intrinsicWidthConstraintsForCell(const TableGrid::Cell& cell)
+{
+    auto& cellBox = cell.box();
+    auto& style = cellBox.style();
+
+    auto computedIntrinsicWidthConstraints = [&]() -> FormattingContext::IntrinsicWidthConstraints {
+        // Even fixed width cells expand to their minimum content width
+        // <td style="width: 10px">test_content</td> will size to max(minimum content width, computed width).
+        auto intrinsicWidthConstraints = FormattingContext::IntrinsicWidthConstraints { };
+        if (cellBox.hasChild())
+            intrinsicWidthConstraints = LayoutContext::createFormattingContext(cellBox, layoutState())->computedIntrinsicWidthConstraints();
+        if (auto fixedWidth = fixedValue(style.logicalWidth()))
+            return { std::max(intrinsicWidthConstraints.minimum, *fixedWidth), std::max(intrinsicWidthConstraints.minimum, *fixedWidth) };
+        return intrinsicWidthConstraints;
+    };
+    // FIXME Check for box-sizing: border-box;
+    auto intrinsicWidthConstraints = constrainByMinMaxWidth(cellBox, computedIntrinsicWidthConstraints());
+    // Expand with border
+    intrinsicWidthConstraints.expand(computedCellBorder(cell).width());
+    // padding
+    intrinsicWidthConstraints.expand(fixedValue(style.paddingLeft()).valueOr(0) + fixedValue(style.paddingRight()).valueOr(0));
+    // and margin
+    intrinsicWidthConstraints.expand(fixedValue(style.marginStart()).valueOr(0) + fixedValue(style.marginEnd()).valueOr(0));
+    return intrinsicWidthConstraints;
+}
+
+
+InlineLayoutUnit TableFormattingContext::Geometry::usedBaselineForCell(const ContainerBox& cellBox)
+{
+    // The baseline of a cell is defined as the baseline of the first in-flow line box in the cell,
+    // or the first in-flow table-row in the cell, whichever comes first.
+    // If there is no such line box, the baseline is the bottom of content edge of the cell box.
+    if (cellBox.establishesInlineFormattingContext())
+        return layoutState().establishedInlineFormattingState(cellBox).lines()[0].baseline();
+    for (auto& cellDescendant : descendantsOfType<ContainerBox>(cellBox)) {
+        if (cellDescendant.establishesInlineFormattingContext()) {
+            auto& inlineFormattingStateForCell = layoutState().establishedInlineFormattingState(cellDescendant);
+            if (!inlineFormattingStateForCell.lines().isEmpty())
+                return inlineFormattingStateForCell.lines()[0].baseline();
+        }
+        if (cellDescendant.establishesTableFormattingContext())
+            return layoutState().establishedTableFormattingState(cellDescendant).tableGrid().rows().list()[0].baseline();
+    }
+    return formattingContext().geometryForBox(cellBox).contentBoxBottom();
+}
+
+}
+}
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextstableTableFormattingContextQuirkscppfromrev276885trunkSourceWebCorelayoutformattingContextstableformattingTableFormattingContextQuirkscpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingContextQuirks.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/tableformatting/TableFormattingContextQuirks.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingContextQuirks.cpp                            (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingContextQuirks.cpp       2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,57 @@
</span><ins>+/*
+ * Copyright (C) 2020 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 "TableFormattingContext.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "LayoutBox.h"
+#include "LayoutContainerBox.h"
+#include "LayoutState.h"
+
+namespace WebCore {
+namespace Layout {
+
+bool TableFormattingContext::Quirks::shouldIgnoreChildContentVerticalMargin(const ContainerBox& cellBox) const
+{
+    // Normally BFC root content height takes the margin box of the child content as vertical margins don't collapse with BFC roots,
+    // but table cell boxes do collapse their (non-existing) margins with child quirk margins (so much quirk), so here we check
+    // if the content height should include margins or not.
+    // e.g <table><tr><td><p>text content</td></tr></table> <- <p>'s quirk margin collapses with the <td> so its content
+    // height should not include vertical margins.
+    if (!layoutState().inQuirksMode())
+        return false;
+    if (cellBox.establishesInlineFormattingContext())
+        return false;
+    if (!cellBox.hasInFlowChild())
+        return false;
+    return cellBox.firstInFlowChild()->style().hasMarginBeforeQuirk() || cellBox.lastInFlowChild()->style().hasMarginAfterQuirk();
+}
+
+}
+}
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextstableTableFormattingStatecppfromrev276885trunkSourceWebCorelayoutformattingContextstableformattingTableFormattingStatecpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingState.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/tableformatting/TableFormattingState.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingState.cpp                            (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingState.cpp       2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,51 @@
</span><ins>+/*
+ * 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 "TableFormattingState.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "TableFormattingContext.h"
+#include <wtf/IsoMallocInlines.h>
+
+namespace WebCore {
+namespace Layout {
+
+WTF_MAKE_ISO_ALLOCATED_IMPL(TableFormattingState);
+
+TableFormattingState::TableFormattingState(Ref<FloatingState>&& floatingState, LayoutState& layoutState, const ContainerBox& tableBox)
+    : FormattingState(WTFMove(floatingState), Type::Table, layoutState)
+    , m_tableGrid(TableFormattingContext::ensureTableGrid(tableBox))
+{
+}
+
+TableFormattingState::~TableFormattingState()
+{
+}
+
+}
+}
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextstableTableFormattingStatehfromrev276885trunkSourceWebCorelayoutformattingContextstableformattingTableFormattingStateh"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingState.h (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/tableformatting/TableFormattingState.h) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingState.h                              (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingState.h 2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,57 @@
</span><ins>+/*
+ * 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 "FormattingState.h"
+#include "TableGrid.h"
+#include <wtf/IsoMalloc.h>
+#include <wtf/UniqueRef.h>
+
+namespace WebCore {
+namespace Layout {
+
+// TableFormattingState holds the state for a particular table formatting context tree.
+class TableFormattingState : public FormattingState {
+    WTF_MAKE_ISO_ALLOCATED(TableFormattingState);
+public:
+    TableFormattingState(Ref<FloatingState>&&, LayoutState&, const ContainerBox& tableBox);
+    ~TableFormattingState();
+
+    TableGrid& tableGrid() { return m_tableGrid; }
+    const TableGrid& tableGrid() const { return m_tableGrid; }
+
+private:
+    UniqueRef<TableGrid> m_tableGrid;
+};
+
+}
+}
+
+SPECIALIZE_TYPE_TRAITS_LAYOUT_FORMATTING_STATE(TableFormattingState, isTableFormattingState())
+
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextstableTableGridcppfromrev276885trunkSourceWebCorelayoutformattingContextstableformattingTableGridcpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/table/TableGrid.cpp (from rev 276885, trunk/Source/WebCore/layout/formattingContexts/tableformatting/TableGrid.cpp) (0 => 276886)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/table/TableGrid.cpp                               (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/table/TableGrid.cpp  2021-05-02 16:59:56 UTC (rev 276886)
</span><span class="lines">@@ -0,0 +1,197 @@
</span><ins>+/*
+ * 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 "TableGrid.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include <wtf/IsoMallocInlines.h>
+
+namespace WebCore {
+namespace Layout {
+
+WTF_MAKE_ISO_ALLOCATED_IMPL(TableGrid);
+
+TableGrid::Column::Column(const ContainerBox* columnBox)
+    : m_layoutBox(makeWeakPtr(columnBox))
+{
+}
+
+void TableGrid::Column::setLogicalWidth(LayoutUnit computedLogicalWidth)
+{
+#if ASSERT_ENABLED
+    m_hasComputedWidth = true;
+#endif
+    m_computedLogicalWidth = computedLogicalWidth;
+}
+
+LayoutUnit TableGrid::Column::logicalWidth() const
+{
+    ASSERT(m_hasComputedWidth);
+    return m_computedLogicalWidth;
+}
+
+void TableGrid::Column::setLogicalLeft(LayoutUnit computedLogicalLeft)
+{
+#if ASSERT_ENABLED
+    m_hasComputedLeft = true;
+#endif
+    m_computedLogicalLeft = computedLogicalLeft;
+}
+
+LayoutUnit TableGrid::Column::logicalLeft() const
+{
+    ASSERT(m_hasComputedLeft);
+    return m_computedLogicalLeft;
+}
+
+bool TableGrid::Column::isFixedWidth() const
+{
+    return hasFixedWidthCell() || (box() && box()->columnWidth());
+}
+
+void TableGrid::Columns::addColumn(const ContainerBox& columnBox)
+{
+    m_columnList.append({ &columnBox });
+}
+
+void TableGrid::Columns::addAnonymousColumn()
+{
+    m_columnList.append({ nullptr });
+}
+
+bool TableGrid::Columns::hasFixedColumnsOnly() const
+{
+    for (auto& column : m_columnList) {
+        if (!column.isFixedWidth())
+            return false;
+    }
+    return true;
+}
+
+void TableGrid::Rows::addRow(const ContainerBox& rowBox)
+{
+    m_rowList.append({ rowBox });
+}
+
+TableGrid::Row::Row(const ContainerBox& rowBox)
+    : m_layoutBox(makeWeakPtr(rowBox))
+{
+}
+
+TableGrid::Cell::Cell(const ContainerBox& cellBox, SlotPosition position, CellSpan span)
+    : m_layoutBox(makeWeakPtr(cellBox))
+    , m_position(pos