Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/widget/gtk/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 327 kB image not shown  

Quelle  nsWindow.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=2:tabstop=2:
 */

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "nsWindow.h"

#include <algorithm>
#include <cstdint>
#include <dlfcn.h>
#include <gdk/gdkkeysyms.h>
#include <wchar.h>

#include "VsyncSource.h"
#include "gfx2DGlue.h"
#include "gfxContext.h"
#include "gfxImageSurface.h"
#include "gfxPlatformGtk.h"
#include "gfxUtils.h"
#include "GLContextProvider.h"
#include "GLContext.h"
#include "GtkCompositorWidget.h"
#include "gtkdrawing.h"
#include "imgIContainer.h"
#include "InputData.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/Components.h"
#include "mozilla/GRefPtr.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/WheelEventBinding.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/gfx/GPUProcessManager.h"
#include "mozilla/gfx/HelpersCairo.h"
#include "mozilla/layers/APZThreadUtils.h"
#include "mozilla/layers/LayersTypes.h"
#include "mozilla/layers/CompositorBridgeChild.h"
#include "mozilla/layers/CompositorBridgeParent.h"
#include "mozilla/layers/CompositorThread.h"
#include "mozilla/layers/KnowsCompositor.h"
#include "mozilla/layers/WebRenderBridgeChild.h"
#include "mozilla/layers/WebRenderLayerManager.h"
#include "mozilla/layers/APZInputBridge.h"
#include "mozilla/layers/IAPZCTreeManager.h"
#include "mozilla/Likely.h"
#include "mozilla/Maybe.h"
#include "mozilla/MiscEvents.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/NativeKeyBindingsType.h"
#include "mozilla/Preferences.h"
#include "mozilla/PresShell.h"
#include "mozilla/ProfilerLabels.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/StaticPrefs_apz.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/StaticPrefs_mozilla.h"
#include "mozilla/StaticPrefs_ui.h"
#include "mozilla/StaticPrefs_widget.h"
#include "mozilla/SwipeTracker.h"
#include "mozilla/TextEventDispatcher.h"
#include "mozilla/TextEvents.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/UniquePtrExtensions.h"
#include "mozilla/WidgetUtils.h"
#include "mozilla/WritingModes.h"
#ifdef MOZ_X11
#  include "mozilla/X11Util.h"
#endif
#include "mozilla/XREAppData.h"
#include "NativeKeyBindings.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsAppRunner.h"
#include "nsDragService.h"
#include "nsGTKToolkit.h"
#include "nsGtkKeyUtils.h"
#include "nsGtkCursors.h"
#include "nsGfxCIID.h"
#include "nsGtkUtils.h"
#include "nsIFile.h"
#include "nsIGSettingsService.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsImageToPixbuf.h"
#include "nsINode.h"
#include "nsIRollupListener.h"
#include "nsIScreenManager.h"
#include "nsIUserIdleServiceInternal.h"
#include "nsIWidgetListener.h"
#include "nsLayoutUtils.h"
#include "nsMenuPopupFrame.h"
#include "nsPresContext.h"
#include "nsShmImage.h"
#include "nsString.h"
#include "nsWidgetsCID.h"
#include "nsViewManager.h"
#include "nsXPLookAndFeel.h"
#include "prlink.h"
#include "Screen.h"
#include "ScreenHelperGTK.h"
#include "SystemTimeConverter.h"
#include "WidgetUtilsGtk.h"
#include "NativeMenuGtk.h"

#ifdef ACCESSIBILITY
#  include "mozilla/a11y/LocalAccessible.h"
#  include "mozilla/a11y/Platform.h"
#  include "nsAccessibilityService.h"
#endif

#ifdef MOZ_X11
#  include <gdk/gdkkeysyms-compat.h>
#  include <X11/Xatom.h>
#  include <X11/extensions/XShm.h>
#  include <X11/extensions/shape.h>
#  include "gfxXlibSurface.h"
#  include "GLContextGLX.h"  // for GLContextGLX::FindVisual()
#  include "GLContextEGL.h"  // for GLContextEGL::FindVisual()
#  include "WindowSurfaceX11Image.h"
#  include "WindowSurfaceX11SHM.h"
#endif
#ifdef MOZ_WAYLAND
#  include <gdk/gdkkeysyms-compat.h>
#  include "nsIClipboard.h"
#  include "nsView.h"
#  include "WaylandVsyncSource.h"
#endif

using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::layers;
using namespace mozilla::widget;
#ifdef MOZ_X11
using mozilla::gl::GLContextEGL;
using mozilla::gl::GLContextGLX;
#endif

// Don't put more than this many rects in the dirty region, just fluff
// out to the bounding-box if there are more
#define MAX_RECTS_IN_REGION 100

#if !GTK_CHECK_VERSION(3, 22, 0)

constexpr gint GDK_WINDOW_STATE_TOP_TILED = 1 << 9;
constexpr gint GDK_WINDOW_STATE_TOP_RESIZABLE = 1 << 10;
constexpr gint GDK_WINDOW_STATE_RIGHT_TILED = 1 << 11;
constexpr gint GDK_WINDOW_STATE_RIGHT_RESIZABLE = 1 << 12;
constexpr gint GDK_WINDOW_STATE_BOTTOM_TILED = 1 << 13;
constexpr gint GDK_WINDOW_STATE_BOTTOM_RESIZABLE = 1 << 14;
constexpr gint GDK_WINDOW_STATE_LEFT_TILED = 1 << 15;
constexpr gint GDK_WINDOW_STATE_LEFT_RESIZABLE = 1 << 16;

#endif

constexpr gint kPerSideTiledStates =
    GDK_WINDOW_STATE_TOP_TILED | GDK_WINDOW_STATE_RIGHT_TILED |
    GDK_WINDOW_STATE_BOTTOM_TILED | GDK_WINDOW_STATE_LEFT_TILED;

constexpr gint kTiledStates = GDK_WINDOW_STATE_TILED | kPerSideTiledStates;

constexpr gint kResizableStates =
    GDK_WINDOW_STATE_TOP_RESIZABLE | GDK_WINDOW_STATE_RIGHT_RESIZABLE |
    GDK_WINDOW_STATE_BOTTOM_RESIZABLE | GDK_WINDOW_STATE_LEFT_RESIZABLE;

#if !GTK_CHECK_VERSION(3, 18, 0)
struct _GdkEventTouchpadPinch {
  GdkEventType type;
  GdkWindow* window;
  gint8 send_event;
  gint8 phase;
  gint8 n_fingers;
  guint32 time;
  gdouble x;
  gdouble y;
  gdouble dx;
  gdouble dy;
  gdouble angle_delta;
  gdouble scale;
  gdouble x_root, y_root;
  guint state;
};

constexpr gint GDK_TOUCHPAD_GESTURE_MASK = 1 << 24;
constexpr GdkEventType GDK_TOUCHPAD_PINCH = static_cast<GdkEventType>(42);

#endif

constexpr gint kEvents =
    GDK_TOUCHPAD_GESTURE_MASK | GDK_EXPOSURE_MASK | GDK_STRUCTURE_MASK |
    GDK_VISIBILITY_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
    GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_SMOOTH_SCROLL_MASK |
    GDK_TOUCH_MASK | GDK_SCROLL_MASK | GDK_POINTER_MOTION_MASK |
    GDK_PROPERTY_CHANGE_MASK;

/* utility functions */
static bool is_mouse_in_window(GdkWindow* aWindow, gdouble aMouseX,
                               gdouble aMouseY);
static nsWindow* get_window_for_gtk_widget(GtkWidget* widget);
static nsWindow* get_window_for_gdk_window(GdkWindow* window);
static GtkWidget* get_gtk_widget_for_gdk_window(GdkWindow* window);
static GdkCursor* get_gtk_cursor(nsCursor aCursor);

/* callbacks from widgets */
static gboolean expose_event_cb(GtkWidget* widget, cairo_t* cr);
static gboolean configure_event_cb(GtkWidget* widget, GdkEventConfigure* event);
static void size_allocate_cb(GtkWidget* widget, GtkAllocation* allocation);
static void toplevel_window_size_allocate_cb(GtkWidget* widget,
                                             GtkAllocation* allocation);
static gboolean delete_event_cb(GtkWidget* widget, GdkEventAny* event);
static gboolean enter_notify_event_cb(GtkWidget* widget,
                                      GdkEventCrossing* event);
static gboolean leave_notify_event_cb(GtkWidget* widget,
                                      GdkEventCrossing* event);
static gboolean motion_notify_event_cb(GtkWidget* widget,
                                       GdkEventMotion* event);
MOZ_CAN_RUN_SCRIPT static gboolean button_press_event_cb(GtkWidget* widget,
                                                         GdkEventButton* event);
static gboolean button_release_event_cb(GtkWidget* widget,
                                        GdkEventButton* event);
static gboolean focus_in_event_cb(GtkWidget* widget, GdkEventFocus* event);
static gboolean focus_out_event_cb(GtkWidget* widget, GdkEventFocus* event);
static gboolean key_press_event_cb(GtkWidget* widget, GdkEventKey* event);
static gboolean key_release_event_cb(GtkWidget* widget, GdkEventKey* event);
static gboolean property_notify_event_cb(GtkWidget* widget,
                                         GdkEventProperty* event);
