diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h --- a/widget/gtk/nsWindow.h +++ b/widget/gtk/nsWindow.h @@ -687,6 +687,9 @@ void HideWaylandPopupAndAllChildren(); void CleanupWaylandPopups(); GtkWindow* GetCurrentTopmostWindow(); + GtkWindow* GetCurrentWindow(); + GtkWindow* GetTopmostWindow(); + bool IsWidgetOverflowWindow(); /** * |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 @@ -1193,20 +1193,26 @@ while (popupList) { LOG((" Looking for %p [nsWindow]\n", popupList->data)); nsWindow* waylandWnd = static_cast(popupList->data); - bool popupFound = false; - for (unsigned long i = 0; i < widgetChain.Length(); i++) { - if (waylandWnd == widgetChain[i]) { - popupFound = true; - break; + // Remove only menu popups or empty frames - they are most likely + // already rolledup popups + if (waylandWnd->IsMainMenuWindow() || !waylandWnd->GetFrame()) { + bool popupFound = false; + for (unsigned long i = 0; i < widgetChain.Length(); i++) { + if (waylandWnd == widgetChain[i]) { + popupFound = true; + break; + } } - } - if (!popupFound) { - LOG((" nsWindow [%p] not found in PopupManager, hiding it.\n", - waylandWnd)); - waylandWnd->HideWaylandWindow(); - popupList = gVisibleWaylandPopupWindows; + if (!popupFound) { + LOG((" nsWindow [%p] not found in PopupManager, hiding it.\n", + waylandWnd)); + waylandWnd->HideWaylandWindow(); + popupList = gVisibleWaylandPopupWindows; + } else { + LOG((" nsWindow [%p] is still open.\n", waylandWnd)); + popupList = popupList->next; + } } else { - LOG((" nsWindow [%p] is still open.\n", waylandWnd)); popupList = popupList->next; } } @@ -1229,6 +1235,55 @@ return false; } +GtkWindow* nsWindow::GetTopmostWindow() { + nsView* view = nsView::GetViewFor(this); + if (view) { + nsView* parentView = view->GetParent(); + if (parentView) { + nsIWidget* parentWidget = parentView->GetNearestWidget(nullptr); + if (parentWidget) { + nsWindow* parentnsWindow = static_cast(parentWidget); + LOG((" Topmost window: %p [nsWindow]\n", parentnsWindow)); + return GTK_WINDOW(parentnsWindow->mShell); + } + } + } + return nullptr; +} + +GtkWindow* nsWindow::GetCurrentWindow() { + GtkWindow* parentGtkWindow = nullptr; + // get the last opened window from gVisibleWaylandPopupWindows + if (gVisibleWaylandPopupWindows) { + nsWindow* parentnsWindow = + static_cast(gVisibleWaylandPopupWindows->data); + if (parentnsWindow) { + LOG((" Setting parent to last opened window: %p [nsWindow]\n", + parentnsWindow)); + parentGtkWindow = GTK_WINDOW(parentnsWindow->GetGtkWidget()); + } + } + // get the topmost window if the last opened windows are empty + if (!parentGtkWindow) { + parentGtkWindow = GetTopmostWindow(); + } + if (parentGtkWindow && GTK_IS_WINDOW(parentGtkWindow)) { + return GTK_WINDOW(parentGtkWindow); + } else { + LOG((" Failed to get current window for %p: %p\n", this, parentGtkWindow)); + } + return nullptr; +} + +bool nsWindow::IsWidgetOverflowWindow() { + if (this->GetFrame() && this->GetFrame()->GetContent()->GetID()) { + nsCString nodeId; + this->GetFrame()->GetContent()->GetID()->ToUTF8String(nodeId); + return nodeId.Equals("widget-overflow"); + } + return false; +} + // Wayland keeps strong popup window hierarchy. We need to track active // (visible) popup windows and make sure we hide popup on the same level // before we open another one on that level. It means that every open @@ -1247,9 +1302,18 @@ } #endif - // Check if we're already configured. + if (!GetFrame()) { + LOG((" Window without frame cannot be configured.\n")); + return nullptr; + } + + // Check if we're already configured. Popup can be reattached to various + // windows, so don't consider them configured. Also the widget-overflow needs + // special care because the opened (remote) popups has to be closed before is + // it shown again. if (gVisibleWaylandPopupWindows && - g_list_find(gVisibleWaylandPopupWindows, this)) { + g_list_find(gVisibleWaylandPopupWindows, this) && + mPopupType != ePopupTypeTooltip && !IsWidgetOverflowWindow()) { LOG((" [%p] is already configured.\n", (void*)this)); return GTK_WIDGET(gtk_window_get_transient_for(GTK_WINDOW(mShell))); } @@ -1257,80 +1321,30 @@ // If we're opening a new window we don't want to attach it to a tooltip // as it's short lived temporary window. HideWaylandTooltips(); + // Cleanup already closed menus + CleanupWaylandPopups(); GtkWindow* parentGtkWindow = nullptr; - - if (IsMainMenuWindow()) { - // Remove and hide already closed popups from the - // gVisibleWaylandPopupWindows which were not yet been hidden. - CleanupWaylandPopups(); - // Since the popups are shown by unknown order it can happen that child - // popup is shown before parent popup. - // We look for the current window parent in nsXULPopupManager since it - // always has correct popup hierarchy while gVisibleWaylandPopupWindows may - // not. - nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); - AutoTArray widgetChain; - pm->GetSubmenuWidgetChain(&widgetChain); - for (unsigned long i = 0; i < widgetChain.Length() - 1; i++) { - unsigned long parentIndex = i + 1; - if (widgetChain.Length() > parentIndex && widgetChain[i] == this) { - nsWindow* parentWindow = - static_cast(widgetChain[parentIndex]); - parentGtkWindow = GTK_WINDOW(parentWindow->GetGtkWidget()); - LOG((" [%p] Found %p as parent in nsXULPopupManager.", this, - parentWindow)); - break; - } - } - } else { - // Panels usually ends there - if (gVisibleWaylandPopupWindows && HasRemoteContent()) { - // If the new panel is remote content, we need to close all other popups - // before to keep the correct hierarchy because the remote content popup - // can replace the overflow-widget panel. - HideWaylandOpenedPopups(); - } else if (gVisibleWaylandPopupWindows) { - // If there is any remote content panel currently opened, close all - // opened popups to keep the correct hierarchy. - GList* popupList = gVisibleWaylandPopupWindows; - while (popupList) { - nsWindow* waylandWnd = static_cast(popupList->data); - LOG((" Checking [%p] IsRemoteContent %d\n", popupList->data, - waylandWnd->IsRemoteContent())); - if (waylandWnd->IsRemoteContent()) { - // close all popups including remote content before showing our panel - // Most likely returning from addon panel to overflow-widget. - HideWaylandOpenedPopups(); - break; - } - popupList = popupList->next; - } - } - // For popups in panels use the last opened popup window as parent, - // panels are not stored in nsXULPopupManager. - if (gVisibleWaylandPopupWindows) { - nsWindow* parentWindow = - static_cast(gVisibleWaylandPopupWindows->data); - parentGtkWindow = GTK_WINDOW(parentWindow->GetGtkWidget()); - } - } + if (HasRemoteContent() || IsWidgetOverflowWindow()) { + LOG( + (" Hiding all opened popups because the window is remote content or " + "overflow-widget")); + HideWaylandOpenedPopups(); + } + + parentGtkWindow = GetCurrentWindow(); if (parentGtkWindow) { MOZ_ASSERT(parentGtkWindow != GTK_WINDOW(this->GetGtkWidget()), "Cannot set self as parent"); gtk_window_set_transient_for(GTK_WINDOW(mShell), GTK_WINDOW(parentGtkWindow)); - } else { - // Fallback to the parent given in nsWindow::Create (most likely the - // toplevel window). - parentGtkWindow = gtk_window_get_transient_for(GTK_WINDOW(mShell)); - LOG((" Setting parent from transient: %p [GtkWindow]\n", parentGtkWindow)); - } - // Add current window to the visible popup list - gVisibleWaylandPopupWindows = - g_list_prepend(gVisibleWaylandPopupWindows, this); - - LOG((" Parent window for %p: %p [GtkWindow]", this, parentGtkWindow)); + // Add current window to the visible popup list + gVisibleWaylandPopupWindows = + g_list_prepend(gVisibleWaylandPopupWindows, this); + LOG((" Parent window for %p: %p [GtkWindow]", this, parentGtkWindow)); + } + + MOZ_ASSERT(parentGtkWindow, "NO parent window for %p: expect popup glitches"); return GTK_WIDGET(parentGtkWindow); }