diff --git a/view/nsView.cpp b/view/nsView.cpp --- a/view/nsView.cpp +++ b/view/nsView.cpp @@ -320,7 +320,6 @@ // Stash a copy of these and use them so we can handle this being deleted (say // from sync painting/flushing from Show/Move/Resize on the widget). LayoutDeviceIntRect newBounds; - RefPtr dx = mViewManager->GetDeviceContext(); nsWindowType type = widget->WindowType(); @@ -360,7 +359,8 @@ // because of the potential for device-pixel coordinate spaces for mixed // hidpi/lodpi screens to overlap each other and result in bad placement // (bug 814434). - DesktopToLayoutDeviceScale scale = dx->GetDesktopToDeviceScale(); + + DesktopToLayoutDeviceScale scale = widget->GetDesktopToDeviceScaleByScreen(); DesktopRect deskRect = newBounds / scale; if (changedPos) { diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h --- a/widget/gtk/nsWindow.h +++ b/widget/gtk/nsWindow.h @@ -125,6 +125,7 @@ virtual float GetDPI() override; virtual double GetDefaultScaleInternal() override; mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() override; + mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScaleByScreen() override; virtual void SetParent(nsIWidget* aNewParent) override; virtual void SetModal(bool aModal) override; virtual bool IsVisible() const override; diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -57,6 +57,7 @@ #if defined(MOZ_WAYLAND) #include +#include "nsView.h" #endif #include "nsGkAtoms.h" @@ -841,6 +842,37 @@ return DesktopToLayoutDeviceScale(1.0); } +DesktopToLayoutDeviceScale +nsWindow::GetDesktopToDeviceScaleByScreen() +{ +#ifdef MOZ_WAYLAND + GdkDisplay* gdkDisplay = gdk_display_get_default(); + // In Wayland there's no way to get absolute position of the window and use it to + // determine the screen factor of the monitor on which the window is placed. + // The window is notified of the current scale factor but not at this point, + // so the GdkScaleFactor can return wrong value which can lead to wrong popup + // placement. + // We need to use parent's window scale factor for the new one. + if (GDK_IS_WAYLAND_DISPLAY(gdkDisplay)) { + nsView* view = nsView::GetViewFor(this); + if (view) { + nsView* parentView = view->GetParent(); + if (parentView) { + nsIWidget* parentWidget = parentView->GetNearestWidget(nullptr); + if (parentWidget) { + return DesktopToLayoutDeviceScale(parentWidget->RoundsWidgetCoordinatesTo()); + } else { + NS_WARNING("Widget has no parent"); + } + } + } else { + NS_WARNING("Cannot find widget view"); + } + } +#endif + return nsBaseWidget::GetDesktopToDeviceScale(); +} + void nsWindow::SetParent(nsIWidget *aNewParent) { diff --git a/widget/nsBaseWidget.h b/widget/nsBaseWidget.h --- a/widget/nsBaseWidget.h +++ b/widget/nsBaseWidget.h @@ -24,6 +24,8 @@ #include "nsIWidgetListener.h" #include "nsPIDOMWindow.h" #include "nsWeakReference.h" +#include "nsView.h" +#include "nsViewManager.h" #include #if defined(XP_WIN) @@ -238,6 +240,10 @@ mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() override { return mozilla::DesktopToLayoutDeviceScale(1.0); } + mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScaleByScreen() override { + return (nsView::GetViewFor(this)->GetViewManager()->GetDeviceContext())->GetDesktopToDeviceScale(); + } + virtual void ConstrainPosition(bool aAllowSlop, int32_t *aX, int32_t *aY) override {} diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -578,6 +578,13 @@ virtual mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() = 0; /** + * Return the scaling factor between device pixels and the platform- + * dependent "desktop pixels" by looking up the screen by the position + * of the widget. + */ + virtual mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScaleByScreen() = 0; + + /** * Return the default scale factor for the window. This is the * default number of device pixels per CSS pixel to use. This should * depend on OS/platform settings such as the Mac's "UI scale factor"