static gboolean scroll_event_cb(GtkWidget* widget, GdkEventScroll* event);
static gboolean visibility_notify_event_cb(GtkWidget* widget,
                                           GdkEventVisibility* event);
static void hierarchy_changed_cb(GtkWidget* widget,
                                 GtkWidget* previous_toplevel);
static gboolean window_state_event_cb(GtkWidget* widget,
                                      GdkEventWindowState* event);
static void settings_xft_dpi_changed_cb(GtkSettings* settings,
                                        GParamSpec* pspec, nsWindow* data);
static void check_resize_cb(GtkContainer* container, gpointer user_data);
static void screen_composited_changed_cb(GdkScreen* screen, gpointer user_data);
static void widget_composited_changed_cb(GtkWidget* widget, gpointer user_data);

static void scale_changed_cb(GtkWidget* widget, GParamSpec* aPSpec,
                             gpointer aPointer);
static gboolean touch_event_cb(GtkWidget* aWidget, GdkEventTouch* aEvent);
static gboolean generic_event_cb(GtkWidget* widget, GdkEvent* aEvent);
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,
                                     GdkDragContext* aDragContext, gint aX,
                                     gint aY, guint aTime, gpointer aData);
static void drag_leave_event_cb(GtkWidget* aWidget,
                                GdkDragContext* aDragContext, guint aTime,
                                gpointer aData);
static gboolean drag_drop_event_cb(GtkWidget* aWidget,
                                   GdkDragContext* aDragContext, gint aX,
                                   gint aY, guint aTime, gpointer aData);
static void drag_data_received_event_cb(GtkWidget* aWidget,
                                        GdkDragContext* aDragContext, gint aX,
                                        gint aY,
                                        GtkSelectionData* aSelectionData,
                                        guint aInfo, guint32 aTime,
                                        gpointer aData);

/* initialization static functions */
static nsresult initialize_prefs(void);

static guint32 sLastUserInputTime = GDK_CURRENT_TIME;

static SystemTimeConverter<guint32>& TimeConverter() {
  static SystemTimeConverter<guint32> sTimeConverterSingleton;
  return sTimeConverterSingleton;
}

bool nsWindow::sTransparentMainWindow = false;

// forward declare from mozgtk
extern "C" MOZ_EXPORT void mozgtk_linker_holder();

namespace mozilla {

#ifdef MOZ_X11
class CurrentX11TimeGetter {
 public:
  explicit CurrentX11TimeGetter(GdkWindow* aWindow) : mWindow(aWindow) {}

  guint32 GetCurrentTime() const { return gdk_x11_get_server_time(mWindow); }

  void GetTimeAsyncForPossibleBackwardsSkew(const TimeStamp& aNow) {
    // Check for in-flight request
    if (!mAsyncUpdateStart.IsNull()) {
      return;
    }
    mAsyncUpdateStart = aNow;

    Display* xDisplay = GDK_WINDOW_XDISPLAY(mWindow);
    Window xWindow = GDK_WINDOW_XID(mWindow);
    unsigned char c = 'a';
    Atom timeStampPropAtom = TimeStampPropAtom();
    XChangeProperty(xDisplay, xWindow, timeStampPropAtom, timeStampPropAtom, 8,
                    PropModeReplace, &c, 1);
    XFlush(xDisplay);
  }

  gboolean PropertyNotifyHandler(GtkWidget* aWidget, GdkEventProperty* aEvent) {
    if (aEvent->atom != gdk_x11_xatom_to_atom(TimeStampPropAtom())) {
      return FALSE;
    }

    guint32 eventTime = aEvent->time;
    TimeStamp lowerBound = mAsyncUpdateStart;

    TimeConverter().CompensateForBackwardsSkew(eventTime, lowerBound);
    mAsyncUpdateStart = TimeStamp();
    return TRUE;
  }

 private:
  static Atom TimeStampPropAtom() {
    return gdk_x11_get_xatom_by_name_for_display(gdk_display_get_default(),
                                                 "GDK_TIMESTAMP_PROP");
  }

  // This is safe because this class is stored as a member of mWindow and
  // won't outlive it.
  GdkWindow* mWindow;
  TimeStamp mAsyncUpdateStart;
};
#endif

}  // namespace mozilla

// The window from which the focus manager asks us to dispatch key events.
static nsWindow* gFocusWindow = nullptr;
static bool gBlockActivateEvent = false;
static bool gGlobalsInitialized = false;
static bool gUseAspectRatio = true;
static uint32_t gLastTouchID = 0;
// See Bug 1777269 for details. We don't know if the suspected leave notify
// event is a correct one when we get it.
// Store it and issue it later from enter notify event if it's correct,
// throw it away otherwise.
MOZ_RUNINIT static GUniquePtr<GdkEventCrossing> sStoredLeaveNotifyEvent;

#define NS_WINDOW_TITLE_MAX_LENGTH 4095

// cursor cache
static GdkCursor* gCursorCache[eCursorCount];

// Sometimes this actually also includes the state of the modifier keys, but
// only the button state bits are used.
static guint gButtonState;

static inline bool TimestampIsNewerThan(guint32 a, guint32 b) {
  // Timestamps are just the least significant bits of a monotonically
  // increasing function, and so the use of unsigned overflow arithmetic.
  return a - b <= G_MAXUINT32 / 2;
}

static void UpdateLastInputEventTime(void* aGdkEvent) {
  nsCOMPtr<nsIUserIdleServiceInternal> idleService =
      do_GetService("@mozilla.org/widget/useridleservice;1");
  if (idleService) {
    idleService->ResetIdleTimeOut(0);
  }

  guint timestamp = gdk_event_get_time(static_cast<GdkEvent*>(aGdkEvent));
  if (timestamp == GDK_CURRENT_TIME) {
    return;
  }

  sLastUserInputTime = timestamp;
}

// Don't set parent (transient for) if nothing changes.
// gtk_window_set_transient_for() blows up wl_subsurfaces used by aWindow
// even if aParent is the same.
static void GtkWindowSetTransientFor(GtkWindow* aWindow, GtkWindow* aParent) {
  GtkWindow* parent = gtk_window_get_transient_for(aWindow);
  if (parent != aParent) {
    gtk_window_set_transient_for(aWindow, aParent);
  }
}

#define gtk_window_set_transient_for(a, b)                         \
  {                                                                \
    MOZ_ASSERT_UNREACHABLE(                                        \
        "gtk_window_set_transient_for() can't be used directly."); \
  }

nsWindow::nsWindow()
    : mWindowVisibilityMutex("nsWindow::mWindowVisibilityMutex"),
      mIsMapped(false),
      mIsDestroyed(false),
      mIsShown(false),
      mNeedsShow(false),
      mEnabled(true),
      mCreated(false),
      mHandleTouchEvent(false),
      mIsDragPopup(false),
      mCompositedScreen(gdk_screen_is_composited(gdk_screen_get_default())),
      mIsAccelerated(false),
      mIsAlert(false),
      mWindowShouldStartDragging(false),
      mHasMappedToplevel(false),
      mPanInProgress(false),
      mTitlebarBackdropState(false),
      mIsChildWindow(false),
      mAlwaysOnTop(false),
      mNoAutoHide(false),
      mIsTransparent(false),
      mHasReceivedSizeAllocate(false),
      mWidgetCursorLocked(false),
      mUndecorated(false),
      mPopupTrackInHierarchy(false),
      mPopupTrackInHierarchyConfigured(false),
      mHiddenPopupPositioned(false),
      mHasAlphaVisual(false),
      mPopupAnchored(false),
      mPopupContextMenu(false),
      mPopupMatchesLayout(false),
      mPopupChanged(false),
      mPopupTemporaryHidden(false),
      mPopupClosed(false),
      mPopupUseMoveToRect(false),
      mWaitingForMoveToRectCallback(false),
      mMovedAfterMoveToRect(false),
      mResizedAfterMoveToRect(false),
      mConfiguredClearColor(false),
      mGotNonBlankPaint(false),
      mNeedsToRetryCapturingMouse(false) {
  mWindowType = WindowType::Child;
  mSizeConstraints.mMaxSize = GetSafeWindowSize(mSizeConstraints.mMaxSize);

  if (!gGlobalsInitialized) {
    gGlobalsInitialized = true;

    // It's OK if either of these fail, but it may not be one day.
    initialize_prefs();

#ifdef MOZ_WAYLAND
    // Wayland provides clipboard data to application on focus-in event
    // so we need to init our clipboard hooks before we create window
    // and get focus.
    if (GdkIsWaylandDisplay()) {
      nsCOMPtr<nsIClipboard> clipboard =
          do_GetService("@mozilla.org/widget/clipboard;1");
      NS_ASSERTION(clipboard, "Failed to init clipboard!");
    }
#endif
  }
  // Dummy call to mozgtk to prevent the linker from removing
  // the dependency with --as-needed.
  // see toolkit/library/moz.build for details.
  mozgtk_linker_holder();
}

nsWindow::~nsWindow() {
  LOG("nsWindow::~nsWindow()");
  // We don't want to release live nsWindow.
  MOZ_RELEASE_ASSERT(mIsDestroyed, "Releasing live window!");
}

/* static */
void nsWindow::ReleaseGlobals() {
  for (auto& cursor : gCursorCache) {
    if (cursor) {
      g_object_unref(cursor);
      cursor = nullptr;
    }
  }
}

