mtasaka / rpms / firefox

Forked from rpms/firefox 4 years ago
Clone
Blob Blame History Raw
diff --git a/layout/xul/nsMenuPopupFrame.cpp b/layout/xul/nsMenuPopupFrame.cpp
--- a/layout/xul/nsMenuPopupFrame.cpp
+++ b/layout/xul/nsMenuPopupFrame.cpp
@@ -1502,7 +1502,7 @@
 
   nscoord oldAlignmentOffset = mAlignmentOffset;
 
-  bool inWayland = false;
+  static bool inWayland = false;
 #ifdef MOZ_WAYLAND
   inWayland = !GDK_IS_X11_DISPLAY(gdk_display_get_default());
 #endif
@@ -1512,9 +1512,9 @@
   // However, if a panel is already constrained or flipped (mIsOffset), then we
   // want to continue to calculate this. Also, always do this for content
   // shells, so that the popup doesn't extend outside the containing frame.
-  if (!inWayland && (mInContentShell || (mFlip != FlipType_None &&
-                                         (!aIsMove || mIsOffset ||
-                                          mPopupType != ePopupTypePanel)))) {
+  if (mInContentShell ||
+      (mFlip != FlipType_None &&
+       (!aIsMove || mIsOffset || mPopupType != ePopupTypePanel))) {
     int32_t appPerDev = presContext->AppUnitsPerDevPixel();
     LayoutDeviceIntRect anchorRectDevPix =
         LayoutDeviceIntRect::FromAppUnitsToNearest(anchorRect, appPerDev);
@@ -1532,60 +1532,66 @@
     if (mRect.width > screenRect.width) mRect.width = screenRect.width;
     if (mRect.height > screenRect.height) mRect.height = screenRect.height;
 
-    // at this point the anchor (anchorRect) is within the available screen
-    // area (screenRect) and the popup is known to be no larger than the screen.
+    // We can't get the subsequent change of the popup position under
+    // waylande where gdk_window_move_to_rect is used to place them
+    // because we don't know the absolute position of the window on the screen.
+    if (!inWayland) {
+      // at this point the anchor (anchorRect) is within the available screen
+      // area (screenRect) and the popup is known to be no larger than the
+      // screen.
 
-    // We might want to "slide" an arrow if the panel is of the correct type -
-    // but we can only slide on one axis - the other axis must be "flipped or
-    // resized" as normal.
-    bool slideHorizontal = false, slideVertical = false;
-    if (mFlip == FlipType_Slide) {
-      int8_t position = GetAlignmentPosition();
-      slideHorizontal = position >= POPUPPOSITION_BEFORESTART &&
-                        position <= POPUPPOSITION_AFTEREND;
-      slideVertical = position >= POPUPPOSITION_STARTBEFORE &&
-                      position <= POPUPPOSITION_ENDAFTER;
-    }
+      // We might want to "slide" an arrow if the panel is of the correct type -
+      // but we can only slide on one axis - the other axis must be "flipped or
+      // resized" as normal.
+      bool slideHorizontal = false, slideVertical = false;
+      if (mFlip == FlipType_Slide) {
+        int8_t position = GetAlignmentPosition();
+        slideHorizontal = position >= POPUPPOSITION_BEFORESTART &&
+                          position <= POPUPPOSITION_AFTEREND;
+        slideVertical = position >= POPUPPOSITION_STARTBEFORE &&
+                        position <= POPUPPOSITION_ENDAFTER;
+      }
 
-    // Next, check if there is enough space to show the popup at full size when
-    // positioned at screenPoint. If not, flip the popups to the opposite side
-    // of their anchor point, or resize them as necessary.
-    bool endAligned = IsDirectionRTL()
-                          ? mPopupAlignment == POPUPALIGNMENT_TOPLEFT ||
-                                mPopupAlignment == POPUPALIGNMENT_BOTTOMLEFT
-                          : mPopupAlignment == POPUPALIGNMENT_TOPRIGHT ||
-                                mPopupAlignment == POPUPALIGNMENT_BOTTOMRIGHT;
-    nscoord preOffsetScreenPoint = screenPoint.x;
-    if (slideHorizontal) {
-      mRect.width = SlideOrResize(screenPoint.x, mRect.width, screenRect.x,
-                                  screenRect.XMost(), &mAlignmentOffset);
-    } else {
-      mRect.width = FlipOrResize(
-          screenPoint.x, mRect.width, screenRect.x, screenRect.XMost(),
-          anchorRect.x, anchorRect.XMost(), margin.left, margin.right,
-          offsetForContextMenu.x, hFlip, endAligned, &mHFlip);
-    }
-    mIsOffset = preOffsetScreenPoint != screenPoint.x;
+      // Next, check if there is enough space to show the popup at full size
+      // when positioned at screenPoint. If not, flip the popups to the opposite
+      // side of their anchor point, or resize them as necessary.
+      bool endAligned = IsDirectionRTL()
+                            ? mPopupAlignment == POPUPALIGNMENT_TOPLEFT ||
+                                  mPopupAlignment == POPUPALIGNMENT_BOTTOMLEFT
+                            : mPopupAlignment == POPUPALIGNMENT_TOPRIGHT ||
+                                  mPopupAlignment == POPUPALIGNMENT_BOTTOMRIGHT;
+      nscoord preOffsetScreenPoint = screenPoint.x;
+      if (slideHorizontal) {
+        mRect.width = SlideOrResize(screenPoint.x, mRect.width, screenRect.x,
+                                    screenRect.XMost(), &mAlignmentOffset);
+      } else {
+        mRect.width = FlipOrResize(
+            screenPoint.x, mRect.width, screenRect.x, screenRect.XMost(),
+            anchorRect.x, anchorRect.XMost(), margin.left, margin.right,
+            offsetForContextMenu.x, hFlip, endAligned, &mHFlip);
+      }
+      mIsOffset = preOffsetScreenPoint != screenPoint.x;
 
-    endAligned = mPopupAlignment == POPUPALIGNMENT_BOTTOMLEFT ||
-                 mPopupAlignment == POPUPALIGNMENT_BOTTOMRIGHT;
-    preOffsetScreenPoint = screenPoint.y;
-    if (slideVertical) {
-      mRect.height = SlideOrResize(screenPoint.y, mRect.height, screenRect.y,
-                                   screenRect.YMost(), &mAlignmentOffset);
-    } else {
-      mRect.height = FlipOrResize(
-          screenPoint.y, mRect.height, screenRect.y, screenRect.YMost(),
-          anchorRect.y, anchorRect.YMost(), margin.top, margin.bottom,
-          offsetForContextMenu.y, vFlip, endAligned, &mVFlip);
+      endAligned = mPopupAlignment == POPUPALIGNMENT_BOTTOMLEFT ||
+                   mPopupAlignment == POPUPALIGNMENT_BOTTOMRIGHT;
+      preOffsetScreenPoint = screenPoint.y;
+      if (slideVertical) {
+        mRect.height = SlideOrResize(screenPoint.y, mRect.height, screenRect.y,
+                                     screenRect.YMost(), &mAlignmentOffset);
+      } else {
+        mRect.height = FlipOrResize(
+            screenPoint.y, mRect.height, screenRect.y, screenRect.YMost(),
+            anchorRect.y, anchorRect.YMost(), margin.top, margin.bottom,
+            offsetForContextMenu.y, vFlip, endAligned, &mVFlip);
+      }
+      mIsOffset = mIsOffset || (preOffsetScreenPoint != screenPoint.y);
+
+      NS_ASSERTION(screenPoint.x >= screenRect.x &&
+                       screenPoint.y >= screenRect.y &&
+                       screenPoint.x + mRect.width <= screenRect.XMost() &&
+                       screenPoint.y + mRect.height <= screenRect.YMost(),
+                   "Popup is offscreen");
     }
-    mIsOffset = mIsOffset || (preOffsetScreenPoint != screenPoint.y);
-
-    NS_ASSERTION(screenPoint.x >= screenRect.x &&
-                     screenPoint.y >= screenRect.y &&
-                     screenPoint.x + mRect.width <= screenRect.XMost() &&
-                     screenPoint.y + mRect.height <= screenRect.YMost(),
-                 "Popup is offscreen");
   }
 
   // snap the popup's position in screen coordinates to device pixels,
@@ -1687,6 +1693,14 @@
         screen->GetAvailRect(&screenRectPixels.x, &screenRectPixels.y,
                              &screenRectPixels.width, &screenRectPixels.height);
     }
+#ifdef MOZ_WAYLAND
+    else {
+      if (GetWidget() &&
+          GetWidget()->GetScreenRect(&screenRectPixels) != NS_OK) {
+        NS_WARNING("Cannot get screen rect from widget!");
+      }
+    }
+#endif
   }
 
   if (mInContentShell) {
diff --git a/widget/ScreenManager.cpp b/widget/ScreenManager.cpp
--- a/widget/ScreenManager.cpp
+++ b/widget/ScreenManager.cpp
@@ -11,6 +11,11 @@
 #include "mozilla/dom/DOMTypes.h"
 #include "mozilla/Logging.h"
 #include "mozilla/StaticPtr.h"
+#ifdef MOZ_WAYLAND
+#  include <gdk/gdk.h>
+#  include <gdk/gdkx.h>
+#  include <gdk/gdkwayland.h>
+#endif /* MOZ_WAYLAND */
 
 static mozilla::LazyLogModule sScreenLog("WidgetScreen");
 
@@ -104,6 +109,15 @@
 NS_IMETHODIMP
 ScreenManager::ScreenForRect(int32_t aX, int32_t aY, int32_t aWidth,
                              int32_t aHeight, nsIScreen** aOutScreen) {
+#ifdef MOZ_WAYLAND
+  static bool inWayland = !GDK_IS_X11_DISPLAY(gdk_display_get_default());
+
+  if (inWayland) {
+    *aOutScreen = nullptr;
+    return NS_OK;
+  }
+#endif
+
   if (mScreenList.IsEmpty()) {
     MOZ_LOG(sScreenLog, LogLevel::Warning,
             ("No screen available. This can happen in xpcshell."));
diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -398,6 +398,9 @@
   static bool HideTitlebarByDefault();
   static bool GetTopLevelWindowActiveState(nsIFrame* aFrame);
   static bool TitlebarCanUseShapeMask();
+#ifdef MOZ_WAYLAND
+  virtual nsresult GetScreenRect(LayoutDeviceIntRect* aRect) override;
+#endif
 
  protected:
   virtual ~nsWindow();
@@ -630,6 +633,7 @@
   void HideWaylandTooltips();
   void HideWaylandPopupAndAllChildren();
   void CleanupWaylandPopups();
+  GtkWindow* GetCurrentTopmostWindow();
 
   /**
    * |mIMContext| takes all IME related stuff.
diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -1294,10 +1294,14 @@
     GdkWindow* window, const GdkRectangle* flipped_rect,
     const GdkRectangle* final_rect, gboolean flipped_x, gboolean flipped_y,
     void* aWindow) {
-  LOG(("%s [%p] flipped %d %d\n", __FUNCTION__, aWindow, flipped_rect->x,
-       flipped_rect->y));
-  LOG(("%s [%p] final %d %d\n", __FUNCTION__, aWindow, final_rect->x,
-       final_rect->y));
+  LOG(("%s [%p] flipped_x %d flipped_y %d\n", __FUNCTION__, aWindow, flipped_x,
+       flipped_y));
+
+  LOG(("%s [%p] flipped %d %d w:%d h:%d\n", __FUNCTION__, aWindow,
+       flipped_rect->x, flipped_rect->y, flipped_rect->width,
+       flipped_rect->height));
+  LOG(("%s [%p] final %d %d w:%d h:%d\n", __FUNCTION__, aWindow, final_rect->x,
+       final_rect->y, final_rect->width, final_rect->height));
 }
 #endif
 
@@ -1384,6 +1388,16 @@
     HideWaylandWindow();
   }
 
+  LOG(
+      ("nsWindow::NativeMoveResizeWaylandPopup [%p]: requested rect: x%d y%d "
+       "w%d h%d\n",
+       this, rect.x, rect.y, rect.width, rect.height));
+  if (aSize) {
+    LOG(("  aSize: x%d y%d w%d h%d\n", aSize->x, aSize->y, aSize->width,
+         aSize->height));
+  } else {
+    LOG(("  No aSize given"));
+  }
   sGdkWindowMoveToRect(gdkWindow, &rect, rectAnchor, menuAnchor, hints, 0, 0);
 
   if (isWidgetVisible) {
@@ -1399,7 +1413,8 @@
   LOG(("nsWindow::NativeMove [%p] %d %d\n", (void*)this, point.x, point.y));
 
   if (IsWaylandPopup()) {
-    NativeMoveResizeWaylandPopup(&point, nullptr);
+    GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mBounds.Size());
+    NativeMoveResizeWaylandPopup(&point, &size);
   } else if (mIsTopLevel) {
     gtk_window_move(GTK_WINDOW(mShell), point.x, point.y);
   } else if (mGdkWindow) {
@@ -6724,6 +6739,16 @@
   }
 }
 
+GtkWindow* nsWindow::GetCurrentTopmostWindow() {
+  GtkWindow* parentWindow = GTK_WINDOW(GetGtkWidget());
+  GtkWindow* topmostParentWindow;
+  while (parentWindow) {
+    topmostParentWindow = parentWindow;
+    parentWindow = gtk_window_get_transient_for(parentWindow);
+  }
+  return topmostParentWindow;
+}
+
 gint nsWindow::GdkScaleFactor() {
   GdkWindow* scaledGdkWindow = mGdkWindow;
   if (!mIsX11Display) {
@@ -6732,12 +6757,7 @@
     // not updated during it's hidden.
     if (mWindowType == eWindowType_popup || mWindowType == eWindowType_dialog) {
       // Get toplevel window for scale factor:
-      GtkWindow* parentWindow = GTK_WINDOW(GetGtkWidget());
-      GtkWindow* topmostParentWindow;
-      while (parentWindow) {
-        topmostParentWindow = parentWindow;
-        parentWindow = gtk_window_get_transient_for(parentWindow);
-      }
+      GtkWindow* topmostParentWindow = GetCurrentTopmostWindow();
       if (topmostParentWindow) {
         scaledGdkWindow =
             gtk_widget_get_window(GTK_WIDGET(topmostParentWindow));
@@ -7268,6 +7288,41 @@
   return window.forget();
 }
 
+#ifdef MOZ_WAYLAND
+nsresult nsWindow::GetScreenRect(LayoutDeviceIntRect* aRect) {
+  typedef struct _GdkMonitor GdkMonitor;
+  static auto s_gdk_display_get_monitor_at_window =
+      (GdkMonitor * (*)(GdkDisplay*, GdkWindow*))
+          dlsym(RTLD_DEFAULT, "gdk_display_get_monitor_at_window");
+
+  static auto s_gdk_monitor_get_workarea =
+      (void (*)(GdkMonitor*, GdkRectangle*))dlsym(RTLD_DEFAULT,
+                                                  "gdk_monitor_get_workarea");
+
+  if (!s_gdk_display_get_monitor_at_window || !s_gdk_monitor_get_workarea) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  GtkWindow* topmostParentWindow = GetCurrentTopmostWindow();
+  GdkWindow* gdkWindow = gtk_widget_get_window(GTK_WIDGET(topmostParentWindow));
+
+  GdkMonitor* monitor =
+      s_gdk_display_get_monitor_at_window(gdk_display_get_default(), gdkWindow);
+  if (monitor) {
+    GdkRectangle workArea;
+    s_gdk_monitor_get_workarea(monitor, &workArea);
+    aRect->x = workArea.x;
+    aRect->y = workArea.y;
+    aRect->width = workArea.width;
+    aRect->height = workArea.height;
+    LOG(("  workarea for [%p], monitor %p: x%d y%d w%d h%d\n", this, monitor,
+         workArea.x, workArea.y, workArea.width, workArea.height));
+    return NS_OK;
+  }
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+#endif
+
 bool nsWindow::GetTopLevelWindowActiveState(nsIFrame* aFrame) {
   // Used by window frame and button box rendering. We can end up in here in
   // the content process when rendering one of these moz styles freely in a
diff --git a/widget/moz.build b/widget/moz.build
--- a/widget/moz.build
+++ b/widget/moz.build
@@ -210,7 +210,6 @@
     'PuppetBidiKeyboard.cpp',
     'PuppetWidget.cpp',
     'Screen.cpp',
-    'ScreenManager.cpp',
     'SharedWidgetUtils.cpp',
     'TextEventDispatcher.cpp',
     'VsyncDispatcher.cpp',
@@ -242,6 +241,7 @@
 SOURCES += [
     'nsBaseDragService.cpp',
     'nsBaseWidget.cpp',
+    'ScreenManager.cpp',
 ]
 
 if CONFIG['MOZ_INSTRUMENT_EVENT_LOOP']:
diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -1713,6 +1713,15 @@
     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.
+#ifdef MOZ_WAYLAND
+  virtual nsresult GetScreenRect(LayoutDeviceIntRect* aRect) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+#endif
+
  private:
   class LongTapInfo {
    public: