Bug 1757106 - Clean up menu popup rect handling. r=stransky
authorEmilio Cobos Álvarez <emilio@crisal.io>
Tue, 01 Mar 2022 01:06:37 +0000
changeset 609228 420728eba6a02ac8f369382b669881f2c808b206
parent 609227 1eb0b24a4248fcec28864419fb71f1988748025d
child 609229 975f49d4b6859c61daca155e501fac1983662de8
push id158659
push userealvarez@mozilla.com
push dateTue, 01 Mar 2022 01:09:00 +0000
treeherderautoland@975f49d4b685 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersstransky
bugs1757106
milestone99.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 1757106 - Clean up menu popup rect handling. r=stransky Store preferred rect in dev pixels, which simplifies a bit other calculations, and ensures that the nsMenuPopupFrame code accounts for zoom etc. Use existing conversion methods for GDK <-> Device <-> CSS conversions. Differential Revision: https://phabricator.services.mozilla.com/D139623
layout/base/Units.h
layout/xul/nsMenuPopupFrame.cpp
widget/gtk/nsWindow.cpp
widget/gtk/nsWindow.h
widget/nsIWidget.h
--- a/layout/base/Units.h
+++ b/layout/base/Units.h
@@ -435,16 +435,25 @@ struct LayoutDevicePixel {
 
   static LayoutDeviceIntPoint FromAppUnitsRounded(
       const nsPoint& aPoint, nscoord aAppUnitsPerDevPixel) {
     return LayoutDeviceIntPoint(
         NSAppUnitsToIntPixels(aPoint.x, aAppUnitsPerDevPixel),
         NSAppUnitsToIntPixels(aPoint.y, aAppUnitsPerDevPixel));
   }
 
+  static LayoutDeviceIntRect FromAppUnitsRounded(const nsRect& aRect,
+                                                 nscoord aAppUnitsPerDevPixel) {
+    return LayoutDeviceIntRect(
+        NSAppUnitsToIntPixels(aRect.x, aAppUnitsPerDevPixel),
+        NSAppUnitsToIntPixels(aRect.y, aAppUnitsPerDevPixel),
+        NSAppUnitsToIntPixels(aRect.Width(), aAppUnitsPerDevPixel),
+        NSAppUnitsToIntPixels(aRect.Height(), aAppUnitsPerDevPixel));
+  }
+
   static LayoutDeviceIntPoint FromAppUnitsToNearest(
       const nsPoint& aPoint, nscoord aAppUnitsPerDevPixel) {
     return LayoutDeviceIntPoint::FromUnknownPoint(
         aPoint.ToNearestPixels(aAppUnitsPerDevPixel));
   }
 
   static LayoutDeviceIntRect FromAppUnitsToNearest(
       const nsRect& aRect, nscoord aAppUnitsPerDevPixel) {
--- a/layout/xul/nsMenuPopupFrame.cpp
+++ b/layout/xul/nsMenuPopupFrame.cpp
@@ -1580,17 +1580,18 @@ nsresult nsMenuPopupFrame::SetPopupPosit
     vFlip = FlipStyle_Outside;
 #endif  // #ifdef XP_MACOSX
   }
 
   nscoord oldAlignmentOffset = mAlignmentOffset;
 
   if (IS_WAYLAND_DISPLAY()) {
     if (nsIWidget* widget = GetWidget()) {
-      nsRect prefRect = widget->GetPreferredPopupRect();
+      nsRect prefRect = LayoutDeviceIntRect::ToAppUnits(
+          widget->GetPreferredPopupRect(), presContext->AppUnitsPerDevPixel());
       if (prefRect.width > 0 && prefRect.height > 0) {
         // shrink the the popup down if it is larger than the prefered size.
         if (mRect.width > prefRect.width) {
           mRect.width = prefRect.width;
         }
         if (mRect.height > prefRect.height) {
           mRect.height = prefRect.height;
         }
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -1052,19 +1052,18 @@ void nsWindow::Move(double aX, double aY
   mBounds.y = y;
 
   if (!mCreated) {
     LOG("  is not created, return.\n");
     return;
   }
 
   if (IsWaylandPopup()) {
-    int32_t p2a = AppUnitsPerCSSPixel() / gfxPlatformGtk::GetFontScaleFactor();
-    if (mPreferredPopupRect.x != mBounds.x * p2a &&
-        mPreferredPopupRect.y != mBounds.y * p2a) {
+    auto prefBounds = mPreferredPopupRect;
+    if (prefBounds.TopLeft() != mBounds.TopLeft()) {
       NativeMoveResize(/* move */ true, /* resize */ false);
       NotifyRollupGeometryChange();
     } else {
       LOG("  mBounds same as mPreferredPopupRect, no need to move");
     }
   } else {
     NativeMoveResize(/* move */ true, /* resize */ false);
     NotifyRollupGeometryChange();
@@ -1441,24 +1440,22 @@ void nsWindow::WaylandPopupHierarchyCalc
     // its parent, no need to recalculate.
     LOG("  popup [%p] bounds [%d, %d] -> [%d x %d]", popup,
         (int)(popup->mBounds.x / FractionalScaleFactor()),
         (int)(popup->mBounds.y / FractionalScaleFactor()),
         (int)(popup->mBounds.width / FractionalScaleFactor()),
         (int)(popup->mBounds.height / FractionalScaleFactor()));
 #ifdef MOZ_LOGGING
     if (LOG_ENABLED()) {
-      nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
-      if (popupFrame) {
-        auto pos = popupFrame->GetPosition();
-        auto size = popupFrame->GetSize();
-        int32_t p2a =
-            AppUnitsPerCSSPixel() / gfxPlatformGtk::GetFontScaleFactor();
-        LOG("  popup [%p] layout [%d, %d] -> [%d x %d]", popup, pos.x / p2a,
-            pos.y / p2a, size.width / p2a, size.height / p2a);
+      if (nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame())) {
+        auto r = LayoutDeviceRect::FromAppUnitsRounded(
+            popupFrame->GetRect(),
+            popupFrame->PresContext()->AppUnitsPerDevPixel());
+        LOG("  popup [%p] layout [%d, %d] -> [%d x %d]", popup, r.x, r.y,
+            r.width, r.height);
       }
     }
 #endif
     if (popup->mPopupContextMenu && !popup->mPopupAnchored) {
       LOG("  popup [%p] is first context menu", popup);
       static int menuOffsetX =
           LookAndFeel::GetInt(LookAndFeel::IntID::ContextMenuOffsetHorizontal);
       static int menuOffsetY =
@@ -1471,26 +1468,25 @@ void nsWindow::WaylandPopupHierarchyCalc
       if (!popup->mPopupMatchesLayout) {
         NS_WARNING("Anchored popup does not match layout!");
       }
       popup->mRelativePopupPosition = popup->mPopupPosition;
     } else if (popup->mWaylandPopupPrev->mWaylandToplevel == nullptr) {
       LOG("  popup [%p] has toplevel as parent", popup);
       popup->mRelativePopupPosition = popup->mPopupPosition;
     } else {
-      int parentX, parentY;
-      WaylandGetParentPosition(&parentX, &parentY);
+      GdkPoint parent = WaylandGetParentPosition();
 
       LOG("  popup [%p] uses transformed coordinates\n", popup);
-      LOG("    parent position [%d, %d]\n", parentX, parentY);
+      LOG("    parent position [%d, %d]\n", parent.x, parent.y);
       LOG("    popup position [%d, %d]\n", popup->mPopupPosition.x,
           popup->mPopupPosition.y);
 
-      popup->mRelativePopupPosition.x = popup->mPopupPosition.x - parentX;
-      popup->mRelativePopupPosition.y = popup->mPopupPosition.y - parentY;
+      popup->mRelativePopupPosition.x = popup->mPopupPosition.x - parent.x;
+      popup->mRelativePopupPosition.y = popup->mPopupPosition.y - parent.y;
     }
     LOG("  popup [%p] transformed popup coordinates from [%d, %d] to [%d, %d]",
         popup, popup->mPopupPosition.x, popup->mPopupPosition.y,
         popup->mRelativePopupPosition.x, popup->mRelativePopupPosition.y);
     popup = popup->mWaylandPopupNext;
   }
 }
 
@@ -1534,34 +1530,35 @@ bool nsWindow::IsWidgetOverflowWindow() 
   if (this->GetFrame() && this->GetFrame()->GetContent()->GetID()) {
     nsCString nodeId;
     this->GetFrame()->GetContent()->GetID()->ToUTF8String(nodeId);
     return nodeId.Equals("widget-overflow");
   }
   return false;
 }
 
-void nsWindow::WaylandGetParentPosition(int* aX, int* aY) {
+GdkPoint nsWindow::WaylandGetParentPosition() {
   // Don't call WaylandGetParentPosition on X11 as it causes X11 roundtrips.
   // gdk_window_get_origin is very fast on Wayland as the
   // window position is cached by Gtk.
   MOZ_DIAGNOSTIC_ASSERT(GdkIsWaylandDisplay());
 
-  *aX = *aY = 0;
   GtkWindow* parentGtkWindow = gtk_window_get_transient_for(GTK_WINDOW(mShell));
   if (!parentGtkWindow || !GTK_IS_WIDGET(parentGtkWindow)) {
     NS_WARNING("Popup has no parent!");
-    return;
+    return {0, 0};
   }
   GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(parentGtkWindow));
   if (!window) {
     NS_WARNING("Popup parrent is not mapped!");
-    return;
-  }
-  gdk_window_get_origin(window, aX, aY);
+    return {0, 0};
+  }
+  gint x = 0, y = 0;
+  gdk_window_get_origin(window, &x, &y);
+  return {x, y};
 }
 
 #ifdef MOZ_LOGGING
 void nsWindow::LogPopupHierarchy() {
   if (!LOG_ENABLED()) {
     return;
   }
 
@@ -1855,67 +1852,56 @@ void nsWindow::NativeMoveResizeWaylandPo
   bool resized =
       mNewBoundsAfterMoveToRect.width || mNewBoundsAfterMoveToRect.height;
 
   if (moved || resized) {
     LOG("  Another move/resize called during waiting for callback\n");
 
     // Set the preferred size to zero to avoid wrong size of popup because the
     // mPreferredPopupRect is used in nsMenuPopupFrame to set dimensions
-    mPreferredPopupRect = nsRect(0, 0, 0, 0);
+    mPreferredPopupRect = LayoutDeviceIntRect();
     if (moved) {
       mBounds.x = mNewBoundsAfterMoveToRect.x;
       mBounds.y = mNewBoundsAfterMoveToRect.y;
     }
     if (resized) {
       mBounds.width = mNewBoundsAfterMoveToRect.width;
       mBounds.height = mNewBoundsAfterMoveToRect.height;
     }
     mNewBoundsAfterMoveToRect = LayoutDeviceIntRect(0, 0, 0, 0);
     NativeMoveResize(moved, resized);
     return;
   }
 
   LOG("  orig mBounds [%d, %d] -> [%d x %d]\n", mBounds.x, mBounds.y,
       mBounds.width, mBounds.height);
 
-  LayoutDeviceIntRect newBounds(0, 0, 0, 0);
-  int parentX, parentY;
-  WaylandGetParentPosition(&parentX, &parentY);
-  newBounds.x = GdkCoordToDevicePixels(aFinalSize->x + parentX);
-  newBounds.y = GdkCoordToDevicePixels(aFinalSize->y + parentY);
-
-  double scale =
-      BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
-  newBounds.width = NSToIntRound(scale * aFinalSize->width);
-  newBounds.height = NSToIntRound(scale * aFinalSize->height);
+  LayoutDeviceIntRect newBounds = [&] {
+    GdkRectangle finalRect = *aFinalSize;
+    GdkPoint parent = WaylandGetParentPosition();
+    finalRect.x += parent.x;
+    finalRect.y += parent.y;
+    return GdkRectToDevicePixels(finalRect);
+  }();
 
   LOG("  new mBounds [%d, %d] -> [%d x %d]", newBounds.x, newBounds.y,
       newBounds.width, newBounds.height);
 
-  bool needsPositionUpdate =
-      (newBounds.x != mBounds.x || newBounds.y != mBounds.y);
-  bool needsSizeUpdate =
-      (newBounds.width != mBounds.width || newBounds.height != mBounds.height);
+  const bool needsPositionUpdate = newBounds.TopLeft() != mBounds.TopLeft();
+  const bool needsSizeUpdate = newBounds.Size() != mBounds.Size();
 
   // Update view
   if (needsSizeUpdate) {
     LOG("  needSizeUpdate\n");
-    // TODO: use correct monitor here?
-    int32_t p2a = AppUnitsPerCSSPixel() / gfxPlatformGtk::GetFontScaleFactor();
-    mPreferredPopupRect =
-        nsRect(NSIntPixelsToAppUnits(newBounds.x, p2a),
-               NSIntPixelsToAppUnits(newBounds.y, p2a),
-               NSIntPixelsToAppUnits(aFinalSize->width, p2a),
-               NSIntPixelsToAppUnits(aFinalSize->height, p2a));
+    mPreferredPopupRect = newBounds;
     mPreferredPopupRectFlushed = false;
+
     Resize(aFinalSize->width, aFinalSize->height, true);
 
-    nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
-    if (popupFrame) {
+    if (nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame())) {
       RefPtr<PresShell> presShell = popupFrame->PresShell();
       presShell->FrameNeedsReflow(popupFrame, IntrinsicDirty::Resize,
                                   NS_FRAME_IS_DIRTY);
       // Force to trigger popup crop to fit the screen
       popupFrame->SetPopupPosition(nullptr, true, false);
     }
   }
 
@@ -1975,40 +1961,39 @@ bool nsWindow::IsPopupDirectionRTL() {
 }
 
 // Position the popup directly by gtk_window_move() and try to keep it
 // on screen by just moving it in scope of it's parent window.
 //
 // It's used when we position noautihode popup and we don't use xdg_positioner.
 // See Bug 1718867
 void nsWindow::WaylandPopupSetDirectPosition() {
-  GdkPoint position = DevicePixelsToGdkPointRoundDown(mBounds.TopLeft());
-  GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mBounds.Size());
-
-  LOG("nsWindow::WaylandPopupSetDirectPosition %d,%d -> %d x %d\n", position.x,
-      position.y, size.width, size.height);
-
-  mPopupPosition = position;
+  GdkRectangle rect = DevicePixelsToGdkRectRoundOut(mBounds);
+
+  LOG("nsWindow::WaylandPopupSetDirectPosition %d,%d -> %d x %d\n", rect.x,
+      rect.y, rect.width, rect.height);
+
+  mPopupPosition = {rect.x, rect.y};
 
   if (mIsDragPopup) {
-    gtk_window_move(GTK_WINDOW(mShell), mPopupPosition.x, mPopupPosition.y);
-    gtk_window_resize(GTK_WINDOW(mShell), size.width, size.height);
+    gtk_window_move(GTK_WINDOW(mShell), rect.x, rect.y);
+    gtk_window_resize(GTK_WINDOW(mShell), rect.width, rect.height);
     // DND window is placed inside container so we need to make hard size
     // request to ensure parent container is resized too.
-    gtk_widget_set_size_request(GTK_WIDGET(mShell), size.width, size.height);
+    gtk_widget_set_size_request(GTK_WIDGET(mShell), rect.width, rect.height);
     return;
   }
 
   GtkWindow* parentGtkWindow = gtk_window_get_transient_for(GTK_WINDOW(mShell));
   nsWindow* window = get_window_for_gtk_widget(GTK_WIDGET(parentGtkWindow));
   GdkWindow* gdkWindow =
       gtk_widget_get_window(GTK_WIDGET(window->GetMozContainer()));
 
   int parentWidth = gdk_window_get_width(gdkWindow);
-  int popupWidth = size.width;
+  int popupWidth = rect.width;
 
   int x;
   gdk_window_get_position(gdkWindow, &x, nullptr);
 
   // If popup is bigger than main window just center it.
   if (popupWidth > parentWidth) {
     mPopupPosition.x = -(parentWidth - popupWidth) / 2 + x;
   } else {
@@ -2023,27 +2008,28 @@ void nsWindow::WaylandPopupSetDirectPosi
         mPopupPosition.x = parentWidth + x - popupWidth;
       }
     }
   }
 
   LOG("  set position [%d, %d]\n", mPopupPosition.x, mPopupPosition.y);
   gtk_window_move(GTK_WINDOW(mShell), mPopupPosition.x, mPopupPosition.y);
 
-  LOG("  set size [%d, %d]\n", size.width, size.height);
-  gtk_window_resize(GTK_WINDOW(mShell), size.width, size.height);
-
-  if (mPopupPosition.x != position.x) {
-    mBounds.x = mPopupPosition.x * FractionalScaleFactor();
-    mBounds.y = mPopupPosition.y * FractionalScaleFactor();
+  LOG("  set size [%d, %d]\n", rect.width, rect.height);
+  gtk_window_resize(GTK_WINDOW(mShell), rect.width, rect.height);
+
+  if (mPopupPosition.x != rect.x) {
+    mBounds = LayoutDeviceIntRect(GdkPointToDevicePixels(mPopupPosition),
+                                  mBounds.Size());
     LOG("  setting new bounds [%d, %d]\n", mBounds.x, mBounds.y);
     NotifyWindowMoved(mBounds.x, mBounds.y);
-    nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
-    if (popupFrame) {
-      popupFrame->MoveTo(CSSIntPoint(mPopupPosition.x, mPopupPosition.y), true);
+    if (nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame())) {
+      auto p = CSSIntPoint::Round(
+          mBounds.TopLeft() / popupFrame->PresContext()->CSSToDevPixelScale());
+      popupFrame->MoveTo(p, true);
     }
   }
 }
 
 bool nsWindow::WaylandPopupFitsParentWindow(GdkRectangle* aSize) {
   GtkWindow* parentGtkWindow = gtk_window_get_transient_for(GTK_WINDOW(mShell));
   nsWindow* parentWindow =
       get_window_for_gtk_widget(GTK_WIDGET(parentGtkWindow));
@@ -2064,22 +2050,20 @@ bool nsWindow::WaylandPopupFitsParentWin
 
   // We use parent mozcontainer size plus left/top CSD decorations sizes as
   // this coordinates are used by mBounds.
   int parentWidth = gdk_window_get_width(parentGdkWindow) + x;
   int parentHeight = gdk_window_get_width(parentGdkWindow) + y;
   int popupWidth = aSize->width;
   int popupHeight = aSize->height;
 
-  int scale = FractionalScaleFactor();
-  int popupX = mBounds.x / scale;
-  int popupY = mBounds.y / scale;
-
-  return popupX + popupWidth <= parentWidth &&
-         popupY + popupHeight <= parentHeight;
+  auto popupBounds = DevicePixelsToGdkRectRoundOut(mBounds);
+
+  return popupBounds.x + popupWidth <= parentWidth &&
+         popupBounds.y + popupHeight <= parentHeight;
 }
 
 void nsWindow::NativeMoveResizeWaylandPopup(bool aMove, bool aResize) {
   GdkPoint position = DevicePixelsToGdkPointRoundDown(mBounds.TopLeft());
   GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mBounds.Size());
 
   LOG("nsWindow::NativeMoveResizeWaylandPopup %d,%d -> %d x %d\n", position.x,
       position.y, size.width, size.height);
@@ -2268,24 +2252,16 @@ void nsWindow::WaylandPopupMove() {
                   FractionalScaleFactor();
       LOG("  popup is moved, setting new bounds starts at [%d, %d]\n",
           mBounds.x, mBounds.y);
       NotifyWindowMoved(mBounds.x, mBounds.y);
     }
     return;
   }
 
-  int32_t p2a;
-  double devPixelsPerCSSPixel = StaticPrefs::layout_css_devPixelsPerPx();
-  if (devPixelsPerCSSPixel > 0.0) {
-    p2a = AppUnitsPerCSSPixel() / devPixelsPerCSSPixel * GdkCeiledScaleFactor();
-  } else {
-    p2a = AppUnitsPerCSSPixel() / gfxPlatformGtk::GetFontScaleFactor();
-  }
-
   const bool isTopContextMenu = mPopupContextMenu && !mPopupAnchored;
   int8_t popupAlign(popupFrame->GetPopupAlignment());
   int8_t anchorAlign(popupFrame->GetPopupAnchor());
   if (isTopContextMenu) {
     anchorAlign = POPUPALIGNMENT_BOTTOMRIGHT;
     popupAlign = POPUPALIGNMENT_TOPLEFT;
   }
   if (IsPopupDirectionRTL()) {
@@ -2321,50 +2297,51 @@ void nsWindow::WaylandPopupMove() {
           ToString(popupMargin.mPopupOffset).c_str());
       anchorRectAppUnits.Inflate(popupMargin.mAnchorMargin);
       LOG("    after margins %s\n", ToString(anchorRectAppUnits).c_str());
       if (anchorRect.width < 0) {
         auto w = -anchorRect.width;
         anchorRect.width += w + 1;
         anchorRect.x += w;
       }
-      anchorRect =
-          LayoutDeviceIntRect::FromAppUnitsToNearest(anchorRectAppUnits, p2a);
+      anchorRect = LayoutDeviceIntRect::FromAppUnitsToNearest(
+          anchorRectAppUnits, popupFrame->PresContext()->AppUnitsPerDevPixel());
       LOG("    final %s\n", ToString(anchorRect).c_str());
     }
   }
 
   // Get gravity and flip type
   FlipType flipType = FlipType_Default;
   int8_t position = -1;
   GdkGravity rectAnchor = PopupAlignmentToGdkGravity(anchorAlign);
   GdkGravity menuAnchor = PopupAlignmentToGdkGravity(popupAlign);
   if (!isTopContextMenu) {
     flipType = popupFrame->GetFlipType();
     position = popupFrame->GetAlignmentPosition();
   }
 
   if (mRelativePopupOffset.x || mRelativePopupOffset.y) {
     MOZ_ASSERT(isTopContextMenu);
-    anchorRect.SetRect(mRelativePopupPosition.x - mRelativePopupOffset.x,
-                       mRelativePopupPosition.y - mRelativePopupOffset.y,
-                       mRelativePopupOffset.x * 2, mRelativePopupOffset.y * 2);
+    GdkRectangle rect{mRelativePopupPosition.x - mRelativePopupOffset.x,
+                      mRelativePopupPosition.y - mRelativePopupOffset.y,
+                      mRelativePopupOffset.x * 2, mRelativePopupOffset.y * 2};
+
+    anchorRect = GdkRectToDevicePixels(rect);
     LOG("  Set anchor rect with offset [%d, %d] -> [%d x %d]", anchorRect.x,
         anchorRect.y, anchorRect.width, anchorRect.height);
   } else if (!hasAnchorRect) {
     LOG("  No anchor rect given, use position for anchor [%d, %d]",
         mRelativePopupPosition.x, mRelativePopupPosition.y);
-    anchorRect.SetRect(mRelativePopupPosition.x, mRelativePopupPosition.y, 1,
-                       1);
-  } else if (mWaylandPopupPrev->mWaylandToplevel != nullptr) {
-    int parentX, parentY;
-    WaylandGetParentPosition(&parentX, &parentY);
-    LOG("  subtract parent position [%d, %d]\n", parentX, parentY);
-    anchorRect.x -= parentX;
-    anchorRect.y -= parentY;
+    anchorRect =
+        LayoutDeviceIntRect(GdkPointToDevicePixels(mRelativePopupPosition),
+                            LayoutDeviceIntSize(1, 1));
+  } else if (mWaylandPopupPrev->mWaylandToplevel) {
+    GdkPoint parent = WaylandGetParentPosition();
+    LOG("  subtract parent position [%d, %d]\n", parent.x, parent.y);
+    anchorRect.MoveBy(-GdkPointToDevicePixels(parent));
   }
 
   LOG("  final popup rect position [%d, %d] -> [%d x %d]\n", anchorRect.x,
       anchorRect.y, anchorRect.width, anchorRect.height);
 
   LOG("  parentRect gravity: %d anchor gravity: %d\n", rectAnchor, menuAnchor);
 
   // Gtk default is: GDK_ANCHOR_FLIP | GDK_ANCHOR_SLIDE | GDK_ANCHOR_RESIZE.