void nsWindow::DispatchActivateEvent(void) {
#ifdef ACCESSIBILITY
  DispatchActivateEventAccessible();
#endif  // ACCESSIBILITY

  if (mWidgetListener) mWidgetListener->WindowActivated();
}

void nsWindow::DispatchDeactivateEvent() {
  if (mWidgetListener) {
    mWidgetListener->WindowDeactivated();
  }

#ifdef ACCESSIBILITY
  DispatchDeactivateEventAccessible();
#endif  // ACCESSIBILITY
}

void nsWindow::DispatchResized() {
  LOG("nsWindow::DispatchResized() size [%d, %d]", (int)(mBounds.width),
      (int)(mBounds.height));

  mNeedsDispatchSize = LayoutDeviceIntSize(-1, -1);
  if (mWidgetListener) {
    mWidgetListener->WindowResized(this, mBounds.width, mBounds.height);
  }
  if (mAttachedWidgetListener) {
    mAttachedWidgetListener->WindowResized(this, mBounds.width, mBounds.height);
  }
}

void nsWindow::MaybeDispatchResized() {
  if (mNeedsDispatchSize != LayoutDeviceIntSize(-1, -1) && !mIsDestroyed) {
    mBounds.SizeTo(mNeedsDispatchSize);
    // Check mBounds size
    if (mCompositorSession &&
        !wr::WindowSizeSanityCheck(mBounds.width, mBounds.height)) {
      gfxCriticalNoteOnce << "Invalid mBounds in MaybeDispatchResized "
                          << mBounds << " size state " << mSizeMode;
    }

    // Notify the GtkCompositorWidget of a ClientSizeChange
    if (mCompositorWidgetDelegate) {
      mCompositorWidgetDelegate->NotifyClientSizeChanged(GetClientSize());
    }

    DispatchResized();
  }
}

nsIWidgetListener* nsWindow::GetListener() {
  return mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener;
}

nsresult nsWindow::DispatchEvent(WidgetGUIEvent* aEvent,
                                 nsEventStatus& aStatus) {
#ifdef DEBUG
  debug_DumpEvent(stdout, aEvent->mWidget, aEvent, "something", 0);
#endif
  aStatus = nsEventStatus_eIgnore;
  nsIWidgetListener* listener = GetListener();
  if (listener) {
    aStatus = listener->HandleEvent(aEvent, mUseAttachedEvents);
  }

  return NS_OK;
}

void nsWindow::OnDestroy(void) {
  if (mOnDestroyCalled) {
    return;
  }

  mOnDestroyCalled = true;

  // Prevent deletion.
  nsCOMPtr<nsIWidget> kungFuDeathGrip = this;

  // release references to children, device context, toolkit + app shell
  nsBaseWidget::OnDestroy();

  // Remove association between this object and its parent and siblings.
  nsBaseWidget::Destroy();

  NotifyWindowDestroyed();
}

bool nsWindow::AreBoundsSane() {
  // Check requested size, as mBounds might not have been updated.
  return !mLastSizeRequest.IsEmpty();
}

void nsWindow::Destroy() {
  MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());

  if (mIsDestroyed || !mCreated) {
    return;
  }

  LOG("nsWindow::Destroy\n");

  mIsDestroyed = true;
  mCreated = false;

#ifdef MOZ_WAYLAND
  // Shut down our local vsync source
  // Also drops reference to nsWindow::mSurface.
  if (mWaylandVsyncSource) {
    mWaylandVsyncSource->Shutdown();
    mWaylandVsyncSource = nullptr;
  }
  mWaylandVsyncDispatcher = nullptr;
  UnlockNativePointer();
#endif

  // Cancel (dragleave) the current drag session, if any.
  RefPtr<nsDragService> dragService = nsDragService::GetInstance();
  if (dragService) {
    nsDragSession* dragSession =
        static_cast<nsDragSession*>(dragService->GetCurrentSession(this));
    if (dragSession && this == dragSession->GetMostRecentDestWindow()) {
      dragSession->ScheduleLeaveEvent();
    }
  }

  nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
  if (rollupListener) {
    nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget();
    if (static_cast<nsIWidget*>(this) == rollupWidget) {
      rollupListener->Rollup({});
    }
  }

  NativeShow(false);

  MOZ_ASSERT(!gtk_widget_get_mapped(mShell));
  MOZ_ASSERT(!gtk_widget_get_mapped(GTK_WIDGET(mContainer)));

  DestroyLayerManager();

  // mSurfaceProvider holds reference to this nsWindow so we need to explicitly
  // clear it here to avoid nsWindow leak.
  mSurfaceProvider.CleanupResources();

  g_signal_handlers_disconnect_by_data(gtk_settings_get_default(), this);

  if (mIMContext) {
    mIMContext->OnDestroyWindow(this);
  }

  // make sure that we remove ourself as the focus window
  if (gFocusWindow == this) {
    LOG("automatically losing focus...\n");
    gFocusWindow = nullptr;
  }

  if (sStoredLeaveNotifyEvent) {
    nsWindow* window =
        get_window_for_gdk_window(sStoredLeaveNotifyEvent->window);
    if (window == this) {
      sStoredLeaveNotifyEvent = nullptr;
    }
  }

  // We need to detach accessible object here because mContainer is a custom
  // widget and doesn't call gtk_widget_real_destroy() from destroy handler
  // as regular widgets.
  if (AtkObject* ac = gtk_widget_get_accessible(GTK_WIDGET(mContainer))) {
    gtk_accessible_set_widget(GTK_ACCESSIBLE(ac), nullptr);
  }

  gtk_widget_destroy(mShell);
  mShell = nullptr;
  mContainer = nullptr;
#ifdef MOZ_WAYLAND
  mSurface = nullptr;
#endif

  MOZ_ASSERT(!mGdkWindow,
             "mGdkWindow should be NULL when mContainer is destroyed");

#ifdef ACCESSIBILITY
  if (mRootAccessible) {
    mRootAccessible = nullptr;
  }
#endif

  // Save until last because OnDestroy() may cause us to be deleted.
  OnDestroy();
}

float nsWindow::GetDPI() {
  float dpi = 96.0f;
  nsCOMPtr<nsIScreen> screen = GetWidgetScreen();
  if (screen) {
    screen->GetDpi(&dpi);
  }
  return dpi;
}

double nsWindow::GetDefaultScaleInternal() { return FractionalScaleFactor(); }

DesktopToLayoutDeviceScale nsWindow::GetDesktopToDeviceScale() {
#ifdef MOZ_WAYLAND
  if (GdkIsWaylandDisplay()) {
    return DesktopToLayoutDeviceScale(FractionalScaleFactor());
  }
#endif

  // In Gtk/X11, we manage windows using device pixels.
  return DesktopToLayoutDeviceScale(1.0);
}

DesktopToLayoutDeviceScale nsWindow::GetDesktopToDeviceScaleByScreen() {
#ifdef MOZ_WAYLAND
  if (GdkIsWaylandDisplay()) {
    // In wayland there's no absolute screen position, so we need to use the
    // scale factor of our top level, which is what FractionalScaleFactor does,
    // luckily.
    return DesktopToLayoutDeviceScale(FractionalScaleFactor());
  }
#endif
  return nsBaseWidget::GetDesktopToDeviceScale();
}

bool nsWindow::WidgetTypeSupportsAcceleration() {
  if (IsSmallPopup()) {
    return false;
  }
  if (mWindowType == WindowType::Popup) {
    return HasRemoteContent();
  }
  return true;
}

static void InitPenEvent(WidgetMouseEvent& aGeckoEvent, GdkEvent* aEvent) {
  // Find the source of the event
  GdkDevice* device = gdk_event_get_source_device(aEvent);
  GdkInputSource eSource = gdk_device_get_source(device);
  gdouble value;

  // We distinguish touch screens from pens using the event type
  // Eraser corresponds to the pen with the "erase" button pressed
  if (eSource != GDK_SOURCE_PEN && eSource != GDK_SOURCE_ERASER) {
    bool XWaylandPen = false;
#ifdef MOZ_X11
    // Workaround : When using Xwayland, pens are reported as
    // GDK_SOURCE_TOUCHSCREEN If eSource is GDK_SOURCE_TOUCHSCREEN and the
    // GDK_AXIS_XTILT and GDK_AXIS_YTILT axes are reported then it's a pen and
    // not a finger on a screen. Yes, that's a stupid heuristic but it works...
    // Note, however, that the tilt values are not reliable
    // Another approach could be use the device tool type, but that's only
    // available in GTK > 3.22
    XWaylandPen = (eSource == GDK_SOURCE_TOUCHSCREEN && GdkIsX11Display() &&
                   gdk_event_get_axis(aEvent, GDK_AXIS_XTILT, &value) &&
                   gdk_event_get_axis(aEvent, GDK_AXIS_YTILT, &value));
#endif
    if (!XWaylandPen) {
      return;
    }
    LOGW("InitPenEvent(): Is XWayland pen");
  }

  aGeckoEvent.mInputSource = dom::MouseEvent_Binding::MOZ_SOURCE_PEN;
  aGeckoEvent.pointerId = 1;

  // The range of xtilt and ytilt are -1 to 1. Normalize it to -90 to 90.
  if (gdk_event_get_axis(aEvent, GDK_AXIS_XTILT, &value)) {
    aGeckoEvent.tiltX = int32_t(NS_round(value * 90));
  }
  if (gdk_event_get_axis(aEvent, GDK_AXIS_YTILT, &value)) {
    aGeckoEvent.tiltY = int32_t(NS_round(value * 90));
  }
  if (gdk_event_get_axis(aEvent, GDK_AXIS_PRESSURE, &value)) {
    aGeckoEvent.mPressure = (float)value;
    // Make sure the pression is acceptable
    MOZ_ASSERT(aGeckoEvent.mPressure >= 0.0 && aGeckoEvent.mPressure <= 1.0);
  }

  LOGW("InitPenEvent(): pressure %f\n", aGeckoEvent.mPressure);
}

void nsWindow::SetModal(bool aModal) {
  LOG("nsWindow::SetModal %d\n", aModal);
  if (mIsDestroyed) {
    return;
  }

  gtk_window_set_modal(GTK_WINDOW(mShell), aModal ? TRUE : FALSE);
}

// nsIWidget method, which means IsShown.
bool nsWindow::IsVisible() const { return mIsShown; }

bool nsWindow::IsMapped() const { return mIsMapped; }

void nsWindow::RegisterTouchWindow() {
  mHandleTouchEvent = true;
  mTouches.Clear();
}

LayoutDeviceIntPoint nsWindow::GetScreenEdgeSlop() {
  if (DrawsToCSDTitlebar()) {
    return GetClientOffset();
  }
  return {};
}

void nsWindow::ConstrainPosition(DesktopIntPoint& aPoint) {
  if (!mShell || GdkIsWaylandDisplay()) {
    return;
  }

  double dpiScale = GetDefaultScale().scale;

  // we need to use the window size in logical screen pixels
  int32_t logWidth = std::max(NSToIntRound(mBounds.width / dpiScale), 1);
  int32_t logHeight = std::max(NSToIntRound(mBounds.height / dpiScale), 1);

  /* get our playing field. use the current screen, or failing that
    for any reason, use device caps for the default screen. */

  nsCOMPtr<nsIScreenManager> screenmgr =
      do_GetService("@mozilla.org/gfx/screenmanager;1");
  if (!screenmgr) {
    return;
  }
  nsCOMPtr<nsIScreen> screen;
  screenmgr->ScreenForRect(aPoint.x, aPoint.y, logWidth, logHeight,
                           getter_AddRefs(screen));
  // We don't have any screen so leave the coordinates as is
  if (!screen) {
    return;
  }

  // For normalized windows, use the desktop work area.
  // For full screen windows, use the desktop.
  DesktopIntRect screenRect = mSizeMode == nsSizeMode_Fullscreen
                                  ? screen->GetRectDisplayPix()
                                  : screen->GetAvailRectDisplayPix();

  // Expand for the decoration size if needed.
  auto slop =
      DesktopIntPoint::Round(GetScreenEdgeSlop() / GetDesktopToDeviceScale());
  screenRect.Inflate(slop.x, slop.y);

  aPoint = ConstrainPositionToBounds(aPoint, {logWidth, logHeight}, screenRect);
}

void nsWindow::SetSizeConstraints(const SizeConstraints& aConstraints) {
  mSizeConstraints.mMinSize = GetSafeWindowSize(aConstraints.mMinSize);
  mSizeConstraints.mMaxSize = GetSafeWindowSize(aConstraints.mMaxSize);

  ApplySizeConstraints();
}

bool nsWindow::DrawsToCSDTitlebar() const {
  return mSizeMode == nsSizeMode_Normal &&
         mGtkWindowDecoration == GTK_DECORATION_CLIENT && mDrawInTitlebar;
}

void nsWindow::AddCSDDecorationSize(int* aWidth, int* aHeight) {
  if (mSizeMode != nsSizeMode_Normal || mUndecorated ||
      mGtkWindowDecoration != GTK_DECORATION_CLIENT || !GdkIsWaylandDisplay() ||
      !IsGnomeDesktopEnvironment()) {
    return;
  }

  GtkBorder decorationSize = GetCSDDecorationSize(IsPopup());
  *aWidth += decorationSize.left + decorationSize.right;
  *aHeight += decorationSize.top + decorationSize.bottom;
}

#ifdef MOZ_WAYLAND
bool nsWindow::GetCSDDecorationOffset(int* aDx, int* aDy) {
  if (!DrawsToCSDTitlebar()) {
    return false;
  }
  GtkBorder decorationSize = GetCSDDecorationSize(IsPopup());
  *aDx = decorationSize.left;
  *aDy = decorationSize.top;
  return true;
}
#endif

void nsWindow::ApplySizeConstraints() {
  if (mShell) {
    GdkGeometry geometry;
    geometry.min_width =
        DevicePixelsToGdkCoordRoundUp(mSizeConstraints.mMinSize.width);
    geometry.min_height =
        DevicePixelsToGdkCoordRoundUp(mSizeConstraints.mMinSize.height);
    geometry.max_width =
        DevicePixelsToGdkCoordRoundDown(mSizeConstraints.mMaxSize.width);
    geometry.max_height =
        DevicePixelsToGdkCoordRoundDown(mSizeConstraints.mMaxSize.height);

    uint32_t hints = 0;
    if (mSizeConstraints.mMinSize != LayoutDeviceIntSize()) {
      gtk_widget_set_size_request(GTK_WIDGET(mContainer), geometry.min_width,
                                  geometry.min_height);
      AddCSDDecorationSize(&geometry.min_width, &geometry.min_height);
      hints |= GDK_HINT_MIN_SIZE;
    }
    if (mSizeConstraints.mMaxSize !=
        LayoutDeviceIntSize(NS_MAXSIZE, NS_MAXSIZE)) {
      AddCSDDecorationSize(&geometry.max_width, &geometry.max_height);
      hints |= GDK_HINT_MAX_SIZE;
    }

    if (mAspectRatio != 0.0f && !mAspectResizer) {
      geometry.min_aspect = mAspectRatio;
      geometry.max_aspect = mAspectRatio;
      hints |= GDK_HINT_ASPECT;
    }

    gtk_window_set_geometry_hints(GTK_WINDOW(mShell), nullptr, &geometry,
                                  GdkWindowHints(hints));
  }
}

void nsWindow::Show(bool aState) {
  if (aState == mIsShown) {
    return;
  }

  mIsShown = aState;

#ifdef MOZ_LOGGING
  LOG("nsWindow::Show state %d frame %s\n", aState, GetFrameTag().get());
  if (!aState && mSourceDragContext && GdkIsWaylandDisplay()) {
    LOG(" closing Drag&Drop source window, D&D will be canceled!");
  }
#endif

  // Ok, someone called show on a window that isn't sized to a sane
  // value.  Mark this window as needing to have Show() called on it
  // and return.
  if ((aState && !AreBoundsSane()) || !mCreated) {
    LOG("\tbounds are insane or window hasn't been created yet\n");
    mNeedsShow = true;
    return;
  }

  // If someone is hiding this widget, clear any needing show flag.
  if (!aState) mNeedsShow = false;

#ifdef ACCESSIBILITY
  if (aState && a11y::ShouldA11yBeEnabled()) {
    CreateRootAccessible();
  }
#endif

  NativeShow(aState);
  RefreshWindowClass();
}

void nsWindow::ResizeInt(const Maybe<LayoutDeviceIntPoint>& aMove,
                         LayoutDeviceIntSize aSize) {
  LOG("nsWindow::ResizeInt w:%d h:%d\n", aSize.width, aSize.height);
  const bool moved = aMove && *aMove != mBounds.TopLeft();
  if (moved) {
    mBounds.MoveTo(*aMove);
    LOG(" with move to left:%d top:%d", aMove->x.value, aMove->y.value);
  }

  ConstrainSize(&aSize.width, &aSize.height);
  LOG(" ConstrainSize: w:%d h;%d\n", aSize.width, aSize.height);

  const bool resized = aSize != mLastSizeRequest || mBounds.Size() != aSize;
#if MOZ_LOGGING
  LOG(" resized %d aSize [%d, %d] mLastSizeRequest [%d, %d] mBounds [%d, %d]",
      resized, aSize.width, aSize.height, mLastSizeRequest.width,
      mLastSizeRequest.height, mBounds.width, mBounds.height);
#endif

  // For top-level windows, aSize should possibly be
  // interpreted as frame bounds, but NativeMoveResize treats these as window
  // bounds (Bug 581866).
  mLastSizeRequest = aSize;
  // Check size
  if (mCompositorSession &&
      !wr::WindowSizeSanityCheck(aSize.width, aSize.height)) {
    gfxCriticalNoteOnce << "Invalid aSize in ResizeInt " << aSize
                        << " size state " << mSizeMode;
  }

  // Recalculate aspect ratio when resized from DOM
  if (mAspectRatio != 0.0) {
    LockAspectRatio(true);
  }

  if (!mCreated) {
    return;
  }

  if (!moved && !resized) {
    LOG(" not moved or resized, quit");
    return;
  }

  NativeMoveResize(moved, resized);

  // We optimistically assume size changes immediately in two cases:
  // 1. Override-redirect window: Size is controlled by only us.
  // 2. Managed window that has not not yet received a size-allocate event:
  //    Resize() Callers expect initial sizes to be applied synchronously.
  //    If the size request is not honored, then we'll correct in
  //    OnSizeAllocate().
  //
  // When a managed window has already received a size-allocate, we cannot
  // assume we'll always get a notification if our request does not get
  // honored: "If the configure request has not changed, we don't ever resend
  // it, because it could mean fighting the user or window manager."
  // https://gitlab.gnome.org/GNOME/gtk/-/blob/3.24.31/gtk/gtkwindow.c#L9782
  // So we don't update mBounds until OnSizeAllocate() when we know the
  // request is granted.
  bool isOrWillBeVisible = mHasReceivedSizeAllocate || mNeedsShow || mIsShown;
  if (!isOrWillBeVisible ||
      gtk_window_get_window_type(GTK_WINDOW(mShell)) == GTK_WINDOW_POPUP) {
    mBounds.SizeTo(aSize);
    if (mCompositorWidgetDelegate) {
      mCompositorWidgetDelegate->NotifyClientSizeChanged(aSize);
    }
    DispatchResized();
  }
}

