<!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>[277552] 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/277552">277552</a></dd>
<dt>Author</dt> <dd>zalan@apple.com</dd>
<dt>Date</dt> <dd>2021-05-15 16:55:58 -0700 (Sat, 15 May 2021)</dd>
</dl>
<h3>Log Message</h3>
<pre>[LFC] Move inline formatting geometry to its own class
https://bugs.webkit.org/show_bug.cgi?id=225754
Reviewed by Antti Koivisto.
* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:
* layout/formattingContexts/inline/InlineFormattingContext.cpp:
(WebCore::Layout::InlineFormattingContext::geometry const):
* layout/formattingContexts/inline/InlineFormattingContext.h:
(WebCore::Layout::InlineFormattingContext::Geometry::Geometry): Deleted.
* layout/formattingContexts/inline/InlineFormattingContextGeometry.cpp: Removed.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</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>
<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>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineFormattingGeometrycpp">trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingGeometry.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineFormattingGeometryh">trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingGeometry.h</a></li>
</ul>
<h3>Removed Paths</h3>
<ul>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineFormattingContextGeometrycpp">trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContextGeometry.cpp</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (277551 => 277552)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog 2021-05-15 23:54:52 UTC (rev 277551)
+++ trunk/Source/WebCore/ChangeLog 2021-05-15 23:55:58 UTC (rev 277552)
</span><span class="lines">@@ -1,5 +1,20 @@
</span><span class="cx"> 2021-05-15 Alan Bujtas <zalan@apple.com>
</span><span class="cx">
</span><ins>+ [LFC] Move inline formatting geometry to its own class
+ https://bugs.webkit.org/show_bug.cgi?id=225754
+
+ Reviewed by Antti Koivisto.
+
+ * Sources.txt:
+ * WebCore.xcodeproj/project.pbxproj:
+ * layout/formattingContexts/inline/InlineFormattingContext.cpp:
+ (WebCore::Layout::InlineFormattingContext::geometry const):
+ * layout/formattingContexts/inline/InlineFormattingContext.h:
+ (WebCore::Layout::InlineFormattingContext::Geometry::Geometry): Deleted.
+ * layout/formattingContexts/inline/InlineFormattingContextGeometry.cpp: Removed.
+
+2021-05-15 Alan Bujtas <zalan@apple.com>
+
</ins><span class="cx"> [LFC] Move flex formatting geometry to its own class
</span><span class="cx"> https://bugs.webkit.org/show_bug.cgi?id=225755
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceWebCoreSourcestxt"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Sources.txt (277551 => 277552)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Sources.txt 2021-05-15 23:54:52 UTC (rev 277551)
+++ trunk/Source/WebCore/Sources.txt 2021-05-15 23:55:58 UTC (rev 277552)
</span><span class="lines">@@ -1534,7 +1534,7 @@
</span><span class="cx"> layout/floats/FloatingState.cpp
</span><span class="cx"> layout/formattingContexts/inline/InlineContentBreaker.cpp
</span><span class="cx"> layout/formattingContexts/inline/InlineFormattingContext.cpp
</span><del>-layout/formattingContexts/inline/InlineFormattingContextGeometry.cpp
</del><ins>+layout/formattingContexts/inline/InlineFormattingGeometry.cpp
</ins><span class="cx"> layout/formattingContexts/inline/InlineFormattingQuirks.cpp
</span><span class="cx"> layout/formattingContexts/inline/InlineFormattingState.cpp
</span><span class="cx"> layout/formattingContexts/inline/InlineItem.cpp
</span></span></pre></div>
<a id="trunkSourceWebCoreWebCorexcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (277551 => 277552)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2021-05-15 23:54:52 UTC (rev 277551)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2021-05-15 23:55:58 UTC (rev 277552)
</span><span class="lines">@@ -1334,6 +1334,7 @@
</span><span class="cx"> 46F02A1A23737F8300106A64 /* EventLoop.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B0ABCA123679AB300B45085 /* EventLoop.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx"> 46FCB6181A70820E00C5A21E /* DiagnosticLoggingKeys.h in Headers */ = {isa = PBXBuildFile; fileRef = CD37B37515C1A7E1006DC898 /* DiagnosticLoggingKeys.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx"> 4786356526507A3800C5E2E0 /* BlockFormattingGeometry.h in Headers */ = {isa = PBXBuildFile; fileRef = 4786356426507A3700C5E2E0 /* BlockFormattingGeometry.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><ins>+ 47C4D57D26508BCB00C7AB1F /* InlineFormattingGeometry.h in Headers */ = {isa = PBXBuildFile; fileRef = 47C4D57C26508BCA00C7AB1F /* InlineFormattingGeometry.h */; settings = {ATTRIBUTES = (Private, ); }; };
</ins><span class="cx"> 47F947DB26502F2F0087968C /* BlockFormattingQuirks.h in Headers */ = {isa = PBXBuildFile; fileRef = 47F947DA26502F2F0087968C /* BlockFormattingQuirks.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx"> 49291E4B134172C800E753DE /* ImageRenderingMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 49291E4A134172C800E753DE /* ImageRenderingMode.h */; };
</span><span class="cx"> 493E5E0912D6420500020081 /* PlatformCALayerClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 493E5E0812D6420500020081 /* PlatformCALayerClient.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="lines">@@ -8389,6 +8390,7 @@
</span><span class="cx"> 46EFAF101E5FB9E100E7F34B /* LowPowerModeNotifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LowPowerModeNotifier.h; sourceTree = "<group>"; };
</span><span class="cx"> 46F91BC91FCDD0FE001599C3 /* JSWorkerNavigatorCustom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSWorkerNavigatorCustom.cpp; sourceTree = "<group>"; };
</span><span class="cx"> 4786356426507A3700C5E2E0 /* BlockFormattingGeometry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlockFormattingGeometry.h; sourceTree = "<group>"; };
</span><ins>+ 47C4D57C26508BCA00C7AB1F /* InlineFormattingGeometry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InlineFormattingGeometry.h; sourceTree = "<group>"; };
</ins><span class="cx"> 47F947DA26502F2F0087968C /* BlockFormattingQuirks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlockFormattingQuirks.h; sourceTree = "<group>"; };
</span><span class="cx"> 49291E4A134172C800E753DE /* ImageRenderingMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageRenderingMode.h; sourceTree = "<group>"; };
</span><span class="cx"> 493E5E0812D6420500020081 /* PlatformCALayerClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformCALayerClient.h; sourceTree = "<group>"; };
</span><span class="lines">@@ -9964,7 +9966,7 @@
</span><span class="cx"> 6F26BB6923343E5A002F2BEA /* LayoutContext.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = LayoutContext.cpp; sourceTree = "<group>"; };
</span><span class="cx"> 6F26BB6B23343E5B002F2BEA /* LayoutContext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LayoutContext.h; sourceTree = "<group>"; };
</span><span class="cx"> 6F26EB46234004A5006906E2 /* InlineLineBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InlineLineBuilder.h; sourceTree = "<group>"; };
</span><del>- 6F35EFAF2187CBD50044E0F4 /* InlineFormattingContextGeometry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InlineFormattingContextGeometry.cpp; sourceTree = "<group>"; };
</del><ins>+ 6F35EFAF2187CBD50044E0F4 /* InlineFormattingGeometry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InlineFormattingGeometry.cpp; sourceTree = "<group>"; };
</ins><span class="cx"> 6F360E4E23999420001512A7 /* InlineSoftLineBreakItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InlineSoftLineBreakItem.h; sourceTree = "<group>"; };
</span><span class="cx"> 6F3FBA962556E308003530FD /* LayoutIntegrationInlineContentBuilder.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = LayoutIntegrationInlineContentBuilder.cpp; sourceTree = "<group>"; };
</span><span class="cx"> 6F3FBA972556E312003530FD /* LayoutIntegrationInlineContentBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LayoutIntegrationInlineContentBuilder.h; sourceTree = "<group>"; };
</span><span class="lines">@@ -18264,7 +18266,8 @@
</span><span class="cx"> 6FE198152178397C00446F08 /* InlineContentBreaker.h */,
</span><span class="cx"> 6F7CA3C9208C2B2E002F29AB /* InlineFormattingContext.cpp */,
</span><span class="cx"> 6F7CA3C8208C2B2E002F29AB /* InlineFormattingContext.h */,
</span><del>- 6F35EFAF2187CBD50044E0F4 /* InlineFormattingContextGeometry.cpp */,
</del><ins>+ 6F35EFAF2187CBD50044E0F4 /* InlineFormattingGeometry.cpp */,
+ 47C4D57C26508BCA00C7AB1F /* InlineFormattingGeometry.h */,
</ins><span class="cx"> 6F10B08622B8568D0090E69C /* InlineFormattingQuirks.cpp */,
</span><span class="cx"> 6FE636E72647962900F0951E /* InlineFormattingQuirks.h */,
</span><span class="cx"> 115CFA7D208B8E10001E6991 /* InlineFormattingState.cpp */,
</span><span class="lines">@@ -32953,6 +32956,7 @@
</span><span class="cx"> B57CB52E182A3EFC0079A647 /* InlineElementBox.h in Headers */,
</span><span class="cx"> A8CFF5E30A155A05000A4234 /* InlineFlowBox.h in Headers */,
</span><span class="cx"> 6F7CA3CA208C2B2E002F29AB /* InlineFormattingContext.h in Headers */,
</span><ins>+ 47C4D57D26508BCB00C7AB1F /* InlineFormattingGeometry.h in Headers */,
</ins><span class="cx"> 6FE636E82647962900F0951E /* InlineFormattingQuirks.h in Headers */,
</span><span class="cx"> 115CFA7E208B8E10001E6991 /* InlineFormattingState.h in Headers */,
</span><span class="cx"> 6FE7CFA22177EEF2005B1573 /* InlineItem.h in Headers */,
</span></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineFormattingContextcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContext.cpp (277551 => 277552)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContext.cpp 2021-05-15 23:54:52 UTC (rev 277551)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContext.cpp 2021-05-15 23:55:58 UTC (rev 277552)
</span><span class="lines">@@ -30,6 +30,7 @@
</span><span class="cx">
</span><span class="cx"> #include "FloatingContext.h"
</span><span class="cx"> #include "FontCascade.h"
</span><ins>+#include "InlineFormattingGeometry.h"
</ins><span class="cx"> #include "InlineFormattingQuirks.h"
</span><span class="cx"> #include "InlineFormattingState.h"
</span><span class="cx"> #include "InlineLineBox.h"
</span><span class="lines">@@ -606,6 +607,11 @@
</span><span class="cx"> // FIXME: This is also where we would delete inline items if their content changed.
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+InlineFormattingGeometry InlineFormattingContext::geometry() const
+{
+ return InlineFormattingGeometry(*this);
+}
+
</ins><span class="cx"> InlineFormattingQuirks InlineFormattingContext::quirks() const
</span><span class="cx"> {
</span><span class="cx"> return InlineFormattingQuirks(*this);
</span></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineFormattingContexth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContext.h (277551 => 277552)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContext.h 2021-05-15 23:54:52 UTC (rev 277551)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContext.h 2021-05-15 23:55:58 UTC (rev 277552)
</span><span class="lines">@@ -34,6 +34,7 @@
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> namespace Layout {
</span><span class="cx">
</span><ins>+class InlineFormattingGeometry;
</ins><span class="cx"> class InlineFormattingState;
</span><span class="cx"> class InlineFormattingQuirks;
</span><span class="cx"> class InvalidationState;
</span><span class="lines">@@ -58,23 +59,8 @@
</span><span class="cx"> private:
</span><span class="cx"> IntrinsicWidthConstraints computedIntrinsicWidthConstraints() override;
</span><span class="cx">
</span><del>- class Geometry : public FormattingContext::Geometry {
- public:
- LineBox lineBoxForLineContent(const LineBuilder::LineContent&);
- InlineLayoutUnit logicalTopForNextLine(const LineBuilder::LineContent&, InlineLayoutUnit previousLineLogicalBottom, const FloatingContext&) const;
</del><ins>+ InlineFormattingGeometry geometry() const;
</ins><span class="cx">
</span><del>- 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); }
-
</del><span class="cx"> void lineLayout(InlineItems&, LineBuilder::InlineItemRange, const ConstraintsForInFlowContent&);
</span><span class="cx">
</span><span class="cx"> void computeIntrinsicWidthForFormattingRoot(const Box&);
</span><span class="lines">@@ -89,13 +75,8 @@
</span><span class="cx"> void invalidateFormattingState(const InvalidationState&);
</span><span class="cx"> };
</span><span class="cx">
</span><del>-inline InlineFormattingContext::Geometry::Geometry(const InlineFormattingContext& inlineFormattingContext)
- : FormattingContext::Geometry(inlineFormattingContext)
-{
</del><span class="cx"> }
</span><del>-
</del><span class="cx"> }
</span><del>-}
</del><span class="cx">
</span><span class="cx"> SPECIALIZE_TYPE_TRAITS_LAYOUT_FORMATTING_CONTEXT(InlineFormattingContext, isInlineFormattingContext())
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineFormattingContextGeometrycpp"></a>
<div class="delfile"><h4>Deleted: trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContextGeometry.cpp (277551 => 277552)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContextGeometry.cpp 2021-05-15 23:54:52 UTC (rev 277551)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContextGeometry.cpp 2021-05-15 23:55:58 UTC (rev 277552)
</span><span class="lines">@@ -1,727 +0,0 @@
</span><del>-/*
- * 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
</del></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineFormattingGeometrycppfromrev277551trunkSourceWebCorelayoutformattingContextsinlineInlineFormattingContextGeometrycpp"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingGeometry.cpp (from rev 277551, trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContextGeometry.cpp) (0 => 277552)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingGeometry.cpp (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingGeometry.cpp 2021-05-15 23:55:58 UTC (rev 277552)
</span><span class="lines">@@ -0,0 +1,732 @@
</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 "InlineFormattingGeometry.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());
+}
+
+InlineFormattingGeometry::InlineFormattingGeometry(const InlineFormattingContext& inlineFormattingContext)
+ : FormattingContext::Geometry(inlineFormattingContext)
+{
+}
+
+LineBox InlineFormattingGeometry::lineBoxForLineContent(const LineBuilder::LineContent& lineContent)
+{
+ return LineBoxBuilder(formattingContext()).build(lineContent);
+}
+
+InlineLayoutUnit InlineFormattingGeometry::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 InlineFormattingGeometry::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 InlineFormattingGeometry::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="trunkSourceWebCorelayoutformattingContextsinlineInlineFormattingGeometryh"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingGeometry.h (0 => 277552)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingGeometry.h (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingGeometry.h 2021-05-15 23:55:58 UTC (rev 277552)
</span><span class="lines">@@ -0,0 +1,54 @@
</span><ins>+/*
+ * Copyright (C) 2021 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"
+
+namespace WebCore {
+namespace Layout {
+
+class InlineFormattingGeometry : 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;
+ InlineFormattingGeometry(const InlineFormattingContext&);
+
+ const InlineFormattingContext& formattingContext() const { return downcast<InlineFormattingContext>(FormattingContext::Geometry::formattingContext()); }
+
+};
+
+}
+}
+
+#endif
</ins></span></pre>
</div>
</div>
</body>
</html>