From a57cdd05c5e6dc946da9a5967d518f0c3395309b Mon Sep 17 00:00:00 2001
From: David Shin <dshin@mozilla.com>
Date: Tue, 13 Dec 2022 15:23:35 +0000
Subject: [PATCH] Bug 1800907 - Do not `UnionRect` empty rects in
 `OverflowAreas::Union*`. r=jwatt

Previously, when current overflow area has one axis at zero size, calling
`OverflowAreas::Union*` with a rect with both axes at zero would overwrite
the axis with non-zero size.

Differential Revision: https://phabricator.services.mozilla.com/D162245
---
 layout/generic/ReflowOutput.cpp               | 27 ++++++++-
 .../scrollable-overflow-zero-one-axis.html    | 59 +++++++++++++++++++
 2 files changed, 83 insertions(+), 3 deletions(-)
 create mode 100644 testing/web-platform/tests/css/css-overflow/scrollable-overflow-zero-one-axis.html

diff --git a/layout/generic/ReflowOutput.cpp b/layout/generic/ReflowOutput.cpp
index d54684809a371..a0312dffda947 100644
--- a/layout/generic/ReflowOutput.cpp
+++ b/layout/generic/ReflowOutput.cpp
@@ -11,6 +11,19 @@
 
 namespace mozilla {
 
+static bool IsValidOverflowRect(const nsRect& aRect) {
+  // `IsEmpty` in the context of `nsRect` means "width OR height is zero."
+  // However, in the context of overflow, the rect having one axis as zero is
+  // NOT considered empty.
+  if (MOZ_LIKELY(!aRect.IsEmpty())) {
+    return true;
+  }
+
+  // Be defensive and consider rects with any negative size as invalid.
+  return !aRect.IsEqualEdges(nsRect()) && aRect.Width() >= 0 &&
+         aRect.Height() >= 0;
+}
+
 /* static */
 nsRect OverflowAreas::GetOverflowClipRect(const nsRect& aRectToClip,
                                           const nsRect& aBounds,
@@ -40,12 +53,20 @@ void OverflowAreas::ApplyOverflowClippingOnRect(nsRect& aOverflowRect,
 }
 
 void OverflowAreas::UnionWith(const OverflowAreas& aOther) {
-  InkOverflow().UnionRect(InkOverflow(), aOther.InkOverflow());
-  ScrollableOverflow().UnionRect(ScrollableOverflow(),
-                                 aOther.ScrollableOverflow());
+  if (IsValidOverflowRect(aOther.InkOverflow())) {
+    InkOverflow().UnionRect(InkOverflow(), aOther.InkOverflow());
+  }
+  if (IsValidOverflowRect(aOther.ScrollableOverflow())) {
+    ScrollableOverflow().UnionRect(ScrollableOverflow(),
+                                   aOther.ScrollableOverflow());
+  }
 }
 
 void OverflowAreas::UnionAllWith(const nsRect& aRect) {
+  if (!IsValidOverflowRect(aRect)) {
+    // Same as `UnionWith()` - avoid losing information.
+    return;
+  }
   InkOverflow().UnionRect(InkOverflow(), aRect);
   ScrollableOverflow().UnionRect(ScrollableOverflow(), aRect);
 }
diff --git a/testing/web-platform/tests/css/css-overflow/scrollable-overflow-zero-one-axis.html b/testing/web-platform/tests/css/css-overflow/scrollable-overflow-zero-one-axis.html
new file mode 100644
index 0000000000000..1986a8d48b98f
--- /dev/null
+++ b/testing/web-platform/tests/css/css-overflow/scrollable-overflow-zero-one-axis.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: Scroll Length Calculated Correctly When One Axis Has Zero Length</title>
+<link rel="author" title="David Shin" href="mailto:dshin@mozilla.com">
+<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-3/#scrollable">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1800907">
+<style>
+.scroll {
+  overflow: scroll;
+}
+
+.zero-h {
+  width: 100px;
+  height: 0px;
+}
+
+.zero-w {
+  width: 0;
+  height: 100px;
+}
+
+.flex-row {
+  display: flex;
+}
+
+.flex-col {
+  display: flex;
+  flex-direction: column;
+}
+
+.grid {
+  display: grid;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+<body onload="checkLayout('.scroll')">
+<div class="scroll zero-h" data-expected-scroll-height="100">
+  <div class="zero-w"></div>
+</div>
+<div class="scroll zero-w" data-expected-scroll-width="100">
+  <div class="zero-h"></div>
+</div>
+
+<div class="scroll zero-h flex-row" data-expected-scroll-height="100">
+  <div class="zero-w"></div>
+</div>
+<div class="scroll zero-w flex-col" data-expected-scroll-width="100">
+  <div class="zero-h"></div>
+</div>
+
+<div class="scroll zero-h grid" data-expected-scroll-height="100">
+  <div class="zero-w"></div>
+</div>
+<div class="scroll zero-w grid" data-expected-scroll-width="100">
+  <div class="zero-h"></div>
+</div>
-- 
GitLab