Commit 50cee5c3 authored by stransky's avatar stransky
Browse files

Bug 1718727 [Wayland] Don't constrain popup size by screen size on Wayland, r=emilio

parent b6f99ae0
Loading
Loading
Loading
Loading
+86 −98
Original line number Diff line number Diff line
@@ -58,9 +58,6 @@
#include "mozilla/dom/KeyboardEvent.h"
#include "mozilla/dom/KeyboardEventBinding.h"
#include <algorithm>
#ifdef MOZ_WAYLAND
#  include "mozilla/WidgetUtilsGtk.h"
#endif /* MOZ_WAYLAND */

#include "X11UndefineNone.h"

@@ -74,13 +71,12 @@ int8_t nsMenuPopupFrame::sDefaultLevelIsTop = -1;

DOMTimeStamp nsMenuPopupFrame::sLastKeyTime = 0;

static bool IsWaylandDisplay() {
#ifdef MOZ_WAYLAND
  return mozilla::widget::GdkIsWaylandDisplay();
#  include "mozilla/WidgetUtilsGtk.h"
#  define IS_WAYLAND_DISPLAY() mozilla::widget::GdkIsWaylandDisplay()
#else
  return false;
#  define IS_WAYLAND_DISPLAY() false
#endif
}

// NS_NewMenuPopupFrame
//
@@ -108,6 +104,7 @@ nsMenuPopupFrame::nsMenuPopupFrame(ComputedStyle* aStyle,
      mPrefSize(-1, -1),
      mXPos(0),
      mYPos(0),
      mAnchorRect(),
      mAlignmentOffset(0),
      mLastClientOffset(0, 0),
      mPopupType(ePopupTypePanel),
@@ -558,7 +555,7 @@ void nsMenuPopupFrame::LayoutPopup(nsBoxLayoutState& aState,
  }
  prefSize = XULBoundsCheck(minSize, prefSize, maxSize);

  if (IsWaylandDisplay()) {
  if (IS_WAYLAND_DISPLAY()) {
    // If prefSize it is not a whole number in css pixels we need round it up
    // to avoid reflow of the tooltips/popups and putting the text on two lines
    // (usually happens with 200% scale factor and font scale factor <> 1)
@@ -579,14 +576,12 @@ void nsMenuPopupFrame::LayoutPopup(nsBoxLayoutState& aState,
    shouldPosition = true;
    SetXULBounds(aState, nsRect(0, 0, prefSize.width, prefSize.height), false);
    mPrefSize = prefSize;
#if MOZ_WAYLAND
    nsIWidget* widget = GetWidget();
    if (widget && mPopupState != ePopupShown) {
    if (mPopupState != ePopupShown && widget && IS_WAYLAND_DISPLAY()) {
      // When the popup size changed in the DOM, we need to flush widget
      // preferred popup rect to avoid showing it in wrong size.
      widget->FlushPreferredPopupRect();
    }
#endif
  }

  bool needCallback = false;
@@ -909,7 +904,7 @@ void nsMenuPopupFrame::InitializePopupAtScreen(nsIContent* aTriggerContent,
  mPosition = POPUPPOSITION_UNKNOWN;
  mIsContextMenu = aIsContextMenu;
  // Wayland does menu adjustments at widget code
  mAdjustOffsetForContextMenu = IsWaylandDisplay() ? false : aIsContextMenu;
  mAdjustOffsetForContextMenu = aIsContextMenu && !IS_WAYLAND_DISPLAY();
  mIsNativeMenu = false;
  mAnchorType = MenuPopupAnchorType_Point;
  mPositionedOffset = 0;
@@ -929,7 +924,7 @@ void nsMenuPopupFrame::InitializePopupAsNativeContextMenu(
  mPosition = POPUPPOSITION_UNKNOWN;
  mIsContextMenu = true;
  // Wayland does menu adjustments at widget code
  mAdjustOffsetForContextMenu = !IsWaylandDisplay();
  mAdjustOffsetForContextMenu = !IS_WAYLAND_DISPLAY();
  mIsNativeMenu = true;
  mAnchorType = MenuPopupAnchorType_Point;
  mPositionedOffset = 0;
@@ -1500,13 +1495,10 @@ nsresult nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame,
      // tell us which axis the popup is flush against in case we have to move
      // it around later. The AdjustPositionForAnchorAlign method accounts for
      // the popup's margin.

#ifdef MOZ_WAYLAND
      if (IsWaylandDisplay()) {
      if (IS_WAYLAND_DISPLAY()) {
        screenPoint = nsPoint(anchorRect.x, anchorRect.y);
        mAnchorRect = anchorRect;
      }
#endif
      screenPoint = AdjustPositionForAnchorAlign(anchorRect, hFlip, vFlip);
    } else {
      // with no anchor, the popup is positioned relative to the root frame
@@ -1598,14 +1590,30 @@ nsresult nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame,

  nscoord oldAlignmentOffset = mAlignmentOffset;

  if (IS_WAYLAND_DISPLAY()) {
    if (nsIWidget* widget = GetWidget()) {
      nsRect prefRect = widget->GetPreferredPopupRect();
      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;
        }
      }
    }
  }

  // If a panel is being moved or has flip="none", don't constrain or flip it,
  // in order to avoid visual noise when moving windows between screens.
  // 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 (mInContentShell ||
  if (!IS_WAYLAND_DISPLAY() &&
      (mInContentShell ||
       (mFlip != FlipType_None &&
       (!aIsMove || mIsOffset || mPopupType != ePopupTypePanel))) {
        (!aIsMove || mIsOffset || mPopupType != ePopupTypePanel)))) {
    int32_t appPerDev = presContext->AppUnitsPerDevPixel();
    LayoutDeviceIntRect anchorRectDevPix =
        LayoutDeviceIntRect::FromAppUnitsToNearest(anchorRect, appPerDev);
@@ -1615,30 +1623,13 @@ nsresult nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame,
        GetConstraintRect(anchorRectDevPix, rootScreenRectDevPix, popupLevel);
    nsRect screenRect =
        LayoutDeviceIntRect::ToAppUnits(screenRectDevPix, appPerDev);

    // Ensure that anchorRect is on screen.
    anchorRect = anchorRect.Intersect(screenRect);

#ifdef MOZ_WAYLAND
    nsIWidget* widget = GetWidget();
    if (widget) {
      nsRect prefRect = widget->GetPreferredPopupRect();
      if (prefRect.width > 0 && prefRect.height > 0) {
        screenRect = prefRect;
      }
    } else {
      NS_WARNING("No widget associated with popup frame.");
    }
#endif
    // shrink the the popup down if it is larger than the screen size
    if (mRect.width > screenRect.width) mRect.width = screenRect.width;
    if (mRect.height > screenRect.height) mRect.height = screenRect.height;

    // 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 (!IsWaylandDisplay()) {
    // 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.
@@ -1695,7 +1686,6 @@ nsresult nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame,
                     screenPoint.y + mRect.height <= screenRect.YMost(),
                 "Popup is offscreen");
  }
  }

  // snap the popup's position in screen coordinates to device pixels,
  // see bug 622507, bug 961431
@@ -1775,13 +1765,18 @@ LayoutDeviceIntRect nsMenuPopupFrame::GetConstraintRect(
    const LayoutDeviceIntRect& aRootScreenRect, nsPopupLevel aPopupLevel) {
  LayoutDeviceIntRect screenRectPixels;

  // GetConstraintRect() does not work on Wayland as we can't get absolute
  // window position there.
  MOZ_ASSERT(!IS_WAYLAND_DISPLAY(),
             "GetConstraintRect does not work on Wayland");

  // determine the available screen space. It will be reduced by the OS chrome
  // such as menubars. It addition, for content shells, it will be the area of
  // the content rather than the screen.
  nsCOMPtr<nsIScreen> screen;
  nsCOMPtr<nsIScreenManager> sm(
      do_GetService("@mozilla.org/gfx/screenmanager;1"));
  if (sm && !IsWaylandDisplay()) {
  if (sm) {
    // for content shells, get the screen where the root frame is located.
    // This is because we need to constrain the content to this content area,
    // so we should use the same screen. Otherwise, use the screen where the
@@ -1806,13 +1801,6 @@ LayoutDeviceIntRect nsMenuPopupFrame::GetConstraintRect(
                             &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) {
    // for content shells, clip to the client area rather than the screen area
+3 −6
Original line number Diff line number Diff line
@@ -518,14 +518,12 @@ class nsMenuPopupFrame final : public nsBoxFrame,
  // frame hierarchy, it's needed for Linux/Wayland which demands
  // strict popup windows hierarchy.
  nsIWidget* GetParentMenuWidget();
#ifdef MOZ_WAYLAND
  // We need following getters for Wayland for calling gdk_window_move_to_rect

  // These are used by Wayland backend.
  nsRect GetAnchorRect() { return mAnchorRect; }
  int GetPopupAlignment() { return mPopupAlignment; }
  int GetPopupAnchor() { return mPopupAnchor; }
  int GetPopupPosition() { return mPosition; }
  FlipType GetFlipType() { return mFlip; }
#endif

 protected:
  nsString mIncrementalString;  // for incremental typing navigation
@@ -560,9 +558,8 @@ class nsMenuPopupFrame final : public nsBoxFrame,
  nsIntRect mScreenRect;
  // Used for store rectangle which the popup is going to be anchored to,
  // we need that for Wayland
#ifdef MOZ_WAYLAND
  nsRect mAnchorRect;
#endif

  // If the panel prefers to "slide" rather than resize, then the arrow gets
  // positioned at this offset (along either the x or y axis, depending on
  // mPosition)
+0 −21
Original line number Diff line number Diff line
@@ -2195,11 +2195,9 @@ void nsWindow::WaylandPopupMove(bool aUseMoveToRect) {
    p2a = AppUnitsPerCSSPixel() / gfxPlatformGtk::GetFontScaleFactor();
  }

#ifdef MOZ_WAYLAND
  nsRect anchorRectAppUnits = popupFrame->GetAnchorRect();
  anchorRect = LayoutDeviceIntRect::FromUnknownRect(
      anchorRectAppUnits.ToNearestPixels(p2a));
#endif

  // Anchor rect is in the toplevel coordinates but we need to transfer it to
  // the coordinates relative to the popup parent for the
@@ -2249,13 +2247,11 @@ void nsWindow::WaylandPopupMove(bool aUseMoveToRect) {
      rectAnchor = GDK_GRAVITY_SOUTH_EAST;
      menuAnchor = GDK_GRAVITY_NORTH_WEST;
    }
#ifdef MOZ_WAYLAND
  } else {
    rectAnchor = PopupAlignmentToGdkGravity(popupFrame->GetPopupAnchor());
    menuAnchor = PopupAlignmentToGdkGravity(popupFrame->GetPopupAlignment());
    flipType = popupFrame->GetFlipType();
    position = popupFrame->GetAlignmentPosition();
#endif
  }

  LOG_POPUP(("  parentRect gravity: %d anchor gravity: %d\n", rectAnchor,
@@ -9160,23 +9156,6 @@ void nsWindow::UnlockNativePointer() {
    mLockedPointer = nullptr;
  }
}

nsresult nsWindow::GetScreenRect(LayoutDeviceIntRect* aRect) {
  GtkWindow* topmostParentWindow = GetCurrentTopmostWindow();
  nsWindow* window = get_window_for_gtk_widget(GTK_WIDGET(topmostParentWindow));
  if (!window) {
    return NS_ERROR_FAILURE;
  }

  GdkRectangle rect;
  ScreenHelperGTK::GetScreenRectForWindow(window, &rect);

  aRect->x = aRect->y = 0;
  aRect->width = rect.width;
  aRect->height = rect.height;

  return NS_OK;
}
#endif

bool nsWindow::GetTopLevelWindowActiveState(nsIFrame* aFrame) {
+0 −1
Original line number Diff line number Diff line
@@ -398,7 +398,6 @@ class nsWindow final : public nsBaseWidget {
      const LayoutDeviceIntPoint& aLockCenter) override;
  virtual void LockNativePointer() override;
  virtual void UnlockNativePointer() override;
  virtual nsresult GetScreenRect(LayoutDeviceIntRect* aRect) override;
  virtual nsRect GetPreferredPopupRect() override {
    return mPreferredPopupRect;
  };
+0 −6
Original line number Diff line number Diff line
@@ -1790,10 +1790,6 @@ class nsIWidget : public nsISupports {
  // 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;
  }
  virtual nsRect GetPreferredPopupRect() {
    NS_WARNING("GetPreferredPopupRect implemented only for wayland");
    return nsRect(0, 0, 0, 0);
@@ -1803,8 +1799,6 @@ class nsIWidget : public nsISupports {
    return;
  }

#endif

  /**
   * If this widget uses native pointer lock instead of warp-to-center
   * (currently only GTK on Wayland), these methods provide access to that