@@ -2407,35 +2384,34 @@ void nsWindow::WaylandPopupMove() {
     hints = GdkAnchorHints(hints | GDK_ANCHOR_SLIDE);
   }
   if (!g_signal_handler_find(gdkWindow, G_SIGNAL_MATCH_FUNC, 0, 0, nullptr,
                              FuncToGpointer(NativeMoveResizeCallback), this)) {
     g_signal_connect(gdkWindow, "moved-to-rect",
                      G_CALLBACK(NativeMoveResizeCallback), this);
   }
 
-  GdkRectangle rect = {anchorRect.x, anchorRect.y, anchorRect.width,
-                       anchorRect.height};
-
+  GdkRectangle rect = DevicePixelsToGdkRectRoundOut(anchorRect);
   mWaitingForMoveToRectCallback = true;
 
   if (gtk_widget_is_visible(mShell)) {
     NS_WARNING(
         "Positioning visible popup under Wayland, position may be wrong!");
   }
 
   // Correct popup position now. It will be updated by gdk_window_move_to_rect()
   // anyway but we need to set it now to avoid a race condition here.
   WaylandPopupRemoveNegativePosition();
 
   LOG("  move-to-rect call");
-  mPopupLastAnchor = anchorRect;
 
   auto offset =
-      LayoutDevicePoint::FromAppUnitsToNearest(popupMargin.mPopupOffset, p2a);
+      DevicePixelsToGdkPointRoundDown(LayoutDevicePoint::FromAppUnitsToNearest(
+          popupMargin.mPopupOffset,
+          popupFrame->PresContext()->AppUnitsPerDevPixel()));
   sGdkWindowMoveToRect(gdkWindow, &rect, rectAnchor, menuAnchor, hints,
                        offset.x, offset.y);
 }
 
 void nsWindow::SetZIndex(int32_t aZIndex) {
   nsIWidget* oldPrev = GetPrevSibling();
 
   nsBaseWidget::SetZIndex(aZIndex);
@@ -6233,17 +6209,17 @@ void nsWindow::NativeShow(bool aAction) 
     if (mHiddenPopupPositioned && IsPopup()) {
       LOG("  re-position hidden popup window");
       gtk_window_move(GTK_WINDOW(mShell), mPopupPosition.x, mPopupPosition.y);
       mHiddenPopupPositioned = false;
     }
   } else {
     // There's a chance that when the popup will be shown again it might be
     // resized because parent could be moved meanwhile.
-    mPreferredPopupRect = nsRect(0, 0, 0, 0);
+    mPreferredPopupRect = LayoutDeviceIntRect();
     mPreferredPopupRectFlushed = false;
     LOG("nsWindow::NativeShow hide\n");
     if (GdkIsWaylandDisplay()) {
       if (IsWaylandPopup()) {
         // We can't close tracked popups directly as they may have visible
         // child popups. Just mark is as closed and let
         // UpdateWaylandPopupHierarchy() do the job.
         if (IsInPopupHierarchy()) {
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -388,19 +388,21 @@ class nsWindow final : public nsBaseWidg
   void CreateCompositorVsyncDispatcher() override;
   LayoutDeviceIntPoint GetNativePointerLockCenter() {
     return mNativePointerLockCenter;
   }
   void SetNativePointerLockCenter(
       const LayoutDeviceIntPoint& aLockCenter) override;
   void LockNativePointer() override;
   void UnlockNativePointer() override;
-  nsRect GetPreferredPopupRect() override { return mPreferredPopupRect; };
+  LayoutDeviceIntRect GetPreferredPopupRect() const override {
+    return mPreferredPopupRect;
+  };
   void FlushPreferredPopupRect() override {
-    mPreferredPopupRect = nsRect(0, 0, 0, 0);
+    mPreferredPopupRect = LayoutDeviceIntRect();
     mPreferredPopupRectFlushed = true;
   };
 #endif
 
   typedef enum {
     // WebRender compositor is enabled
     COMPOSITOR_ENABLED,
     // WebRender compositor is paused after window creation.
@@ -728,17 +730,17 @@ class nsWindow final : public nsBaseWidg
   bool DoDrawTilebarCorners();
   bool IsChromeWindowTitlebar();
 
   void SetPopupWindowDecoration(bool aShowOnTaskbar);
 
   void ApplySizeConstraints(void);
 
   // Wayland Popup section
-  void WaylandGetParentPosition(int* aX, int* aY);
+  GdkPoint WaylandGetParentPosition();
   bool WaylandPopupNeedsTrackInHierarchy();
   bool WaylandPopupIsAnchored();
   bool WaylandPopupIsMenu();
   bool WaylandPopupIsContextMenu();
   bool WaylandPopupIsPermanent();
   bool IsWidgetOverflowWindow();
   void RemovePopupFromHierarchyList();
   void ShowWaylandWindow();
@@ -773,45 +775,36 @@ class nsWindow final : public nsBaseWidg
   nsAutoCString GetFrameTag() const;
   nsCString GetPopupTypeName();
   bool IsPopupDirectionRTL();
 
 #ifdef MOZ_LOGGING
   void LogPopupHierarchy();
 #endif
 
-  /*  mPopupPosition is the original popup position from layout,
-   *  set by nsWindow::Move() or nsWindow::Resize().
-   */
+  // mPopupPosition is the original popup position from layout, set by
+  // nsWindow::Move() or nsWindow::Resize().
   GdkPoint mPopupPosition{};
 
-  /*  mRelativePopupPosition is popup position calculated against parent window.
-   */
+  // mRelativePopupPosition is popup position calculated against parent window.
   GdkPoint mRelativePopupPosition{};
 
-  /* mRelativePopupOffset is used by context menus.
-   */
+  // mRelativePopupOffset is used by context menus.
   GdkPoint mRelativePopupOffset{};
 
-  /* Last used anchor for move-to-rect.
-   */
-  LayoutDeviceIntRect mPopupLastAnchor;
-
-  /* Toplevel window (first element) of linked list of wayland popups.
-   * It's nullptr if we're the toplevel.
-   */
+  // Toplevel window (first element) of linked list of Wayland popups. It's null
+  // if we're the toplevel.
   RefPtr<nsWindow> mWaylandToplevel;
 
-  /* Next/Previous popups in Wayland popup hieararchy.
-   */
+  // Next/Previous popups in Wayland popup hierarchy.
   RefPtr<nsWindow> mWaylandPopupNext;
   RefPtr<nsWindow> mWaylandPopupPrev;
 
   // Used by WaylandPopupMove() to track popup movement.
-  nsRect mPreferredPopupRect;
+  LayoutDeviceIntRect mPreferredPopupRect;
 
   LayoutDeviceIntRect mNewBoundsAfterMoveToRect;
 
   /**
    * |mIMContext| takes all IME related stuff.
    *
    * This is owned by the top-level nsWindow or the topmost child
    * nsWindow embedded in a non-Gecko widget.
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -1705,20 +1705,21 @@ class nsIWidget : public nsISupports {
   }
   virtual nsresult GetSystemFont(nsCString& aFontName) {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   // Get rectangle of the screen where the window is placed.
   // It's used to detect popup overflow under Wayland because
   // Screenmanager does not work under it.
-  virtual nsRect GetPreferredPopupRect() {
+  virtual LayoutDeviceIntRect GetPreferredPopupRect() const {
     NS_WARNING("GetPreferredPopupRect implemented only for wayland");
-    return nsRect(0, 0, 0, 0);
+    return LayoutDeviceIntRect();
   }
+
   virtual void FlushPreferredPopupRect() {
     NS_WARNING("FlushPreferredPopupRect implemented only for wayland");
     return;
   }
 
   /**
    * If this widget uses native pointer lock instead of warp-to-center
    * (currently only GTK on Wayland), these methods provide access to that