boxlayout: Do not infloop
authorBenjamin Otte <otte@redhat.com>
Tue, 26 Apr 2022 17:51:16 +0000 (19:51 +0200)
committerSimon McVittie <smcv@debian.org>
Wed, 4 May 2022 10:30:16 +0000 (11:30 +0100)
if the loop for determining max width grows too big, print an error and
abort assuming that a satisfactory value was reached.

This will cause wrong layout and might cause widgets to overlap, but it
will not infloop.

It actually works around and doesn't really fix the primary cause of the
following bugs, but good enough to close them:

(cherry picked from commit 515b1f52929a189b858a38aba733a3e7d2375fbc)

Bug: https://gitlab.gnome.org/GNOME/gtk/-/issues/4517
Bug-Debian: https://bugs.debian.org/1010547
Origin: upstream, 4.6.4, commit:ae0166973795e750f08b89f9f0ef974d7ac48bc7

Gbp-Pq: Name boxlayout-Do-not-infloop.patch

gtk/gtkboxlayout.c

index e79c5e202a6499fe19ae33f17ef6475e6e6bf8f1..5d1b63169dcd3ed7372a46484600359e724ff6d5 100644 (file)
@@ -288,6 +288,12 @@ gtk_box_layout_compute_opposite_size (GtkBoxLayout *self,
   *natural = largest_nat;
 }
 
+/* if widgets haven't reached their min opposite size at this
+ * huge value, things went massively wrong and we need to bail to not
+ * cause an infinite loop.
+ */
+#define MAX_ALLOWED_SIZE (1 << 20)
+
 static int
 distribute_remaining_size (GtkRequestedSize *sizes,
                            gsize             n_sizes,
@@ -321,7 +327,40 @@ distribute_remaining_size (GtkRequestedSize *sizes,
     {
       int test;
 
-      if (max == G_MAXINT)
+      if (min > MAX_ALLOWED_SIZE)
+        {
+          /* sanity check! */
+          for (i = 0; i < n_sizes; i++)
+            {
+              int check_min, check_nat;
+              gtk_widget_measure (sizes[i].data,
+                                  orientation,
+                                  MAX_ALLOWED_SIZE,
+                                  &sizes[i].minimum_size, &sizes[i].natural_size,
+                                  NULL, NULL);
+              gtk_widget_measure (sizes[i].data,
+                                  orientation,
+                                  -1,
+                                  &check_min, &check_nat,
+                                  NULL, NULL);
+              if (check_min < sizes[i].minimum_size)
+                {
+                  g_critical ("%s %p reports a minimum %s of %u, but minimum %s for %s of %u is %u. Expect overlapping widgets.",
+                              G_OBJECT_TYPE_NAME (sizes[i].data), sizes[i].data,
+                              orientation == GTK_ORIENTATION_HORIZONTAL ? "width" : "height",
+                              check_min,
+                              orientation == GTK_ORIENTATION_HORIZONTAL ? "width" : "height",
+                              orientation == GTK_ORIENTATION_HORIZONTAL ? "height" : "width",
+                              MAX_ALLOWED_SIZE, sizes[i].minimum_size);
+                  sizes[i].minimum_size = check_min;
+                  sizes[i].natural_size = check_nat;
+                }
+              total_size += sizes[i].minimum_size;
+            }
+          return MAX (0, available - total_size);
+        }
+
+      if (max == MAX_ALLOWED_SIZE)
         test = min * 2;
       else
         test = (min + max) / 2;
@@ -465,7 +504,7 @@ gtk_box_layout_compute_opposite_size_for_size (GtkBoxLayout *self,
                                              self->orientation,
                                              available,
                                              min_size,
-                                             G_MAXINT);
+                                             MAX_ALLOWED_SIZE);
 
       /* Bring children up to size first */
       available = gtk_distribute_natural_allocation (available, nvis_children, sizes);