void nsWindow::Resize(double aWidth, double aHeight, bool aRepaint) {
  LOG("nsWindow::Resize %f %f\n", aWidth, aHeight);

  double scale =
      BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
  auto size = LayoutDeviceIntSize::Round(scale * aWidth, scale * aHeight);

  ResizeInt(Nothing(), size);
}

void nsWindow::Resize(double aX, double aY, double aWidth, double aHeight,
                      bool aRepaint) {
  LOG("nsWindow::Resize [%f,%f] -> [%f x %f] repaint %d\n", aX, aY, aWidth,
      aHeight, aRepaint);

  double scale =
      BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
  auto size = LayoutDeviceIntSize::Round(scale * aWidth, scale * aHeight);
  auto topLeft = LayoutDeviceIntPoint::Round(scale * aX, scale * aY);

  ResizeInt(Some(topLeft), size);
}

void nsWindow::Enable(bool aState) { mEnabled = aState; }

bool nsWindow::IsEnabled() const { return mEnabled; }

void nsWindow::Move(double aX, double aY) {
  double scale =
      BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
  int32_t x = NSToIntRound(aX * scale);
  int32_t y = NSToIntRound(aY * scale);

  LOG("nsWindow::Move to %d x %d\n", x, y);

  if (mSizeMode != nsSizeMode_Normal && IsTopLevelWidget()) {
    LOG(" size state is not normal, bailing");
    return;
  }

  // Since a popup window's x/y coordinates are in relation to to
  // the parent, the parent might have moved so we always move a
  // popup window.
  LOG(" bounds %d x %d\n", mBounds.x, mBounds.y);
  if (x == mBounds.x && y == mBounds.y && mWindowType != WindowType::Popup) {
    LOG(" position is the same, return\n");
    return;
  }

  // XXX Should we do some AreBoundsSane check here?

  mBounds.x = x;
  mBounds.y = y;

  if (!mCreated) {
    LOG(" is not created, return.\n");
    return;
  }

  NativeMoveResize(/* move */ true, /* resize */ false);
}

bool nsWindow::IsPopup() const { return mWindowType == WindowType::Popup; }

bool nsWindow::IsWaylandPopup() const {
  return GdkIsWaylandDisplay() && IsPopup();
}

static nsMenuPopupFrame* GetMenuPopupFrame(nsIFrame* aFrame) {
  return do_QueryFrame(aFrame);
}

void nsWindow::AppendPopupToHierarchyList(nsWindow* aToplevelWindow) {
  mWaylandToplevel = aToplevelWindow;

  nsWindow* popup = aToplevelWindow;
  while (popup && popup->mWaylandPopupNext) {
    popup = popup->mWaylandPopupNext;
  }
  popup->mWaylandPopupNext = this;

  mWaylandPopupPrev = popup;
  mWaylandPopupNext = nullptr;
  mPopupChanged = true;
  mPopupClosed = false;
}

void nsWindow::RemovePopupFromHierarchyList() {
  // We're already removed from the popup hierarchy
  if (!IsInPopupHierarchy()) {
    return;
  }
  mWaylandPopupPrev->mWaylandPopupNext = mWaylandPopupNext;
  if (mWaylandPopupNext) {
    mWaylandPopupNext->mWaylandPopupPrev = mWaylandPopupPrev;
    mWaylandPopupNext->mPopupChanged = true;
  }
  mWaylandPopupNext = mWaylandPopupPrev = nullptr;
}

// Gtk refuses to map popup window with x < 0 && y < 0 relative coordinates
// see https://gitlab.gnome.org/GNOME/gtk/-/issues/4071
// as a workaround just fool around and place the popup temporary to 0,0.
bool nsWindow::WaylandPopupRemoveNegativePosition(int* aX, int* aY) {
  // https://gitlab.gnome.org/GNOME/gtk/-/issues/4071 applies to temporary
  // windows only
  GdkWindow* window = GetToplevelGdkWindow();
  if (!window || gdk_window_get_window_type(window) != GDK_WINDOW_TEMP) {
    return false;
  }

  LOG("nsWindow::WaylandPopupRemoveNegativePosition()");

  int x, y;
  gtk_window_get_position(GTK_WINDOW(mShell), &x, &y);
  bool moveBack = (x < 0 && y < 0);
  if (moveBack) {
    gtk_window_move(GTK_WINDOW(mShell), 0, 0);
    if (aX) {
      *aX = x;
    }
    if (aY) {
      *aY = y;
    }
  }

  gdk_window_get_geometry(window, &x, &y, nullptr, nullptr);
  if (x < 0 && y < 0) {
    gdk_window_move(window, 0, 0);
  }

  return moveBack;
}

void nsWindow::ShowWaylandPopupWindow() {
  LOG("nsWindow::ShowWaylandPopupWindow. Expected to see visible.");
  MOZ_ASSERT(IsWaylandPopup());

  if (!mPopupTrackInHierarchy) {
    LOG(" popup is not tracked in popup hierarchy, show it now");
    gtk_widget_show(mShell);
    return;
  }

  // Popup position was checked before gdk_window_move_to_rect() callback
  // so just show it.
  if (mPopupUseMoveToRect && mWaitingForMoveToRectCallback) {
    LOG(" active move-to-rect callback, show it as is");
    gtk_widget_show(mShell);
    return;
  }

  if (gtk_widget_is_visible(mShell)) {
    LOG(" is already visible, quit");
    return;
  }

  int x, y;
  bool moved = WaylandPopupRemoveNegativePosition(&x, &y);
  gtk_widget_show(mShell);
  if (moved) {
    LOG(" move back to (%d, %d) and show", x, y);
    gtk_window_move(GTK_WINDOW(mShell), x, y);
  }
}

void nsWindow::WaylandPopupMarkAsClosed() {
  LOG("nsWindow::WaylandPopupMarkAsClosed: [%p]\n"this);
  mPopupClosed = true;
  // If we have any child popup window notify it about
  // parent switch.
  if (mWaylandPopupNext) {
    mWaylandPopupNext->mPopupChanged = true;
  }
}

nsWindow* nsWindow::WaylandPopupFindLast(nsWindow* aPopup) {
  while (aPopup && aPopup->mWaylandPopupNext) {
    aPopup = aPopup->mWaylandPopupNext;
  }
  return aPopup;
}

// Hide and potentially removes popup from popup hierarchy.
void nsWindow::HideWaylandPopupWindow(bool aTemporaryHide,
                                      bool aRemoveFromPopupList) {
  LOG("nsWindow::HideWaylandPopupWindow: remove from list %d\n",
      aRemoveFromPopupList);
  if (aRemoveFromPopupList) {
    RemovePopupFromHierarchyList();
  }

  if (!mPopupClosed) {
    mPopupClosed = !aTemporaryHide;
  }

  bool visible = gtk_widget_is_visible(mShell);
  LOG(" gtk_widget_is_visible() = %d\n", visible);

  // Restore only popups which are really visible
  mPopupTemporaryHidden = aTemporaryHide && visible;

  // Hide only visible popups or popups closed pernamently.
  if (visible) {
    gtk_widget_hide(mShell);

    // If there's pending Move-To-Rect callback and we hide the popup
    // the callback won't be called any more.
    mWaitingForMoveToRectCallback = false;
  }

  if (mPopupClosed) {
    LOG(" Clearing mMoveToRectPopupSize\n");
    mMoveToRectPopupSize = {};
#ifdef MOZ_WAYLAND
    if (moz_container_wayland_is_waiting_to_show(mContainer)) {
      // We need to clear rendering queue, see Bug 1782948.
      LOG(" popup failed to show by Wayland compositor, clear rendering "
          "queue.");
      moz_container_wayland_clear_waiting_to_show_flag(mContainer);
      ClearRenderingQueue();
    }
#endif
  }
}

void nsWindow::HideWaylandToplevelWindow() {
  LOG("nsWindow::HideWaylandToplevelWindow: [%p]\n"this);
  if (mWaylandPopupNext) {
    nsWindow* popup = WaylandPopupFindLast(mWaylandPopupNext);
    while (popup->mWaylandToplevel != nullptr) {
      nsWindow* prev = popup->mWaylandPopupPrev;
      popup->HideWaylandPopupWindow(/* aTemporaryHide */ false,
                                    /* aRemoveFromPopupList */ true);
      popup = prev;
    }
  }
  WaylandStopVsync();
  gtk_widget_hide(mShell);
}

void nsWindow::ShowWaylandToplevelWindow() {
  MOZ_ASSERT(!IsWaylandPopup());
  LOG("nsWindow::ShowWaylandToplevelWindow");
  gtk_widget_show(mShell);
}

void nsWindow::WaylandPopupRemoveClosedPopups() {
  LOG("nsWindow::WaylandPopupRemoveClosedPopups()");
  nsWindow* popup = this;
  while (popup) {
    nsWindow* next = popup->mWaylandPopupNext;
    if (popup->mPopupClosed) {
      popup->HideWaylandPopupWindow(/* aTemporaryHide */ false,
                                    /* aRemoveFromPopupList */ true);
    }
    popup = next;
  }
}

// Hide all tooltips except the latest one.
void nsWindow::WaylandPopupHideTooltips() {
  LOG("nsWindow::WaylandPopupHideTooltips");
  MOZ_ASSERT(mWaylandToplevel == nullptr, "Should be called on toplevel only!");

  nsWindow* popup = mWaylandPopupNext;
  while (popup && popup->mWaylandPopupNext) {
    if (popup->mPopupType == PopupType::Tooltip) {
      LOG(" hidding tooltip [%p]", popup);
      popup->WaylandPopupMarkAsClosed();
    }
    popup = popup->mWaylandPopupNext;
  }
}

void nsWindow::WaylandPopupCloseOrphanedPopups() {
#ifdef MOZ_WAYLAND
  LOG("nsWindow::WaylandPopupCloseOrphanedPopups");
  MOZ_ASSERT(mWaylandToplevel == nullptr, "Should be called on toplevel only!");

  nsWindow* popup = mWaylandPopupNext;
  bool dangling = false;
  while (popup) {
    if (!dangling &&
        moz_container_wayland_is_waiting_to_show(popup->GetMozContainer())) {
      LOG(" popup [%p] is waiting to show, close all child popups", popup);
      dangling = true;
    } else if (dangling) {
      popup->WaylandPopupMarkAsClosed();
    }
    popup = popup->mWaylandPopupNext;
  }
#endif
}

// We can't show popups with remote content or overflow popups
// on top of regular ones.
// If there's any remote popup opened, close all parent popups of it.
void nsWindow::CloseAllPopupsBeforeRemotePopup() {
  LOG("nsWindow::CloseAllPopupsBeforeRemotePopup");
  MOZ_ASSERT(mWaylandToplevel == nullptr, "Should be called on toplevel only!");

  // Don't waste time when there's only one popup opened.
  if (!mWaylandPopupNext || mWaylandPopupNext->mWaylandPopupNext == nullptr) {
    return;
  }

  // Find the first opened remote content popup
  nsWindow* remotePopup = mWaylandPopupNext;
  while (remotePopup) {
    if (remotePopup->HasRemoteContent() ||
        remotePopup->IsWidgetOverflowWindow()) {
      LOG(" remote popup [%p]", remotePopup);
      break;
    }
    remotePopup = remotePopup->mWaylandPopupNext;
  }

  if (!remotePopup) {
    return;
  }

  // ...hide opened popups before the remote one.
  nsWindow* popup = mWaylandPopupNext;
  while (popup && popup != remotePopup) {
    LOG(" hidding popup [%p]", popup);
    popup->WaylandPopupMarkAsClosed();
    popup = popup->mWaylandPopupNext;
  }
}

static void GetLayoutPopupWidgetChain(
    nsTArray<nsIWidget*>* aLayoutWidgetHierarchy) {
  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
  pm->GetSubmenuWidgetChain(aLayoutWidgetHierarchy);
  aLayoutWidgetHierarchy->Reverse();
}

// Compare 'this' popup position in Wayland widget hierarchy
// (mWaylandPopupPrev/mWaylandPopupNext) with
// 'this' popup position in layout hierarchy.
//
// When aMustMatchParent is true we also request
// 'this' parents match, i.e. 'this' has the same parent in
// both layout and widget hierarchy.
bool nsWindow::IsPopupInLayoutPopupChain(
    nsTArray<nsIWidget*>* aLayoutWidgetHierarchy, bool aMustMatchParent) {
  int len = (int)aLayoutWidgetHierarchy->Length();
  for (int i = 0; i < len; i++) {
    if (this == (*aLayoutWidgetHierarchy)[i]) {
      if (!aMustMatchParent) {
        return true;
      }

      // Find correct parent popup for 'this' according to widget
      // hierarchy. That means we need to skip closed popups.
      nsWindow* parentPopup = nullptr;
      if (mWaylandPopupPrev != mWaylandToplevel) {
        parentPopup = mWaylandPopupPrev;
        while (parentPopup != mWaylandToplevel && parentPopup->mPopupClosed) {
          parentPopup = parentPopup->mWaylandPopupPrev;
        }
      }

      if (i == 0) {
        // We found 'this' popups as a first popup in layout hierarchy.
        // It matches layout hierarchy if it's first widget also in
        // wayland widget hierarchy (i.e. parent is null).
        return parentPopup == nullptr;
      }

      return parentPopup == (*aLayoutWidgetHierarchy)[i - 1];
    }
  }
  return false;
}

// Hide popups which are not in popup chain.
void nsWindow::WaylandPopupHierarchyHideByLayout(
    nsTArray<nsIWidget*>* aLayoutWidgetHierarchy) {
  LOG("nsWindow::WaylandPopupHierarchyHideByLayout");
  MOZ_ASSERT(mWaylandToplevel == nullptr, "Should be called on toplevel only!");

  // Hide all popups which are not in layout popup chain
  nsWindow* popup = mWaylandPopupNext;
  while (popup) {
    // Don't check closed popups and drag source popups and tooltips.
    if (!popup->mPopupClosed && popup->mPopupType != PopupType::Tooltip &&
        !popup->mSourceDragContext) {
      if (!popup->IsPopupInLayoutPopupChain(aLayoutWidgetHierarchy,
                                            /* aMustMatchParent */ false)) {
        LOG(" hidding popup [%p]", popup);
        popup->WaylandPopupMarkAsClosed();
      }
    }
    popup = popup->mWaylandPopupNext;
  }
}

// Mark popups outside of layout hierarchy
void nsWindow::WaylandPopupHierarchyValidateByLayout(
    nsTArray<nsIWidget*>* aLayoutWidgetHierarchy) {
  LOG("nsWindow::WaylandPopupHierarchyValidateByLayout");
  nsWindow* popup = mWaylandPopupNext;
  while (popup) {
    if (popup->mPopupType == PopupType::Tooltip) {
      popup->mPopupMatchesLayout = true;
    } else if (!popup->mPopupClosed) {
      popup->mPopupMatchesLayout = popup->IsPopupInLayoutPopupChain(
          aLayoutWidgetHierarchy, /* aMustMatchParent */ true);
      LOG(" popup [%p] parent window [%p] matches layout %d\n", (void*)popup,
          (void*)popup->mWaylandPopupPrev, popup->mPopupMatchesLayout);
    }
    popup = popup->mWaylandPopupNext;
  }
}

void nsWindow::WaylandPopupHierarchyHideTemporary() {
  LOG("nsWindow::WaylandPopupHierarchyHideTemporary()");
  nsWindow* popup = WaylandPopupFindLast(this);
  while (popup && popup != this) {
    LOG(" temporary hidding popup [%p]", popup);
    nsWindow* prev = popup->mWaylandPopupPrev;
    popup->HideWaylandPopupWindow(/* aTemporaryHide */ true,
                                  /* aRemoveFromPopupList */ false);
    popup = prev;
  }
}

void nsWindow::WaylandPopupHierarchyShowTemporaryHidden() {
  LOG("nsWindow::WaylandPopupHierarchyShowTemporaryHidden()");
  nsWindow* popup = this;
  while (popup) {
    if (popup->mPopupTemporaryHidden) {
      popup->mPopupTemporaryHidden = false;
      LOG(" showing temporary hidden popup [%p]", popup);
      popup->ShowWaylandPopupWindow();
    }
    popup = popup->mWaylandPopupNext;
  }
}

