Bug 1532643 - [Wayland] Set popup window hierarchy run-time on Wayland for menus in the same frame hierarchy, r=NeilDeakin
authorMartin Stransky <stransky@redhat.com>
Wed, 17 Apr 2019 14:19:57 +0000
changeset 470542 b9a9068783f7848082d2a6559974ade2a78b3e6f
parent 470541 f40a3485926cadae4bb326767a3ff5268a1814ec
child 470543 1ed5609db4afed63c7b9dd79e2426e907fd19458
push id35908
push useraciure@mozilla.com
push dateWed, 24 Apr 2019 04:28:40 +0000
treeherdermozilla-central@c9f0730a57a6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersNeilDeakin
bugs1532643
milestone68.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 1532643 - [Wayland] Set popup window hierarchy run-time on Wayland for menus in the same frame hierarchy, r=NeilDeakin Wayland protocol allows to have only one popup window attached to a parent widget. Recently we use a toplevel window widget as parent for all popups. That means a second level menu (like File -> New Container Tab) is not displayed as both ("File" and "New Container Tab" menus) have the same parent widget. As a solution allow to get the actual parent run-time and set that when we open the window on toolkit level. This patch covers menu widgets in the same frame hierarchy. Differential Revision: https://phabricator.services.mozilla.com/D26112
layout/xul/nsMenuPopupFrame.cpp
layout/xul/nsMenuPopupFrame.h
widget/gtk/nsWindow.cpp
widget/gtk/nsWindow.h
--- a/layout/xul/nsMenuPopupFrame.cpp
+++ b/layout/xul/nsMenuPopupFrame.cpp
@@ -2480,8 +2480,20 @@ void nsMenuPopupFrame::CheckForAnchorCha
   nsRect anchorRect = ComputeAnchorRect(rootPresContext, anchor);
 
   // If the rectangles are different, move the popup.
   if (!anchorRect.IsEqualEdges(aRect)) {
     aRect = anchorRect;
     SetPopupPosition(nullptr, true, false, true);
   }
 }
+
+nsIWidget* nsMenuPopupFrame::GetParentMenuWidget()
+{
+  nsMenuFrame* menuFrame = do_QueryFrame(GetParent());
+  if (menuFrame) {
+    nsMenuParent* parentPopup = menuFrame->GetMenuParent();
+    if (parentPopup && parentPopup->IsMenu()) {
+      return static_cast<nsMenuPopupFrame*>(parentPopup)->GetWidget();
+    }
+  }
+  return nullptr;
+}
--- a/layout/xul/nsMenuPopupFrame.h
+++ b/layout/xul/nsMenuPopupFrame.h
@@ -540,16 +540,21 @@ class nsMenuPopupFrame final : public ns
   // is no longer visible, or a parent deck page is changed, the popup hides
   // as well. The second variation also sets the anchor rectangle, relative to
   // the popup frame.
   bool ShouldFollowAnchor();
 
  public:
   bool ShouldFollowAnchor(nsRect& aRect);
 
+  // Returns parent menu widget for submenus that are in the same
+  // frame hierarchy, it's needed for Linux/Wayland which demands
+  // strict popup windows hierarchy.
+  nsIWidget* GetParentMenuWidget();
+
  protected:
   nsString mIncrementalString;  // for incremental typing navigation
 
   // the content that the popup is anchored to, if any, which may be in a
   // different document than the popup.
   nsCOMPtr<nsIContent> mAnchorContent;
 
   // the content that triggered the popup, typically the node where the mouse
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -29,16 +29,17 @@
 #include "nsINode.h"
 
 #include "nsWidgetsCID.h"
 #include "nsDragService.h"
 #include "nsIWidgetListener.h"
 #include "nsIScreenManager.h"
 #include "SystemTimeConverter.h"
 #include "nsViewManager.h"
+#include "nsMenuPopupFrame.h"
 
 #include "nsGtkKeyUtils.h"
 #include "nsGtkCursors.h"
 #include "ScreenHelperGTK.h"
 
 #include <gtk/gtk.h>
 #include <gtk/gtkx.h>
 
