Bug 1965143 - Remove X11 special-case for noautohide popups that end up using toplevel windows. r=stransky

As noted in the comment for Wayland, most noautohide popups already work
this way, as the noautohide attribute is dynamic. I did a quick smoke test of
these popups and they still remain not autohiding as expected, and follow their
anchor and so on.

Differential Revision: https://phabricator.services.mozilla.com/D248413
This commit is contained in:
Emilio Cobos Álvarez
2025-05-12 10:21:08 +00:00
committed by ealvarez@mozilla.com
parent f8995ac2ee
commit 7d331ed1ee
4 changed files with 6 additions and 136 deletions

View File

@@ -284,7 +284,6 @@ nsresult nsMenuPopupFrame::CreateWidgetForView(nsView* aView) {
widgetData.mBorderStyle = widget::BorderStyle::Default; widgetData.mBorderStyle = widget::BorderStyle::Default;
widgetData.mClipSiblings = true; widgetData.mClipSiblings = true;
widgetData.mPopupHint = mPopupType; widgetData.mPopupHint = mPopupType;
widgetData.mNoAutoHide = IsNoAutoHide();
if (!mInContentShell) { if (!mInContentShell) {
// A drag popup may be used for non-static translucent drag feedback // A drag popup may be used for non-static translucent drag feedback
@@ -300,7 +299,7 @@ nsresult nsMenuPopupFrame::CreateWidgetForView(nsView* aView) {
const auto mode = nsLayoutUtils::GetFrameTransparency(this, this); const auto mode = nsLayoutUtils::GetFrameTransparency(this, this);
widgetData.mHasRemoteContent = remote; widgetData.mHasRemoteContent = remote;
widgetData.mTransparencyMode = mode; widgetData.mTransparencyMode = mode;
widgetData.mPopupLevel = GetPopupLevel(widgetData.mNoAutoHide); widgetData.mPopupLevel = GetPopupLevel(IsNoAutoHide());
nsCOMPtr<nsIWidget> parentWidget = ComputeParentWidget(); nsCOMPtr<nsIWidget> parentWidget = ComputeParentWidget();
if (NS_WARN_IF(!parentWidget)) { if (NS_WARN_IF(!parentWidget)) {

View File

@@ -83,7 +83,6 @@ struct InitData {
bool mClipChildren = false; bool mClipChildren = false;
bool mClipSiblings = false; bool mClipSiblings = false;
bool mRTL = false; bool mRTL = false;
bool mNoAutoHide = false; // true for noautohide panels
bool mIsDragPopup = false; // true for drag feedback panels bool mIsDragPopup = false; // true for drag feedback panels
// true if window creation animation is suppressed, e.g. for session restore // true if window creation animation is suppressed, e.g. for session restore
bool mIsAnimationSuppressed = false; bool mIsAnimationSuppressed = false;

View File

@@ -249,17 +249,6 @@ static gboolean touch_event_cb(GtkWidget* aWidget, GdkEventTouch* aEvent);
static gboolean generic_event_cb(GtkWidget* widget, GdkEvent* aEvent); static gboolean generic_event_cb(GtkWidget* widget, GdkEvent* aEvent);
static void widget_destroy_cb(GtkWidget* widget, gpointer user_data); static void widget_destroy_cb(GtkWidget* widget, gpointer user_data);
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#ifdef MOZ_X11
static GdkFilterReturn popup_take_focus_filter(GdkXEvent* gdk_xevent,
GdkEvent* event, gpointer data);
#endif /* MOZ_X11 */
#ifdef __cplusplus
}
#endif /* __cplusplus */
static gboolean drag_motion_event_cb(GtkWidget* aWidget, static gboolean drag_motion_event_cb(GtkWidget* aWidget,
GdkDragContext* aDragContext, gint aX, GdkDragContext* aDragContext, gint aX,
gint aY, guint aTime, gpointer aData); gint aY, guint aTime, gpointer aData);
@@ -445,7 +434,6 @@ nsWindow::nsWindow()
mPendingBoundsChangeMayChangeCsdMargin(false), mPendingBoundsChangeMayChangeCsdMargin(false),
mTitlebarBackdropState(false), mTitlebarBackdropState(false),
mAlwaysOnTop(false), mAlwaysOnTop(false),
mNoAutoHide(false),
mIsTransparent(false), mIsTransparent(false),
mHasReceivedSizeAllocate(false), mHasReceivedSizeAllocate(false),
mWidgetCursorLocked(false), mWidgetCursorLocked(false),
@@ -1049,8 +1037,9 @@ void nsWindow::ResizeInt(const Maybe<LayoutDeviceIntPoint>& aMove,
NativeMoveResize(moved, resized); NativeMoveResize(moved, resized);
// We optimistically assume size changes immediately in two cases: // We optimistically assume size/position changes immediately in two cases:
// 1. Override-redirect window: Size is controlled by only us. //
// 1. Popup: Size is controlled by only us.
// 2. Managed window that has not not yet received a size-allocate event: // 2. Managed window that has not not yet received a size-allocate event:
// Resize() Callers expect initial sizes to be applied synchronously. // Resize() Callers expect initial sizes to be applied synchronously.
// If the size request is not honored, then we'll correct in // If the size request is not honored, then we'll correct in
@@ -1064,8 +1053,7 @@ void nsWindow::ResizeInt(const Maybe<LayoutDeviceIntPoint>& aMove,
// So we don't update mBounds until OnContainerSizeAllocate() when we know the // So we don't update mBounds until OnContainerSizeAllocate() when we know the
// request is granted. // request is granted.
bool isOrWillBeVisible = mHasReceivedSizeAllocate || mNeedsShow || mIsShown; bool isOrWillBeVisible = mHasReceivedSizeAllocate || mNeedsShow || mIsShown;
if (!isOrWillBeVisible || if (!isOrWillBeVisible || IsPopup()) {
gtk_window_get_window_type(GTK_WINDOW(mShell)) == GTK_WINDOW_POPUP) {
mBounds.SizeTo(aSize); mBounds.SizeTo(aSize);
if (moved) { if (moved) {
mBounds.MoveTo(*aMove); mBounds.MoveTo(*aMove);
@@ -6012,30 +6000,18 @@ nsresult nsWindow::Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect,
} }
mAlwaysOnTop = aInitData && aInitData->mAlwaysOnTop; mAlwaysOnTop = aInitData && aInitData->mAlwaysOnTop;
// mNoAutoHide seems to be always false here.
// The mNoAutoHide state is set later on nsMenuPopupFrame level
// and can be changed so we use WaylandPopupIsPermanent() to get
// recent popup config (Bug 1728952).
mNoAutoHide = aInitData && aInitData->mNoAutoHide;
mIsAlert = aInitData && aInitData->mIsAlert; mIsAlert = aInitData && aInitData->mIsAlert;
mIsDragPopup = aInitData && aInitData->mIsDragPopup; mIsDragPopup = aInitData && aInitData->mIsDragPopup;
// Popups that are not noautohide are only temporary. The are used // For popups, use the standard GtkWindowType GTK_WINDOW_POPUP,
// for menus and the like and disappear when another window is used.
// For most popups, use the standard GtkWindowType GTK_WINDOW_POPUP,
// which will use a Window with the override-redirect attribute // which will use a Window with the override-redirect attribute
// (for temporary windows). // (for temporary windows).
// For long-lived windows, their stacking order is managed by the
// window manager, as indicated by GTK_WINDOW_TOPLEVEL.
// For Wayland we have to always use GTK_WINDOW_POPUP to control // For Wayland we have to always use GTK_WINDOW_POPUP to control
// popup window position. // popup window position.
GtkWindowType type = GTK_WINDOW_TOPLEVEL; GtkWindowType type = GTK_WINDOW_TOPLEVEL;
if (mWindowType == WindowType::Popup) { if (mWindowType == WindowType::Popup) {
MOZ_ASSERT(aInitData); MOZ_ASSERT(aInitData);
type = GTK_WINDOW_POPUP; type = GTK_WINDOW_POPUP;
if (GdkIsX11Display() && mNoAutoHide) {
type = GTK_WINDOW_TOPLEVEL;
}
} }
mShell = gtk_window_new(type); mShell = gtk_window_new(type);
@@ -6127,35 +6103,6 @@ nsresult nsWindow::Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect,
LOG(" nsWindow::Create() Popup"); LOG(" nsWindow::Create() Popup");
if (mNoAutoHide) {
// ... but the window manager does not decorate this window,
// nor provide a separate taskbar icon.
if (mBorderStyle == BorderStyle::Default) {
gtk_window_set_decorated(GTK_WINDOW(mShell), FALSE);
} else {
bool decorate = bool(mBorderStyle & BorderStyle::Title);
gtk_window_set_decorated(GTK_WINDOW(mShell), decorate);
if (decorate) {
gtk_window_set_deletable(GTK_WINDOW(mShell),
bool(mBorderStyle & BorderStyle::Close));
}
}
gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mShell), TRUE);
// Element focus is managed by the parent window so the
// WM_HINTS input field is set to False to tell the window
// manager not to set input focus to this window ...
gtk_window_set_accept_focus(GTK_WINDOW(mShell), FALSE);
#ifdef MOZ_X11
// ... but when the window manager offers focus through
// WM_TAKE_FOCUS, focus is requested on the parent window.
if (GdkIsX11Display()) {
gtk_widget_realize(mShell);
gdk_window_add_filter(GetToplevelGdkWindow(), popup_take_focus_filter,
nullptr);
}
#endif
}
if (mIsDragPopup) { if (mIsDragPopup) {
gtk_window_set_type_hint(GTK_WINDOW(mShell), GDK_WINDOW_TYPE_HINT_DND); gtk_window_set_type_hint(GTK_WINDOW(mShell), GDK_WINDOW_TYPE_HINT_DND);
LOG(" nsWindow::Create() Drag popup\n"); LOG(" nsWindow::Create() Drag popup\n");
@@ -8085,74 +8032,6 @@ static gboolean focus_out_event_cb(GtkWidget* widget, GdkEventFocus* event) {
return FALSE; return FALSE;
} }
#ifdef MOZ_X11
// For long-lived popup windows that don't really take focus themselves but
// may have elements that accept keyboard input when the parent window is
// active, focus is handled specially. These windows include noautohide
// panels. (This special handling is not necessary for temporary popups where
// the keyboard is grabbed.)
//
// Mousing over or clicking on these windows should not cause them to steal
// focus from their parent windows, so, the input field of WM_HINTS is set to
// False to request that the window manager not set the input focus to this
// window. http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7
//
// However, these windows can still receive WM_TAKE_FOCUS messages from the
// window manager, so they can still detect when the user has indicated that
// they wish to direct keyboard input at these windows. When the window
// manager offers focus to these windows (after a mouse over or click, for
// example), a request to make the parent window active is issued. When the
// parent window becomes active, keyboard events will be received.
static GdkFilterReturn popup_take_focus_filter(GdkXEvent* gdk_xevent,
GdkEvent* event, gpointer data) {
auto* xevent = static_cast<XEvent*>(gdk_xevent);
if (xevent->type != ClientMessage) {
return GDK_FILTER_CONTINUE;
}
XClientMessageEvent& xclient = xevent->xclient;
if (xclient.message_type != gdk_x11_get_xatom_by_name("WM_PROTOCOLS")) {
return GDK_FILTER_CONTINUE;
}
Atom atom = xclient.data.l[0];
if (atom != gdk_x11_get_xatom_by_name("WM_TAKE_FOCUS")) {
return GDK_FILTER_CONTINUE;
}
guint32 timestamp = xclient.data.l[1];
GtkWidget* widget = get_gtk_widget_for_gdk_window(event->any.window);
if (!widget) {
return GDK_FILTER_CONTINUE;
}
GtkWindow* parent = gtk_window_get_transient_for(GTK_WINDOW(widget));
if (!parent) {
return GDK_FILTER_CONTINUE;
}
if (gtk_window_is_active(parent)) {
return GDK_FILTER_REMOVE; // leave input focus on the parent
}
GdkWindow* parent_window = gtk_widget_get_window(GTK_WIDGET(parent));
if (!parent_window) {
return GDK_FILTER_CONTINUE;
}
// In case the parent has not been deiconified.
gdk_window_show_unraised(parent_window);
// Request focus on the parent window.
// Use gdk_window_focus rather than gtk_window_present to avoid
// raising the parent window.
gdk_window_focus(parent_window, timestamp);
return GDK_FILTER_REMOVE;
}
#endif /* MOZ_X11 */
static gboolean key_press_event_cb(GtkWidget* widget, GdkEventKey* event) { static gboolean key_press_event_cb(GtkWidget* widget, GdkEventKey* event) {
LOGW("key_press_event_cb\n"); LOGW("key_press_event_cb\n");
@@ -9788,12 +9667,6 @@ void nsWindow::OnMap() {
} }
if (mWindowType == WindowType::Popup) { if (mWindowType == WindowType::Popup) {
if (mNoAutoHide) {
gint wmd = ConvertBorderStyles(mBorderStyle);
if (wmd != -1) {
gdk_window_set_decorations(mGdkWindow, (GdkWMDecoration)wmd);
}
}
// If the popup ignores mouse events, set an empty input shape. // If the popup ignores mouse events, set an empty input shape.
SetInputRegion(mInputRegion); SetInputRegion(mInputRegion);
} }

View File

@@ -704,7 +704,6 @@ class nsWindow final : public nsBaseWidget {
// Draw titlebar with :backdrop css state (inactive/unfocused). // Draw titlebar with :backdrop css state (inactive/unfocused).
bool mTitlebarBackdropState : 1; bool mTitlebarBackdropState : 1;
bool mAlwaysOnTop : 1; bool mAlwaysOnTop : 1;
bool mNoAutoHide : 1;
bool mIsTransparent : 1; bool mIsTransparent : 1;
// We can expect at least one size-allocate event after early resizes. // We can expect at least one size-allocate event after early resizes.
bool mHasReceivedSizeAllocate : 1; bool mHasReceivedSizeAllocate : 1;