void nsWindow::WaylandPopupHierarchyCalculatePositions() {
  LOG("nsWindow::WaylandPopupHierarchyCalculatePositions()");

  // Set widget hierarchy in Gtk
  nsWindow* popup = mWaylandToplevel->mWaylandPopupNext;
  while (popup) {
    LOG(" popup [%p] set parent window [%p]", (void*)popup,
        (void*)popup->mWaylandPopupPrev);
    GtkWindowSetTransientFor(GTK_WINDOW(popup->mShell),
                             GTK_WINDOW(popup->mWaylandPopupPrev->mShell));
    popup = popup->mWaylandPopupNext;
  }

  popup = this;
  while (popup) {
    // Anchored window has mPopupPosition already calculated against
    // its parent, no need to recalculate.
    LOG(" popup [%p] bounds [%d, %d] -> [%d x %d]", popup,
        (int)(popup->mBounds.x / FractionalScaleFactor()),
        (int)(popup->mBounds.y / FractionalScaleFactor()),
        (int)(popup->mBounds.width / FractionalScaleFactor()),
        (int)(popup->mBounds.height / FractionalScaleFactor()));
#ifdef MOZ_LOGGING
    if (LOG_ENABLED()) {
      if (nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame())) {
        auto r = LayoutDeviceRect::FromAppUnitsRounded(
            popupFrame->GetRect(),
            popupFrame->PresContext()->AppUnitsPerDevPixel());
        LOG(" popup [%p] layout [%d, %d] -> [%d x %d]", popup, r.x, r.y,
            r.width, r.height);
      }
    }
#endif
    if (popup->WaylandPopupIsFirst()) {
      LOG(" popup [%p] has toplevel as parent", popup);
      popup->mRelativePopupPosition = popup->mPopupPosition;
    } else {
      if (popup->mPopupAnchored) {
        LOG(" popup [%p] is anchored", popup);
        if (!popup->mPopupMatchesLayout) {
          NS_WARNING("Anchored popup does not match layout!");
        }
      }
      GdkPoint parent = popup->WaylandGetParentPosition();

      LOG(" popup [%p] uses transformed coordinates\n", popup);
      LOG(" parent position [%d, %d]\n", parent.x, parent.y);
      LOG(" popup position [%d, %d]\n", popup->mPopupPosition.x,
          popup->mPopupPosition.y);

      popup->mRelativePopupPosition.x = popup->mPopupPosition.x - parent.x;
      popup->mRelativePopupPosition.y = popup->mPopupPosition.y - parent.y;
    }
    LOG(" popup [%p] transformed popup coordinates from [%d, %d] to [%d, %d]",
        popup, popup->mPopupPosition.x, popup->mPopupPosition.y,
        popup->mRelativePopupPosition.x, popup->mRelativePopupPosition.y);
    popup = popup->mWaylandPopupNext;
  }
}

// The MenuList popups are used as dropdown menus for example in WebRTC
// microphone/camera chooser or autocomplete widgets.
bool nsWindow::WaylandPopupIsMenu() {
  nsMenuPopupFrame* menuPopupFrame = GetMenuPopupFrame(GetFrame());
  if (menuPopupFrame) {
    return mPopupType == PopupType::Menu && !menuPopupFrame->IsMenuList();
  }
  return false;
}

bool nsWindow::WaylandPopupIsContextMenu() {
  nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
  if (!popupFrame) {
    return false;
  }
  return popupFrame->IsContextMenu();
}

bool nsWindow::WaylandPopupIsPermanent() {
  nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
  if (!popupFrame) {
    // We can always hide popups without frames.
    return false;
  }
  return popupFrame->IsNoAutoHide();
}

bool nsWindow::WaylandPopupIsAnchored() {
  nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
  if (!popupFrame) {
    // We can always hide popups without frames.
    return false;
  }
  return !!popupFrame->GetAnchor();
}

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;
}

bool nsWindow::WaylandPopupIsFirst() {
  return !mWaylandPopupPrev || !mWaylandPopupPrev->mWaylandToplevel;
}

nsWindow* nsWindow::GetEffectiveParent() {
  GtkWindow* parentGtkWindow = gtk_window_get_transient_for(GTK_WINDOW(mShell));
  if (!parentGtkWindow || !GTK_IS_WIDGET(parentGtkWindow)) {
    return nullptr;
  }
  return get_window_for_gtk_widget(GTK_WIDGET(parentGtkWindow));
}

GdkPoint nsWindow::WaylandGetParentPosition() {
  GdkPoint topLeft = {0, 0};
  nsWindow* window = GetEffectiveParent();
  if (window->IsPopup()) {
    topLeft = DevicePixelsToGdkPointRoundDown(window->mBounds.TopLeft());
  }
  LOG("nsWindow::WaylandGetParentPosition() [%d, %d]\n", topLeft.x, topLeft.y);
  return topLeft;
}

#ifdef MOZ_LOGGING
void nsWindow::LogPopupHierarchy() {
  if (!LOG_ENABLED()) {
    return;
  }

  LOG("Widget Popup Hierarchy:\n");
  if (!mWaylandToplevel->mWaylandPopupNext) {
    LOG(" Empty\n");
  } else {
    int indent = 4;
    nsWindow* popup = mWaylandToplevel->mWaylandPopupNext;
    while (popup) {
      nsPrintfCString indentString("%*s", indent, " ");
      LOG("%s %s %s nsWindow [%p] Menu %d Permanent %d ContextMenu %d "
          "Anchored %d Visible %d MovedByRect %d\n",
          indentString.get(), popup->GetFrameTag().get(),
          popup->GetPopupTypeName().get(), popup, popup->WaylandPopupIsMenu(),
          popup->WaylandPopupIsPermanent(), popup->mPopupContextMenu,
          popup->mPopupAnchored, gtk_widget_is_visible(popup->mShell),
          popup->mPopupUseMoveToRect);
      indent += 4;
      popup = popup->mWaylandPopupNext;
    }
  }

  LOG("Layout Popup Hierarchy:\n");
  AutoTArray<nsIWidget*, 5> widgetChain;
  GetLayoutPopupWidgetChain(&widgetChain);
  if (widgetChain.Length() == 0) {
    LOG(" Empty\n");
  } else {
    for (unsigned long i = 0; i < widgetChain.Length(); i++) {
      nsWindow* window = static_cast<nsWindow*>(widgetChain[i]);
      nsPrintfCString indentString("%*s", (int)(i + 1) * 4, " ");
      if (window) {
        LOG("%s %s %s nsWindow [%p] Menu %d Permanent %d ContextMenu %d "
            "Anchored %d Visible %d MovedByRect %d\n",
            indentString.get(), window->GetFrameTag().get(),
            window->GetPopupTypeName().get(), window,
            window->WaylandPopupIsMenu(), window->WaylandPopupIsPermanent(),
            window->mPopupContextMenu, window->mPopupAnchored,
            gtk_widget_is_visible(window->mShell), window->mPopupUseMoveToRect);
      } else {
        LOG("%s null window\n", indentString.get());
      }
    }
  }
}
#endif

nsWindow* nsWindow::GetTopmostWindow() {
  if (nsView* view = nsView::GetViewFor(this)) {
    if (nsView* parentView = view->GetParent()) {
      if (nsIWidget* parentWidget = parentView->GetNearestWidget(nullptr)) {
        return static_cast<nsWindow*>(parentWidget);
      }
    }
  }
  return nullptr;
}

// Configure Wayland popup. If true is returned we need to track popup
// in popup hierarchy. Otherwise we just show it as is.
bool nsWindow::WaylandPopupConfigure() {
  if (mIsDragPopup) {
    return false;
  }

  // Don't track popups without frame
  nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
  if (!popupFrame) {
    return false;
  }

  // Popup state can be changed, see Bug 1728952.
  bool permanentStateMatches =
      mPopupTrackInHierarchy == !WaylandPopupIsPermanent();

  // Popup permanent state (noautohide attribute) can change during popup life.
  if (mPopupTrackInHierarchyConfigured && permanentStateMatches) {
    return mPopupTrackInHierarchy;
  }

  // Configure persistent popup params only once.
  // WaylandPopupIsAnchored() can give it wrong value after
  // nsMenuPopupFrame::MoveTo() call which we use in move-to-rect callback
  // to position popup after wayland position change.
  if (!mPopupTrackInHierarchyConfigured) {
    mPopupAnchored = WaylandPopupIsAnchored();
    mPopupContextMenu = WaylandPopupIsContextMenu();
  }

  LOG("nsWindow::WaylandPopupConfigure tracked %d anchored %d hint %d\n",
      mPopupTrackInHierarchy, mPopupAnchored, int(mPopupType));

  // Permanent state changed and popup is mapped.
  // We need to switch popup type but that's done when popup is mapped
  // by Gtk so we need to unmap the popup here.
  // It will be mapped again by gtk_widget_show().
  if (!permanentStateMatches && mIsMapped) {
    LOG(" permanent state change from %d to %d, unmapping",
        mPopupTrackInHierarchy, !WaylandPopupIsPermanent());
    gtk_widget_unmap(mShell);
  }

  mPopupTrackInHierarchy = !WaylandPopupIsPermanent();
  LOG(" tracked in hierarchy %d\n", mPopupTrackInHierarchy);

  // See gdkwindow-wayland.c and
  // should_map_as_popup()/should_map_as_subsurface()
  GdkWindowTypeHint gtkTypeHint;
  switch (mPopupType) {
    case PopupType::Menu:
      // GDK_WINDOW_TYPE_HINT_POPUP_MENU is mapped as xdg_popup by default.
      // We use this type for all menu popups.
      gtkTypeHint = GDK_WINDOW_TYPE_HINT_POPUP_MENU;
      LOG(" popup type Menu");
      break;
    case PopupType::Tooltip:
      gtkTypeHint = GDK_WINDOW_TYPE_HINT_TOOLTIP;
      LOG(" popup type Tooltip");
      break;
    default:
      gtkTypeHint = GDK_WINDOW_TYPE_HINT_UTILITY;
      LOG(" popup type Utility");
      break;
  }

  if (!mPopupTrackInHierarchy) {
    // GDK_WINDOW_TYPE_HINT_UTILITY is mapped as wl_subsurface
    // by default.
    LOG(" not tracked in popup hierarchy, switch to Utility");
    gtkTypeHint = GDK_WINDOW_TYPE_HINT_UTILITY;
  }
  gtk_window_set_type_hint(GTK_WINDOW(mShell), gtkTypeHint);

  mPopupTrackInHierarchyConfigured = true;
  return mPopupTrackInHierarchy;
}