@@ -3446,21 +3447,17 @@ nsresult nsWindow::Create(nsIWidget *aPa
 
         GdkWindowTypeHint gtkTypeHint;
         if (aInitData->mIsDragPopup) {
           gtkTypeHint = GDK_WINDOW_TYPE_HINT_DND;
           mIsDragPopup = true;
         } else {
           switch (aInitData->mPopupHint) {
             case ePopupTypeMenu:
-              // Use GDK_WINDOW_TYPE_HINT_UTILITY on Wayland which
-              // guides Gtk to create the popup as subsurface
-              // instead of xdg_shell popup (see Bug 1423598).
-              gtkTypeHint = mIsX11Display ? GDK_WINDOW_TYPE_HINT_POPUP_MENU
-                                          : GDK_WINDOW_TYPE_HINT_UTILITY;
+              gtkTypeHint = GDK_WINDOW_TYPE_HINT_POPUP_MENU;
               break;
             case ePopupTypeTooltip:
               gtkTypeHint = GDK_WINDOW_TYPE_HINT_TOOLTIP;
               break;
             default:
               gtkTypeHint = GDK_WINDOW_TYPE_HINT_UTILITY;
               break;
           }
@@ -3951,17 +3948,23 @@ void nsWindow::NativeShow(bool aAction) 
     // unset our flag now that our window has been shown
     mNeedsShow = false;
 
     if (mIsTopLevel) {
       // Set up usertime/startupID metadata for the created window.
       if (mWindowType != eWindowType_invisible) {
         SetUserTimeAndStartupIDForActivatedWindow(mShell);
       }
-
+      // Update popup window hierarchy run-time on Wayland.
+      if (!mIsX11Display && mWindowType == eWindowType_popup) {
+        GtkWindow *parentWindow = GetPopupParentWindow();
+        if (parentWindow) {
+          gtk_window_set_transient_for(GTK_WINDOW(mShell), parentWindow);
+        }
+      }
       gtk_widget_show(mShell);
     } else if (mContainer) {
       gtk_widget_show(GTK_WIDGET(mContainer));
     } else if (mGdkWindow) {
       gdk_window_show_unraised(mGdkWindow);
     }
   } else {
 #ifdef MOZ_WAYLAND
@@ -5435,16 +5438,20 @@ static gboolean key_press_event_cb(GtkWi
 #ifdef MOZ_X11
   // Keyboard repeat can cause key press events to queue up when there are
   // slow event handlers (bug 301029).  Throttle these events by removing
   // consecutive pending duplicate KeyPress events to the same window.
   // We use the event time of the last one.
   // Note: GDK calls XkbSetDetectableAutorepeat so that KeyRelease events
   // are generated only when the key is physically released.
 #  define NS_GDKEVENT_MATCH_MASK 0x1FFF  // GDK_SHIFT_MASK .. GDK_BUTTON5_MASK
+  // Our headers undefine X11 KeyPress - let's redefine it here.
+#ifndef KeyPress
+  #define KeyPress 2
+#endif
   GdkDisplay *gdkDisplay = gtk_widget_get_display(widget);
   if (GDK_IS_X11_DISPLAY(gdkDisplay)) {
     Display *dpy = GDK_DISPLAY_XDISPLAY(gdkDisplay);
     while (XPending(dpy)) {
       XEvent next_event;
       XPeekEvent(dpy, &next_event);
       GdkWindow *nextGdkWindow =
           gdk_x11_window_lookup_for_display(gdkDisplay, next_event.xany.window);
@@ -6806,8 +6813,24 @@ void nsWindow::ForceTitlebarRedraw(void)
   }
 
   frame = FindTitlebarFrame(frame);
   if (frame) {
     nsLayoutUtils::PostRestyleEvent(frame->GetContent()->AsElement(),
                                     RestyleHint{0}, nsChangeHint_RepaintFrame);
   }
 }
+
+GtkWindow* nsWindow::GetPopupParentWindow()
+{
+  nsView* view = nsView::GetViewFor(this);
+  if (!view) {
+    return nullptr;
+  }
+  nsIFrame* frame = view->GetFrame();
+  if (!frame) {
+    return nullptr;
+  }
+  nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(frame);
+  nsWindow* window =
+    static_cast<nsWindow*>(menuPopupFrame->GetParentMenuWidget());
+  return window ? GTK_WINDOW(window->GetGtkWidget()) : nullptr;
+}
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -600,16 +600,19 @@ class nsWindow final : public nsBaseWidg
   void SetCompositorWidgetDelegate(CompositorWidgetDelegate* delegate) override;
 
   void CleanLayerManagerRecursive();
 
   virtual int32_t RoundsWidgetCoordinatesTo() override;
 
   void ForceTitlebarRedraw();
 
+  // This is used by Wayland backend to keep strict popup window hierarchy.
+  GtkWindow* GetPopupParentWindow();
+
   /**
    * |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.
    *
    * The instance is created when the top level widget is created.  And when
    * the widget is destroyed, it's released.  All child windows refer its