<!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>[284678] 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/284678">284678</a></dd>
<dt>Author</dt> <dd>zalan@apple.com</dd>
<dt>Date</dt> <dd>2021-10-22 06:44:00 -0700 (Fri, 22 Oct 2021)</dd>
</dl>

<h3>Log Message</h3>
<pre>[LFC][IFC] Add support for checking leading collapsible whitespace in InlineContentBreaker::processOverflowingContent
https://bugs.webkit.org/show_bug.cgi?id=232091

Reviewed by Antti Koivisto.

This patch ensures that the leading collapsible whitespace on the continuous run is taken
into account when checking if the otherwise overflowing content may fit the line.
e.g.
<span style="white-space: nowrap">no_wrap </span><span> yes_wrap</span>
The " yes_wrap" content has a collapsible leading whitespace here.
(This is also a preparation for fixing a regression and a WPT test.)

* layout/formattingContexts/inline/InlineContentBreaker.cpp:
(WebCore::Layout::isVisuallyEmptyWhitespaceContent):
(WebCore::Layout::InlineContentBreaker::processOverflowingContent const):
(WebCore::Layout::InlineContentBreaker::ContinuousContent::append):
(WebCore::Layout::InlineContentBreaker::ContinuousContent::reset):
* layout/formattingContexts/inline/InlineContentBreaker.h:
(WebCore::Layout::InlineContentBreaker::ContinuousContent::leadingCollapsibleWidth const):
(WebCore::Layout::InlineContentBreaker::ContinuousContent::trailingCollapsibleWidth const):
(WebCore::Layout::InlineContentBreaker::ContinuousContent::hasCollapsibleContent const):
(WebCore::Layout::InlineContentBreaker::ContinuousContent::isFullyCollapsible const):
(WebCore::Layout::InlineContentBreaker::ContinuousContent::collapsibleLogicalWidth const): Deleted.
(WebCore::Layout::InlineContentBreaker::ContinuousContent::nonCollapsibleLogicalWidth const): Deleted.
(WebCore::Layout::InlineContentBreaker::ContinuousContent::hasTrailingCollapsibleContent const): Deleted.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineContentBreakercpp">trunk/Source/WebCore/layout/formattingContexts/inline/InlineContentBreaker.cpp</a></li>
<li><a href="#trunkSourceWebCorelayoutformattingContextsinlineInlineContentBreakerh">trunk/Source/WebCore/layout/formattingContexts/inline/InlineContentBreaker.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (284677 => 284678)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog   2021-10-22 13:24:11 UTC (rev 284677)
+++ trunk/Source/WebCore/ChangeLog      2021-10-22 13:44:00 UTC (rev 284678)
</span><span class="lines">@@ -1,5 +1,33 @@
</span><span class="cx"> 2021-10-22  Alan Bujtas  <zalan@apple.com>
</span><span class="cx"> 
</span><ins>+        [LFC][IFC] Add support for checking leading collapsible whitespace in InlineContentBreaker::processOverflowingContent
+        https://bugs.webkit.org/show_bug.cgi?id=232091
+
+        Reviewed by Antti Koivisto.
+
+        This patch ensures that the leading collapsible whitespace on the continuous run is taken
+        into account when checking if the otherwise overflowing content may fit the line.
+        e.g.
+        <span style="white-space: nowrap">no_wrap </span><span> yes_wrap</span>
+        The " yes_wrap" content has a collapsible leading whitespace here.
+        (This is also a preparation for fixing a regression and a WPT test.)
+
+        * layout/formattingContexts/inline/InlineContentBreaker.cpp:
+        (WebCore::Layout::isVisuallyEmptyWhitespaceContent):
+        (WebCore::Layout::InlineContentBreaker::processOverflowingContent const):
+        (WebCore::Layout::InlineContentBreaker::ContinuousContent::append):
+        (WebCore::Layout::InlineContentBreaker::ContinuousContent::reset):
+        * layout/formattingContexts/inline/InlineContentBreaker.h:
+        (WebCore::Layout::InlineContentBreaker::ContinuousContent::leadingCollapsibleWidth const):
+        (WebCore::Layout::InlineContentBreaker::ContinuousContent::trailingCollapsibleWidth const):
+        (WebCore::Layout::InlineContentBreaker::ContinuousContent::hasCollapsibleContent const):
+        (WebCore::Layout::InlineContentBreaker::ContinuousContent::isFullyCollapsible const):
+        (WebCore::Layout::InlineContentBreaker::ContinuousContent::collapsibleLogicalWidth const): Deleted.
+        (WebCore::Layout::InlineContentBreaker::ContinuousContent::nonCollapsibleLogicalWidth const): Deleted.
+        (WebCore::Layout::InlineContentBreaker::ContinuousContent::hasTrailingCollapsibleContent const): Deleted.
+
+2021-10-22  Alan Bujtas  <zalan@apple.com>
+
</ins><span class="cx">         FontCascade::widthForSimpleText fails to produce matching measured width for monospace font
</span><span class="cx">         https://bugs.webkit.org/show_bug.cgi?id=232104
</span><span class="cx">         <rdar://83991027>
</span></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineContentBreakercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/layout/formattingContexts/inline/InlineContentBreaker.cpp (284677 => 284678)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineContentBreaker.cpp   2021-10-22 13:24:11 UTC (rev 284677)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineContentBreaker.cpp      2021-10-22 13:44:00 UTC (rev 284678)
</span><span class="lines">@@ -97,15 +97,19 @@
</span><span class="cx"> {
</span><span class="cx">     // [<span></span> ] [<span> </span>] [ <span style="padding: 0px;"></span>] are all considered visually empty whitespace content.
</span><span class="cx">     // [<span style="border: 1px solid red"></span> ] while this is whitespace content only, it is not considered visually empty.
</span><del>-    // Due to commit boundary rules, we just need to check the first non-typeless inline item (can't have both [img] and [text])
</del><ins>+    ASSERT(!continuousContent.runs().isEmpty());
+    auto hasWhitespace = false;
</ins><span class="cx">     for (auto& run : continuousContent.runs()) {
</span><span class="cx">         auto& inlineItem = run.inlineItem;
</span><del>-        // FIXME: check for padding border etc.
</del><ins>+        // FIXME: check if visual decoration makes a difference here e.g. padding border.
</ins><span class="cx">         if (inlineItem.isInlineBoxStart() || inlineItem.isInlineBoxEnd())
</span><span class="cx">             continue;
</span><del>-        return inlineItem.isText() && downcast<InlineTextItem>(inlineItem).isWhitespace();
</del><ins>+        auto isWhitespace = inlineItem.isText() && downcast<InlineTextItem>(inlineItem).isWhitespace();
+        if (!isWhitespace)
+            return false;
+        hasWhitespace = true;
</ins><span class="cx">     }
</span><del>-    return false;
</del><ins>+    return hasWhitespace;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static inline bool isNonContentRunsOnly(const InlineContentBreaker::ContinuousContent& continuousContent)
</span><span class="lines">@@ -173,16 +177,17 @@
</span><span class="cx">     ASSERT(!continuousContent.runs().isEmpty());
</span><span class="cx"> 
</span><span class="cx">     ASSERT(continuousContent.logicalWidth() > lineStatus.availableWidth);
</span><del>-    if (continuousContent.hasTrailingCollapsibleContent()) {
-        ASSERT(hasTrailingTextContent(overflowContent));
-        // First check if the content fits without the trailing collapsible part.
-        if (continuousContent.nonCollapsibleLogicalWidth() <= lineStatus.availableWidth)
-            return { Result::Action::Keep, IsEndOfLine::No };
-        // Now check if we can trim the line too.
-        if (lineStatus.hasFullyCollapsibleTrailingRun && continuousContent.isFullyCollapsible()) {
</del><ins>+    if (continuousContent.hasCollapsibleContent()) {
+        if (lineStatus.hasFullyCollapsibleTrailingContent && continuousContent.isFullyCollapsible()) {
</ins><span class="cx">             // If this new content is fully collapsible, it should surely fit.
</span><span class="cx">             return { Result::Action::Keep, IsEndOfLine::No };
</span><span class="cx">         }
</span><ins>+        // Check if the content fits if we collapsed it.
+        auto spaceRequired = continuousContent.logicalWidth() - continuousContent.trailingCollapsibleWidth();
+        if (lineStatus.hasFullyCollapsibleTrailingContent)
+            spaceRequired -= continuousContent.leadingCollapsibleWidth();
+        if (spaceRequired <= lineStatus.availableWidth)
+            return { Result::Action::Keep, IsEndOfLine::No };
</ins><span class="cx">     } else if (lineStatus.collapsibleWidth && isNonContentRunsOnly(continuousContent)) {
</span><span class="cx">         // Let's see if the non-content runs fit when the line has trailing collapsible content.
</span><span class="cx">         // "text content <span style="padding: 1px"></span>" <- the <span></span> runs could fit after collapsing the trailing whitespace.
</span><span class="lines">@@ -649,29 +654,30 @@
</span><span class="cx"> void InlineContentBreaker::ContinuousContent::append(const InlineItem& inlineItem, const RenderStyle& style, InlineLayoutUnit logicalWidth, std::optional<InlineLayoutUnit> collapsibleWidth)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(inlineItem.isText() || inlineItem.isBox() || inlineItem.isInlineBoxStart() || inlineItem.isInlineBoxEnd());
</span><ins>+    auto isLeadingCollapsible = collapsibleWidth && (m_runs.isEmpty() || isFullyCollapsible());
</ins><span class="cx">     m_runs.append({ inlineItem, style, logicalWidth });
</span><span class="cx">     m_logicalWidth = clampTo<InlineLayoutUnit>(m_logicalWidth + logicalWidth);
</span><span class="cx">     if (!collapsibleWidth) {
</span><span class="cx">         if (inlineItem.isText() || inlineItem.isBox()) {
</span><span class="cx">             // Inline boxes do not prevent the trailing content from getting collapsed.
</span><del>-            m_collapsibleLogicalWidth = { };
</del><ins>+            m_trailingCollapsibleWidth = { };
</ins><span class="cx">         }
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx">     ASSERT(*collapsibleWidth <= logicalWidth);
</span><del>-    if (*collapsibleWidth == logicalWidth) {
-        // Fully collapsible run.
-        m_collapsibleLogicalWidth += logicalWidth;
</del><ins>+    if (isLeadingCollapsible) {
+        ASSERT(!m_trailingCollapsibleWidth);
+        m_leadingCollapsibleWidth += *collapsibleWidth;
</ins><span class="cx">         return;
</span><span class="cx">     }
</span><del>-    // Partially collapsible run.
-    m_collapsibleLogicalWidth = *collapsibleWidth;
</del><ins>+    m_trailingCollapsibleWidth = *collapsibleWidth == logicalWidth ? m_trailingCollapsibleWidth + logicalWidth : *collapsibleWidth;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void InlineContentBreaker::ContinuousContent::reset()
</span><span class="cx"> {
</span><span class="cx">     m_logicalWidth = { };
</span><del>-    m_collapsibleLogicalWidth = { };
</del><ins>+    m_leadingCollapsibleWidth = { };
+    m_trailingCollapsibleWidth = { };
</ins><span class="cx">     m_runs.clear();
</span><span class="cx"> }
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCorelayoutformattingContextsinlineInlineContentBreakerh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/layout/formattingContexts/inline/InlineContentBreaker.h (284677 => 284678)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineContentBreaker.h     2021-10-22 13:24:11 UTC (rev 284677)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineContentBreaker.h        2021-10-22 13:44:00 UTC (rev 284678)
</span><span class="lines">@@ -77,12 +77,12 @@
</span><span class="cx">     // see https://drafts.csswg.org/css-text-3/#line-break-details
</span><span class="cx">     struct ContinuousContent {
</span><span class="cx">         InlineLayoutUnit logicalWidth() const { return m_logicalWidth; }
</span><del>-        InlineLayoutUnit collapsibleLogicalWidth() const { return m_collapsibleLogicalWidth; }
-        InlineLayoutUnit nonCollapsibleLogicalWidth() const { return logicalWidth() - collapsibleLogicalWidth(); }
-        bool hasTrailingCollapsibleContent() const { return !!collapsibleLogicalWidth(); }
-        bool isFullyCollapsible() const { return logicalWidth() == collapsibleLogicalWidth(); }
</del><ins>+        InlineLayoutUnit leadingCollapsibleWidth() const { return m_leadingCollapsibleWidth; }
+        InlineLayoutUnit trailingCollapsibleWidth() const { return m_trailingCollapsibleWidth; }
+        bool hasCollapsibleContent() const { return trailingCollapsibleWidth() > 0 || leadingCollapsibleWidth() > 0; }
+        bool isFullyCollapsible() const { return logicalWidth() == trailingCollapsibleWidth() + leadingCollapsibleWidth(); }
</ins><span class="cx"> 
</span><del>-        void append(const InlineItem&, const RenderStyle&, InlineLayoutUnit logicalWidth, std::optional<InlineLayoutUnit> collapsibleWidth);
</del><ins>+        void append(const InlineItem&, const RenderStyle&, InlineLayoutUnit logicalWidth, std::optional<InlineLayoutUnit> collapsibleWidth = std::nullopt);
</ins><span class="cx">         void reset();
</span><span class="cx"> 
</span><span class="cx">         struct Run {
</span><span class="lines">@@ -100,7 +100,8 @@
</span><span class="cx">     private:
</span><span class="cx">         RunList m_runs;
</span><span class="cx">         InlineLayoutUnit m_logicalWidth { 0 };
</span><del>-        InlineLayoutUnit m_collapsibleLogicalWidth { 0 };
</del><ins>+        InlineLayoutUnit m_leadingCollapsibleWidth { 0 };
+        InlineLayoutUnit m_trailingCollapsibleWidth { 0 };
</ins><span class="cx">     };
</span><span class="cx"> 
</span><span class="cx">     struct LineStatus {
</span><span class="lines">@@ -108,7 +109,7 @@
</span><span class="cx">         InlineLayoutUnit availableWidth { 0 };
</span><span class="cx">         InlineLayoutUnit collapsibleWidth { 0 };
</span><span class="cx">         std::optional<InlineLayoutUnit> trailingSoftHyphenWidth;
</span><del>-        bool hasFullyCollapsibleTrailingRun { false };
</del><ins>+        bool hasFullyCollapsibleTrailingContent { false };
</ins><span class="cx">         bool hasContent { false };
</span><span class="cx">         bool hasWrapOpportunityAtPreviousPosition { false };
</span><span class="cx">     };
</span></span></pre>
</div>
</div>

</body>
</html>