Bug 1731120 - Make truly-empty textframes return true for IsEmpty even when WhiteSpaceIsSignificant, unless they're editable or in an <input> element. r=emilio
authorJonathan Kew <jkew@mozilla.com>
Mon, 11 Oct 2021 12:54:44 +0000
changeset 595339 49f06640fdee2aa53ce8d9f0bc5959f95f376e88
parent 595338 df62726f3d16bb27d0c408e204151148035871dd
child 595340 b3e0e27d7ba7127694a955b5e5e59a0afb7f95ce
push id38868
push usermlaza@mozilla.com
push dateMon, 11 Oct 2021 21:46:13 +0000
treeherdermozilla-central@32a3cf57dd43 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs1731120
milestone95.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1731120 - Make truly-empty textframes return true for IsEmpty even when WhiteSpaceIsSignificant, unless they're editable or in an <input> element. r=emilio Differential Revision: https://phabricator.services.mozilla.com/D128009
layout/generic/nsTextFrame.cpp
testing/web-platform/tests/css/css-inline/empty-text-node-001-ref.html
testing/web-platform/tests/css/css-inline/empty-text-node-001.html
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -4500,17 +4500,18 @@ class nsContinuingTextFrame final : publ
       // If there's a prev-prev, then we can safely cast mPrevContinuation to
       // an nsContinuingTextFrame and access its mFirstContinuation pointer
       // directly, to avoid recursively calling FirstContinuation(), leading
       // to exponentially-slow behavior in the assertion.
       if (mPrevContinuation->GetPrevContinuation()) {
         auto* prev = static_cast<nsContinuingTextFrame*>(mPrevContinuation);
         MOZ_ASSERT(mFirstContinuation == prev->mFirstContinuation);
       } else {
-        MOZ_ASSERT(mFirstContinuation == mPrevContinuation->FirstContinuation());
+        MOZ_ASSERT(mFirstContinuation ==
+                   mPrevContinuation->FirstContinuation());
       }
     } else {
       MOZ_ASSERT(!mFirstContinuation);
     }
 #endif
     return mFirstContinuation;
   };
 
@@ -10173,18 +10174,23 @@ nsIFrame::RenderedText nsTextFrame::GetR
 bool nsTextFrame::IsEmpty() {
   NS_ASSERTION(!(mState & TEXT_IS_ONLY_WHITESPACE) ||
                    !(mState & TEXT_ISNOT_ONLY_WHITESPACE),
                "Invalid state");
 
   // XXXldb Should this check compatibility mode as well???
   const nsStyleText* textStyle = StyleText();
   if (textStyle->WhiteSpaceIsSignificant()) {
-    // XXX shouldn't we return true if the length is zero?
-    return false;
+    // When WhiteSpaceIsSignificant styles are in effect, we only treat the
+    // frame as empty if its content really is entirely *empty* (not just
+    // whitespace), AND it is NOT editable or within an <input> element.
+    // In these cases we consider that the whitespace-preserving style makes
+    // the frame behave as non-empty so that its height doesn't become zero.
+    return GetContentLength() == 0 && !GetContent()->IsEditable() &&
+           !GetContent()->GetParent()->IsHTMLElement(nsGkAtoms::input);
   }
 
   if (mState & TEXT_ISNOT_ONLY_WHITESPACE) {
     return false;
   }
 
   if (mState & TEXT_IS_ONLY_WHITESPACE) {
     return true;
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-inline/empty-text-node-001-ref.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Inline reference</title>
+<link rel="author" title="Jonathan Kew" href="mailto:jkew@mozilla.com">
+<style>
+.green { color: green; }
+.red { color: red; }
+.ref {
+  display: inline-block;
+}
+div div {
+  width: 50px;
+  height: 0px;
+  border: 20px solid green;
+  margin: 10px;
+}
+</style>
+<p>Test passes if the <span class=green>green</span> boxes have <span class=red>no red</span> in the middle.</p>
+
+<div class=ref>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+</div>
+
+<div class=ref>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+</div>
+
+<div class=ref>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-inline/empty-text-node-001.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Inline test: empty text node</title>
+<link rel="author" title="Jonathan Kew" href="mailto:jkew@mozilla.com">
+<link rel="help" title="2.1. Layout of Line Boxes" href="https://drafts.csswg.org/css-inline/#line-boxes">
+<link rel="match" href="empty-text-node-001-ref.html">
+<meta name="assert" content="Empty text node in a line box is treated as zero height.">
+<style>
+.green { color: green; }
+.red { color: red; }
+.testContent, .testBefore, .testAfter {
+  display: inline-block;
+}
+div div {
+  width: 50px;
+  line-height: 30px;
+  border: 20px solid green;
+  background-color: red;
+  margin: 10px;
+}
+.testBefore div::before {
+  content: "";
+}
+.testAfter div::after {
+  content: "";
+}
+.normal { white-space: normal; }
+.nowrap { white-space: nowrap; }
+.pre { white-space: pre; }
+.prewrap { white-space: pre-wrap; }
+.preline { white-space: pre-line; }
+.breakspaces { white-space: break-spaces; }
+</style>
+<p>Test passes if the <span class=green>green</span> boxes have <span class=red>no red</span> in the middle.</p>
+
+<div class=testContent>
+  <div class=normal></div>
+  <div class=nowrap></div>
+  <div class=pre></div>
+  <div class=prewrap></div>
+  <div class=preline></div>
+  <div class=breakspaces></div>
+  <script>
+  [...document.querySelectorAll(".testContent div")].forEach((node, i) => node.appendChild(document.createTextNode("")));
+  </script>
+</div>
+
+<div class=testBefore>
+  <div class=normal></div>
+  <div class=nowrap></div>
+  <div class=pre></div>
+  <div class=prewrap></div>
+  <div class=preline></div>
+  <div class=breakspaces></div>
+</div>
+
+<div class=testAfter>
+  <div class=normal></div>
+  <div class=nowrap></div>
+  <div class=pre></div>
+  <div class=prewrap></div>
+  <div class=preline></div>
+  <div class=breakspaces></div>
+</div>