bool nsWindow::IsInPopupHierarchy() {
  return mPopupTrackInHierarchy && mWaylandToplevel && mWaylandPopupPrev;
}

void nsWindow::AddWindowToPopupHierarchy() {
  LOG("nsWindow::AddWindowToPopupHierarchy\n");
  if (!GetFrame()) {
    LOG(" Window without frame cannot be added as popup!\n");
    return;
  }

  // Check if we're already in the hierarchy
  if (!IsInPopupHierarchy()) {
    mWaylandToplevel = GetTopmostWindow();
    AppendPopupToHierarchyList(mWaylandToplevel);
  }
}

// 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
// popup needs to have an unique parent.
void nsWindow::UpdateWaylandPopupHierarchy() {
  LOG("nsWindow::UpdateWaylandPopupHierarchy\n");

  // This popup hasn't been added to popup hierarchy yet so no need to
  // do any configurations.
  if (!IsInPopupHierarchy()) {
    LOG(" popup isn't in hierarchy\n");
    return;
  }

#ifdef MOZ_LOGGING
  LogPopupHierarchy();
  auto printPopupHierarchy = MakeScopeExit([&] { LogPopupHierarchy(); });
#endif

  // Hide all tooltips without the last one. Tooltip can't be popup parent.
  mWaylandToplevel->WaylandPopupHideTooltips();

  // See Bug 1709254 / https://gitlab.gnome.org/GNOME/gtk/-/issues/5092
  // It's possible that Wayland compositor refuses to show
  // a popup although Gtk claims it's visible.
  // We don't know if the popup is shown or not.
  // To avoid application crash refuse to create any child of such invisible
  // popup and close any child of it now.
  mWaylandToplevel->WaylandPopupCloseOrphanedPopups();

  // Check if we have any remote content / overflow window in hierarchy.
  // We can't attach such widget on top of other popup.
  mWaylandToplevel->CloseAllPopupsBeforeRemotePopup();

  // Check if your popup hierarchy matches layout hierarchy.
  // For instance we should not connect hamburger menu on top
  // of context menu.
  // Close all popups from different layout chains if possible.
  AutoTArray<nsIWidget*, 5> layoutPopupWidgetChain;
  GetLayoutPopupWidgetChain(&layoutPopupWidgetChain);

  mWaylandToplevel->WaylandPopupHierarchyHideByLayout(&layoutPopupWidgetChain);
  mWaylandToplevel->WaylandPopupHierarchyValidateByLayout(
      &layoutPopupWidgetChain);

  // Now we have Popup hierarchy complete.
  // Find first unchanged (and still open) popup to start with hierarchy
  // changes.
  nsWindow* changedPopup = mWaylandToplevel->mWaylandPopupNext;
  while (changedPopup) {
    // Stop when parent of this popup was changed and we need to recalc
    // popup position.
    if (changedPopup->mPopupChanged) {
      break;
    }
    // Stop when this popup is closed.
    if (changedPopup->mPopupClosed) {
      break;
    }
    changedPopup = changedPopup->mWaylandPopupNext;
  }

  // We don't need to recompute popup positions, quit now.
  if (!changedPopup) {
    LOG(" changed Popup is null, quit.\n");
    return;
  }

  LOG(" first changed popup [%p]\n", (void*)changedPopup);

  // Hide parent popups if necessary (there are layout discontinuity)
  // reposition the popup and show them again.
  changedPopup->WaylandPopupHierarchyHideTemporary();

  nsWindow* parentOfchangedPopup = nullptr;
  if (changedPopup->mPopupClosed) {
    parentOfchangedPopup = changedPopup->mWaylandPopupPrev;
  }
  changedPopup->WaylandPopupRemoveClosedPopups();

  // It's possible that changedPopup was removed from widget hierarchy,
  // in such case use child popup of the removed one if there's any.
  if (!changedPopup->IsInPopupHierarchy()) {
    if (!parentOfchangedPopup || !parentOfchangedPopup->mWaylandPopupNext) {
      LOG(" last popup was removed, quit.\n");
      return;
    }
    changedPopup = parentOfchangedPopup->mWaylandPopupNext;
  }

  GetLayoutPopupWidgetChain(&layoutPopupWidgetChain);
  mWaylandToplevel->WaylandPopupHierarchyValidateByLayout(
      &layoutPopupWidgetChain);

  changedPopup->WaylandPopupHierarchyCalculatePositions();

  nsWindow* popup = changedPopup;
  while (popup) {
    const bool useMoveToRect = [&] {
      if (!StaticPrefs::widget_wayland_use_move_to_rect_AtStartup()) {
        return false;  // Not available.
      }
      if (!popup->mPopupMatchesLayout) {
        // We can use move_to_rect only when popups in popup hierarchy matches
        // layout hierarchy as move_to_rect request that parent/child
        // popups are adjacent.
        return false;
      }
      if (popup->mPopupType == PopupType::Panel &&
          popup->WaylandPopupIsFirst() &&
          popup->WaylandPopupFitsToplevelWindow(/* aMove */ true)) {
        // Workaround for https://gitlab.gnome.org/GNOME/gtk/-/issues/1986
        //
        // PopupType::Panel types are used for extension popups which may be
        // resized. If such popup uses move-to-rect, we need to hide it before
        // resize and show it again. That leads to massive flickering
        // so use plain move if possible to avoid it.
        //
        // Bug 1760276 - don't use move-to-rect when popup is inside main
        // Firefox window.
        //
        // Use it for first popups only due to another mutter bug
        // https://gitlab.gnome.org/GNOME/gtk/-/issues/5089
        // https://bugzilla.mozilla.org/show_bug.cgi?id=1784873
        return false;
      }
      if (!popup->WaylandPopupIsFirst() &&
          !popup->mWaylandPopupPrev->WaylandPopupIsFirst() &&
          !popup->mWaylandPopupPrev->mPopupUseMoveToRect) {
        // We can't use move-to-rect if there are more parents of
        // wl_subsurface popups types.
        //
        // It's because wl_subsurface is ignored by xgd_popup
        // (created by move-to-rect) so our popup scenario:
        //
        // toplevel -> xgd_popup(1) -> wl_subsurface(2) -> xgd_popup(3)
        //
        // looks for Wayland compositor as:
        //
        // toplevel -> xgd_popup(1) -> xgd_popup(3)
        //
        // If xgd_popup(1) and xgd_popup(3) are not connected
        // move-to-rect applied to xgd_popup(3) fails and we get missing popup.
        return false;
      }
      return true;
    }();

    LOG(" popup [%p] matches layout [%d] anchored [%d] first popup [%d] use "
        "move-to-rect %d\n",
        popup, popup->mPopupMatchesLayout, popup->mPopupAnchored,
        popup->WaylandPopupIsFirst(), useMoveToRect);

    popup->mPopupUseMoveToRect = useMoveToRect;
    popup->WaylandPopupMoveImpl();
    popup->mPopupChanged = false;
    popup = popup->mWaylandPopupNext;
  }

  changedPopup->WaylandPopupHierarchyShowTemporaryHidden();
}

static void NativeMoveResizeCallback(GdkWindow* window,
                                     const GdkRectangle* flipped_rect,
                                     const GdkRectangle* final_rect,
                                     gboolean flipped_x, gboolean flipped_y,
                                     void* aWindow) {
  LOG_POPUP("[%p] NativeMoveResizeCallback flipped_x %d flipped_y %d\n",
            aWindow, flipped_x, flipped_y);
  LOG_POPUP("[%p] new position [%d, %d] -> [%d x %d]", aWindow,
            final_rect->x, final_rect->y, final_rect->width,
            final_rect->height);
  nsWindow* wnd = get_window_for_gdk_window(window);

  wnd->NativeMoveResizeWaylandPopupCallback(final_rect, flipped_x, flipped_y);
}

// When popup is repositioned by widget code, we need to notify
// layout about it. It's because we control popup placement
// on widget on Wayland so layout may have old popup size/coordinates.
void nsWindow::WaylandPopupPropagateChangesToLayout(bool aMove, bool aResize) {
  LOG("nsWindow::WaylandPopupPropagateChangesToLayout()");

  if (aResize) {
    LOG(" needSizeUpdate\n");
    if (nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame())) {
      RefPtr<PresShell> presShell = popupFrame->PresShell();
      presShell->FrameNeedsReflow(popupFrame, IntrinsicDirty::None,
                                  NS_FRAME_IS_DIRTY);
    }
  }
  if (aMove) {
    LOG(" needPositionUpdate, bounds [%d, %d]", mBounds.x, mBounds.y);
    NotifyWindowMoved(mBounds.x, mBounds.y, ByMoveToRect::Yes);
  }
}

void nsWindow::NativeMoveResizeWaylandPopupCallback(
    const GdkRectangle* aFinalSize, bool aFlippedX, bool aFlippedY) {
  // We're getting move-to-rect callback without move-to-rect call.
  // That indicates a compositor bug. It happens when a window is hidden and
  // shown again before move-to-rect callback is fired.
  // It may lead to incorrect popup placement as we may call
  // gtk_window_move() between hide & show.
  // See Bug 1777919, 1789581.
#if MOZ_LOGGING
  if (!mWaitingForMoveToRectCallback) {
    LOG(" Bogus move-to-rect callback! Expect wrong popup coordinates.");
  }
#endif

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=92 H=95 G=93

¤ Dauer der Verarbeitung: 0.26 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.