Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/widget/gtk/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 48 kB image not shown  

Quelle  nsWaylandDisplay.cpp   Sprache: C

 

/* 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 "nsWaylandDisplay.h"
#include "DMABufFormats.h"

#include "base/message_loop.h"    // for MessageLoop
#include "base/task.h"            // for NewRunnableMethod, etc
#include "mozilla/gfx/Logging.h"  // for gfxCriticalNote
#include "mozilla/StaticMutex.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/StaticPrefs_widget.h"
#include "mozilla/StaticPrefs_general.h"
#include "mozilla/Sprintf.h"
#include "WidgetUtilsGtk.h"
#include "nsGtkKeyUtils.h"
#include "nsGtkUtils.h"
#include "nsLayoutUtils.h"
#include "nsWindow.h"
#include "wayland-proxy.h"
#include "ScreenHelperGTK.h"
#include "nsIAppStartup.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"

#include <dlfcn.h>

#undef LOG
#undef LOG_VERBOSE
#ifdef MOZ_LOGGING
#  include "mozilla/Logging.h"
#  include "nsTArray.h"
#  include "Units.h"
extern mozilla::LazyLogModule gWidgetWaylandLog;
#  define LOG(...) \
    MOZ_LOG(gWidgetWaylandLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
#  define LOG_VERBOSE(...) \
    MOZ_LOG(gWidgetWaylandLog, mozilla::LogLevel::Verbose, (__VA_ARGS__))
#else
#  define LOG(...)
#  define LOG_VERBOSE(...)
#endif /* MOZ_LOGGING */

namespace mozilla::widget {

static nsWaylandDisplay* gWaylandDisplay;

void WaylandDisplayRelease() {
  MOZ_RELEASE_ASSERT(NS_IsMainThread(),
                     "WaylandDisplay can be released in main thread only!");
  if (!gWaylandDisplay) {
    return;
  }
  LOG("WaylandDisplayRelease()");
  delete gWaylandDisplay;
  gWaylandDisplay = nullptr;
}

wl_display* WaylandDisplayGetWLDisplay() {
  GdkDisplay* disp = gdk_display_get_default();
  if (!GdkIsWaylandDisplay(disp)) {
    return nullptr;
  }
  return gdk_wayland_display_get_wl_display(disp);
}

nsWaylandDisplay* WaylandDisplayGet() {
  if (!gWaylandDisplay) {
    MOZ_RELEASE_ASSERT(NS_IsMainThread(),
                       "WaylandDisplay can be created in main thread only!");
    wl_display* waylandDisplay = WaylandDisplayGetWLDisplay();
    if (!waylandDisplay) {
      return nullptr;
    }
    // We're setting Wayland client buffer size here (i.e. our write buffer).
    // Server buffer size is set by compositor and we may use the same buffer
    // sizes on both sides. Mutter uses 1024 * 1024 (1M) so let's use the same
    // value.
    wl_display_set_max_buffer_size(waylandDisplay, 1024 * 1024);
    gWaylandDisplay = new nsWaylandDisplay(waylandDisplay);
    gWaylandDisplay->Init();
  }
  return gWaylandDisplay;
}

void nsWaylandDisplay::SetShm(wl_shm* aShm) { mShm = aShm; }

class WaylandPointerEvent {
 public:
  constexpr WaylandPointerEvent() = default;

  RefPtr<nsWindow> TakeWindow(wl_surface* aSurface) {
    if (!aSurface) {
      mWindow = nullptr;
    } else {
      GdkWindow* window =
          static_cast<GdkWindow*>(wl_surface_get_user_data(aSurface));
      mWindow = window ? static_cast<nsWindow*>(
                             g_object_get_data(G_OBJECT(window), "nsWindow"))
                       : nullptr;
    }
    return mWindow;
  }
  already_AddRefed<nsWindow> GetAndClearWindow() { return mWindow.forget(); }
  RefPtr<nsWindow> GetWindow() { return mWindow; }

  void SetSource(int32_t aSource) { mSource = aSource; }

  void SetDelta120(uint32_t aAxis, int32_t aDelta) {
    switch (aAxis) {
      case WL_POINTER_AXIS_VERTICAL_SCROLL:
        mDeltaY = aDelta / 120.0f;
        break;
      case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
        mDeltaX = aDelta / 120.0f;
        break;
      default:
        NS_WARNING("WaylandPointerEvent::SetDelta120(): wrong axis!");
        break;
    }
  }

  void SetTime(uint32_t aTime) { mTime = aTime; }

  void SendScrollEvent() {
    if (!mWindow || !nsLayoutUtils::IsSmoothScrollingEnabled()) {
      return;
    }

    // nsWindow::OnSmoothScrollEvent() may spin event loop so
    // mWindow/mSource/delta may be replaced.
    int32_t source = mSource;
    float deltaX = mDeltaX;
    float deltaY = mDeltaY;

    mSource = -1;
    mDeltaX = mDeltaY = 0.0f;

    // We process wheel events only now.
    if (source != WL_POINTER_AXIS_SOURCE_WHEEL) {
      return;
    }

    RefPtr<nsWindow> win = mWindow;
    uint32_t eventTime = mTime;
    win->OnSmoothScrollEvent(eventTime, deltaX, deltaY);
  }

  void Clear() { mWindow = nullptr; }

 private:
  StaticRefPtr<nsWindow> mWindow;
  uint32_t mTime = 0;
  int32_t mSource = 0;
  float mDeltaX = 0;
  float mDeltaY = 0;
};

static WaylandPointerEvent sHoldGesture;

static void gesture_hold_begin(void* data,
                               struct zwp_pointer_gesture_hold_v1* hold,
                               uint32_t serial, uint32_t time,
                               struct wl_surface* surface, uint32_t fingers) {
  RefPtr<nsWindow> window = sHoldGesture.TakeWindow(surface);
  if (!window) {
    return;
  }
  window->OnTouchpadHoldEvent(GDK_TOUCHPAD_GESTURE_PHASE_BEGIN, time, fingers);
}

static void gesture_hold_end(void* data,
                             struct zwp_pointer_gesture_hold_v1* hold,
                             uint32_t serial, uint32_t time,
                             int32_t cancelled) {
  RefPtr<nsWindow> window = sHoldGesture.GetAndClearWindow();
  if (!window) {
    return;
  }
  window->OnTouchpadHoldEvent(cancelled ? GDK_TOUCHPAD_GESTURE_PHASE_CANCEL
                                        : GDK_TOUCHPAD_GESTURE_PHASE_END,
                              time, 0);
}

static const struct zwp_pointer_gesture_hold_v1_listener gesture_hold_listener =
    {gesture_hold_begin, gesture_hold_end};

static WaylandPointerEvent sScrollEvent;

uint32_t gLastSerial = 0;
uint32_t nsWaylandDisplay::GetLastEventSerial() { return gLastSerial; }

static void pointer_handle_enter(void* data, struct wl_pointer* pointer,
                                 uint32_t serial, struct wl_surface* surface,
                                 wl_fixed_t sx, wl_fixed_t sy) {
  sScrollEvent.TakeWindow(surface);
}

static void pointer_handle_leave(void* data, struct wl_pointer* pointer,
                                 uint32_t serial, struct wl_surface* surface) {
  sScrollEvent.Clear();
}

static void pointer_handle_motion(void* data, struct wl_pointer* pointer,
                                  uint32_t time, wl_fixed_t sx, wl_fixed_t sy) {
}

static void pointer_handle_button(void* data, struct wl_pointer* pointer,
                                  uint32_t serial, uint32_t time,
                                  uint32_t button, uint32_t state) {
  if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
    gLastSerial = serial;
  }
}

static void pointer_handle_axis(void* data, struct wl_pointer* pointer,
                                uint32_t time, uint32_t axis,
                                wl_fixed_t value) {
  sScrollEvent.SetTime(time);
}

static void pointer_handle_frame(void* data, struct wl_pointer* pointer) {
  sScrollEvent.SendScrollEvent();
}

static void pointer_handle_axis_source(
    void* data, struct wl_pointer* pointer,
    /*enum wl_pointer_axis_source */ uint32_t source) {
  sScrollEvent.SetSource(source);
}

static void pointer_handle_axis_stop(void* data, struct wl_pointer* pointer,
                                     uint32_t time, uint32_t axis) {}

static void pointer_handle_axis_discrete(void* data, struct wl_pointer* pointer,
                                         uint32_t axis, int32_t value) {}

static void pointer_handle_axis_value120(void* data, struct wl_pointer* pointer,
                                         uint32_t axis, int32_t value) {
  sScrollEvent.SetDelta120(axis, value);
}

/*
 * Example of scroll events we get for various devices. Note that
 * even three different devices has the same wl_pointer.
 *
 * Standard mouse wheel:
 *
 *  pointer_handle_axis_source pointer 0x7fd14fd4bac0 source 0
 *  pointer_handle_axis_value120 pointer 0x7fd14fd4bac0 value 120
 *  pointer_handle_axis pointer 0x7fd14fd4bac0 time 9470441 value 10.000000
 *  pointer_handle_frame
 *
 * Hi-res mouse wheel:
 *
 * pointer_handle_axis_source pointer 0x7fd14fd4bac0 source 0
 * pointer_handle_axis_value120 pointer 0x7fd14fd4bac0 value -24
 * pointer_handle_axis pointer 0x7fd14fd4bac0 time 9593205 value -1.992188
 * pointer_handle_frame
 *
 * Touchpad:
 *
 * pointer_handle_axis_source pointer 0x7fd14fd4bac0 source 1
 * pointer_handle_axis pointer 0x7fd14fd4bac0 time 9431830 value 0.312500
 * pointer_handle_axis pointer 0x7fd14fd4bac0 time 9431830 value -1.015625
 * pointer_handle_frame
 */


static const struct moz_wl_pointer_listener pointer_listener = {
    pointer_handle_enter,         pointer_handle_leave,
    pointer_handle_motion,        pointer_handle_button,
    pointer_handle_axis,          pointer_handle_frame,
    pointer_handle_axis_source,   pointer_handle_axis_stop,
    pointer_handle_axis_discrete, pointer_handle_axis_value120,
};

void nsWaylandDisplay::SetPointer(wl_pointer* aPointer) {
  // Don't even try on such old interface
  if (wl_proxy_get_version((struct wl_proxy*)aPointer) <
      WL_POINTER_RELEASE_SINCE_VERSION) {
    return;
  }

  MOZ_DIAGNOSTIC_ASSERT(!mPointer);
  mPointer = aPointer;

  // We're interested in pointer_handle_axis_value120() only for now.
  if (wl_proxy_get_version((struct wl_proxy*)aPointer) >=
      WL_POINTER_AXIS_VALUE120_SINCE_VERSION) {
    wl_pointer_add_listener(
        mPointer, (const wl_pointer_listener*)&pointer_listener, this);
  }

  // mPointerGestures is set by zwp_pointer_gestures_v1 if we have it.
  if (mPointerGestures) {
    mPointerGestureHold =
        zwp_pointer_gestures_v1_get_hold_gesture(mPointerGestures, mPointer);
    zwp_pointer_gesture_hold_v1_set_user_data(mPointerGestureHold, this);
    zwp_pointer_gesture_hold_v1_add_listener(mPointerGestureHold,
                                             &gesture_hold_listener, this);
  }
}

void nsWaylandDisplay::RemovePointer() {
  wl_pointer_release(mPointer);
  mPointer = nullptr;
}

static void seat_handle_capabilities(void* data, struct wl_seat* seat,
                                     unsigned int caps) {
  auto* display = static_cast<nsWaylandDisplay*>(data);
  if (!display) {
    return;
  }

  if ((caps & WL_SEAT_CAPABILITY_POINTER) && !display->GetPointer()) {
    display->SetPointer(wl_seat_get_pointer(seat));
  } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && display->GetPointer()) {
    display->RemovePointer();
  }

  wl_keyboard* keyboard = display->GetKeyboard();
  if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !keyboard) {
    display->SetKeyboard(wl_seat_get_keyboard(seat));
  } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && keyboard) {
    display->ClearKeyboard();
  }

  wl_touch* touch = display->GetTouch();
  if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !touch) {
    display->SetTouch(wl_seat_get_touch(seat));
  } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && touch) {
    display->ClearTouch();
  }
}

static void seat_handle_name(void* data, struct wl_seat* seat,
                             const char* name) {
  /* We don't care about the name. */
}

static const struct wl_seat_listener seat_listener = {
    seat_handle_capabilities,
    seat_handle_name,
};

void nsWaylandDisplay::SetSeat(wl_seat* aSeat, int aSeatId) {
  mSeat = aSeat;
  mSeatId = aSeatId;
  wl_seat_add_listener(aSeat, &seat_listener, this);
}

void nsWaylandDisplay::RemoveSeat(int aSeatId) {
  if (mSeatId == aSeatId) {
    mSeat = nullptr;
    mSeatId = -1;
  }
}

/* This keymap routine is derived from weston-2.0.0/clients/simple-im.c
 */

static void keyboard_handle_keymap(void* data, struct wl_keyboard* wl_keyboard,
                                   uint32_t format, int fd, uint32_t size) {
  KeymapWrapper::HandleKeymap(format, fd, size);
}

static void keyboard_handle_enter(void* data, struct wl_keyboard* keyboard,
                                  uint32_t serial, struct wl_surface* surface,
                                  struct wl_array* keys) {
  gLastSerial = serial;
}

static void keyboard_handle_leave(void* data, struct wl_keyboard* keyboard,
                                  uint32_t serial, struct wl_surface* surface) {
  KeymapWrapper::ResetRepeatState();
}

static void keyboard_handle_key(void* data, struct wl_keyboard* keyboard,
                                uint32_t serial, uint32_t time, uint32_t key,
                                uint32_t state) {
  if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
    gLastSerial = serial;
  }
  // hardware key code is +8.
  // https://gitlab.gnome.org/GNOME/gtk/-/blob/3.24.41/gdk/wayland/gdkdevice-wayland.c#L2341
  KeymapWrapper::KeyboardHandlerForWayland(serial, key + 8, state);
}

static void keyboard_handle_modifiers(void* data, struct wl_keyboard* keyboard,
                                      uint32_t serial, uint32_t mods_depressed,
                                      uint32_t mods_latched,
                                      uint32_t mods_locked, uint32_t group) {}
static void keyboard_handle_repeat_info(void* data,
                                        struct wl_keyboard* keyboard,
                                        int32_t rate, int32_t delay) {}

static const struct wl_keyboard_listener keyboard_listener = {
    keyboard_handle_keymap,    keyboard_handle_enter,
    keyboard_handle_leave,     keyboard_handle_key,
    keyboard_handle_modifiers, keyboard_handle_repeat_info};

void nsWaylandDisplay::SetKeyboard(wl_keyboard* aKeyboard) {
  MOZ_ASSERT(aKeyboard);
  MOZ_DIAGNOSTIC_ASSERT(!mKeyboard);
  mKeyboard = aKeyboard;
  wl_keyboard_add_listener(mKeyboard, &keyboard_listener, nullptr);
}

void nsWaylandDisplay::ClearKeyboard() {
  if (mKeyboard) {
    wl_keyboard_release(mKeyboard);
    mKeyboard = nullptr;
    KeymapWrapper::ClearKeymap();
  }
}

static void touch_handle_down(void* data, struct wl_touch* touch,
                              uint32_t serial, uint32_t time,
                              struct wl_surface* surface, int32_t id,
                              wl_fixed_t x, wl_fixed_t y) {
  gLastSerial = serial;
}

static void touch_handle_up(void* data, struct wl_touch* touch, uint32_t serial,
                            uint32_t time, int32_t id) {}

static void touch_handle_motion(void* data, struct wl_touch* touch,
                                uint32_t time, int32_t id, wl_fixed_t x,
                                wl_fixed_t y) {}

static void touch_handle_frame(void* data, struct wl_touch* touch) {}

static void touch_handle_cancel(void* data, struct wl_touch* touch) {}

static const struct wl_touch_listener touch_listener = {
    touch_handle_down,  touch_handle_up,     touch_handle_motion,
    touch_handle_frame, touch_handle_cancel,
};

void nsWaylandDisplay::SetTouch(wl_touch* aTouch) {
  MOZ_ASSERT(aTouch);
  MOZ_DIAGNOSTIC_ASSERT(!mTouch);
  mTouch = aTouch;
  wl_touch_add_listener(mTouch, &touch_listener, nullptr);
}

void nsWaylandDisplay::ClearTouch() {
  MozClearPointer(mTouch, wl_touch_release);
}

void nsWaylandDisplay::SetCompositor(wl_compositor* aCompositor) {
  mCompositor = aCompositor;
}

void nsWaylandDisplay::SetSubcompositor(wl_subcompositor* aSubcompositor) {
  mSubcompositor = aSubcompositor;
}

void nsWaylandDisplay::SetDataDeviceManager(
    wl_data_device_manager* aDataDeviceManager) {
  mDataDeviceManager = aDataDeviceManager;
}

void nsWaylandDisplay::SetPrimarySelectionDeviceManager(
    gtk_primary_selection_device_manager* aPrimarySelectionDeviceManager) {
  mPrimarySelectionDeviceManagerGtk = aPrimarySelectionDeviceManager;
  mIsPrimarySelectionEnabled = !!mPrimarySelectionDeviceManagerGtk;
}

void nsWaylandDisplay::SetPrimarySelectionDeviceManager(
    zwp_primary_selection_device_manager_v1* aPrimarySelectionDeviceManager) {
  mPrimarySelectionDeviceManagerZwpV1 = aPrimarySelectionDeviceManager;
  mIsPrimarySelectionEnabled = !!mPrimarySelectionDeviceManagerZwpV1;
}

void nsWaylandDisplay::SetIdleInhibitManager(
    zwp_idle_inhibit_manager_v1* aIdleInhibitManager) {
  mIdleInhibitManager = aIdleInhibitManager;
}

void nsWaylandDisplay::SetViewporter(wp_viewporter* aViewporter) {
  mViewporter = aViewporter;
}

void nsWaylandDisplay::SetRelativePointerManager(
    zwp_relative_pointer_manager_v1* aRelativePointerManager) {
  mRelativePointerManager = aRelativePointerManager;
}

void nsWaylandDisplay::SetPointerConstraints(
    zwp_pointer_constraints_v1* aPointerConstraints) {
  mPointerConstraints = aPointerConstraints;
}

void nsWaylandDisplay::SetPointerGestures(
    zwp_pointer_gestures_v1* aPointerGestures) {
  mPointerGestures = aPointerGestures;
}

void nsWaylandDisplay::SetDmabuf(zwp_linux_dmabuf_v1* aDmabuf, int aVersion) {
  if (!aDmabuf || aVersion < ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) {
    return;
  }
  mDmabuf = aDmabuf;
  mDmabufIsFeedback =
      (aVersion >= ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION);
  if (mDmabufIsFeedback) {
    mFormats->InitFeedback(mDmabuf, nullptr);
  } else {
    mFormats->InitV3(mDmabuf);
  }
}

void nsWaylandDisplay::EnsureDMABufFormats() {
  if (mDmabuf && !mDmabufIsFeedback) {
    mFormats->InitV3Done();
  }
  mFormats->EnsureBasicFormats();
}

void nsWaylandDisplay::SetXdgActivation(xdg_activation_v1* aXdgActivation) {
  mXdgActivation = aXdgActivation;
}

void nsWaylandDisplay::SetAppMenuManager(
    org_kde_kwin_appmenu_manager* aAppMenuManager) {
  mAppMenuManager = aAppMenuManager;
}

void nsWaylandDisplay::SetFixes(wl_fixes* aFixes) { mFixes = aFixes; }

void nsWaylandDisplay::SetCMSupportedFeature(uint32_t aFeature) {
  LOG("nsWaylandDisplay::SetCMSupportedFeature() [%d]", aFeature);
  switch (aFeature) {
    case WP_COLOR_MANAGER_V1_FEATURE_ICC_V2_V4:
      mColorManagerSupportedFeature.mICC = true;
      break;
    case WP_COLOR_MANAGER_V1_FEATURE_PARAMETRIC:
      mColorManagerSupportedFeature.mParametric = true;
      break;
    case WP_COLOR_MANAGER_V1_FEATURE_SET_PRIMARIES:
      mColorManagerSupportedFeature.mPrimaries = true;
      break;
    case WP_COLOR_MANAGER_V1_FEATURE_SET_TF_POWER:
      mColorManagerSupportedFeature.mFTPower = true;
      break;
    case WP_COLOR_MANAGER_V1_FEATURE_SET_LUMINANCES:
      mColorManagerSupportedFeature.mLuminances = true;
      break;
    case WP_COLOR_MANAGER_V1_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES:
      mColorManagerSupportedFeature.mDisplayPrimaries = true;
      break;
  }
}

void nsWaylandDisplay::SetCMSupportedTFNamed(uint32_t aTF) {
  if (aTF < sColorTransfersNum) {
    LOG("nsWaylandDisplay::SetCMSupportedTFNamed() [%d]", aTF);
    mSupportedTransfer[aTF] = aTF;
  } else {
    NS_WARNING("Unknow color transfer function!");
  }
}

void nsWaylandDisplay::SetCMSupportedPrimariesNamed(uint32_t aPrimaries) {
  if (aPrimaries < sColorPrimariesNum) {
    LOG("nsWaylandDisplay::SetCMSupportedPrimariesNamed() [%u]", aPrimaries);
    mSupportedPrimaries[aPrimaries] = aPrimaries;
  } else {
    NS_WARNING("Unknown color primaries!");
  }
}

static void supported_intent(void* data,
                             struct wp_color_manager_v1* color_manager,
                             uint32_t render_intent) {}

static void supported_feature(void* data,
                              struct wp_color_manager_v1* color_manager,
                              uint32_t feature) {
  auto* display = static_cast<nsWaylandDisplay*>(data);
  display->SetCMSupportedFeature(feature);
}

static void supported_tf_named(void* data,
                               struct wp_color_manager_v1* color_manager,
                               uint32_t tf) {
  auto* display = static_cast<nsWaylandDisplay*>(data);
  display->SetCMSupportedTFNamed(tf);
}

static void supported_primaries_named(void* data,
                                      struct wp_color_manager_v1* color_manager,
                                      uint32_t primaries) {
  auto* display = static_cast<nsWaylandDisplay*>(data);
  display->SetCMSupportedPrimariesNamed(primaries);
}

static void supported_done(void* data,
                           struct wp_color_manager_v1* wp_color_manager_v1) {}

static const struct wp_color_manager_v1_listener color_manager_listener = {
    supported_intent,          supported_feature, supported_tf_named,
    supported_primaries_named, supported_done,
};

void nsWaylandDisplay::SetColorManager(wp_color_manager_v1* aColorManager) {
  mColorManager = aColorManager;
  if (mColorManager) {
    LOG("nsWaylandDisplay::SetColorManager()");
    wp_color_manager_v1_add_listener(mColorManager, &color_manager_listener,
                                     this);
  }
}

void nsWaylandDisplay::SetSupportedCoefficientsAndRanges(uint32_t aCoefficients,
                                                         uint32_t aRange) {
  if (aCoefficients < sSupportedRangesNum) {
    LOG("nsWaylandDisplay::SetSupportedCoefficientsAndRanges(): coefficients "
        "%d range %d",
        aCoefficients, aRange);
    mSupportedRanges[aCoefficients] += aRange;
  }
}

uint32_t nsWaylandDisplay::GetColorRange(uint32_t aCoefficients,
                                         bool aFullRange) {
  if (aCoefficients >= sSupportedRangesNum) {
    return 0;
  }
  auto range = mSupportedRanges[aCoefficients];
  if (aFullRange) {
    return range == sSupportedRangeBoth || range == sSupportedRangeFull
               ? WP_COLOR_REPRESENTATION_SURFACE_V1_RANGE_FULL
               : 0;
  } else {
    return range == sSupportedRangeBoth || range == sSupportedRangeLimited
               ? WP_COLOR_REPRESENTATION_SURFACE_V1_RANGE_LIMITED
               : 0;
  }
}

static void supported_alpha_mode(
    void* data,
    struct wp_color_representation_manager_v1* color_representation_manager,
    uint32_t alpha_mode) {}

static void supported_coefficients_and_ranges(
    void* data,
    struct wp_color_representation_manager_v1* color_representation_manager,
    uint32_t coefficients, uint32_t range) {
  auto* display = static_cast<nsWaylandDisplay*>(data);
  display->SetSupportedCoefficientsAndRanges(coefficients, range);
}

static void color_representation_done(
    void* data,
    struct wp_color_representation_manager_v1* color_representation_manager) {}

static const struct wp_color_representation_manager_v1_listener
    color_representation_listener = {
        supported_alpha_mode,
        supported_coefficients_and_ranges,
        color_representation_done,
};

void nsWaylandDisplay::SetColorRepresentationManager(
    wp_color_representation_manager_v1* aColorRepresentationManager) {
  mColorRepresentationManager = aColorRepresentationManager;
  if (mColorRepresentationManager) {
    LOG("nsWaylandDisplay::SetColorRepresentationManager()");
    wp_color_representation_manager_v1_add_listener(
        mColorRepresentationManager, &color_representation_listener, this);
  }
}

static void output_handle_geometry(void* data, struct wl_output* wl_output,
                                   int x, int y, int physical_width,
                                   int physical_height, int subpixel,
                                   const char* make, const char* model,
                                   int32_t transform) {
  auto* monitor = static_cast<nsWaylandDisplay::MonitorConfig*>(data);
  LOG("nsWaylandDisplay ID %d geometry position %d %d physical size %d %d "
      "subpixel %d transform %d",
      monitor->id, x, y, physical_width, physical_height, subpixel, transform);
  monitor->x = x;
  monitor->y = y;
  monitor->pendingChanges = true;
}

static void output_handle_mode(void* data, struct wl_output* wl_output,
                               uint32_t flags, int width, int height,
                               int refresh) {
  auto* monitor = static_cast<nsWaylandDisplay::MonitorConfig*>(data);
  if ((flags & WL_OUTPUT_MODE_CURRENT) == 0) {
    return;
  }
  LOG("nsWaylandDisplay ID %d mode output size %d x %d", monitor->id, width,
      height);
  monitor->pixelWidth = width;
  monitor->pixelHeight = height;
  monitor->pendingChanges = true;
}

static void output_handle_scale(void* data, struct wl_output* wl_output,
                                int32_t scale) {
  auto* monitor = static_cast<nsWaylandDisplay::MonitorConfig*>(data);
  LOG("nsWaylandDisplay ID %d Scale change [%d]", monitor->id, scale);
  monitor->pendingChanges = true;
}

static void output_handle_done(void* data, struct wl_output* wl_output) {
  auto* monitor = static_cast<nsWaylandDisplay::MonitorConfig*>(data);
  LOG("nsWaylandDisplay ID %d Done", monitor->id);
  monitor->pendingChanges = false;

  WaylandDisplayGet()->RefreshScreens();
}

static const struct wl_output_listener output_listener = {
    output_handle_geometry,
    output_handle_mode,
    output_handle_done,
    output_handle_scale,
};

void nsWaylandDisplay::AddMonitorConfig(int aId, wl_output* aWlOutput) {
  LOG("nsWaylandDisplay add monitor ID %d num %zu", aId, mMonitors.Length());
  mMonitors.AppendElement(MakeUnique<MonitorConfig>(aId, aWlOutput));
}

nsWaylandDisplay::MonitorConfig::MonitorConfig(int aId,
                                               struct wl_output* aWlOutput)
    : id(aId), wlOutput(aWlOutput) {
  LOG("MonitorConfig() add ID %d", id);
  wl_output_add_listener(wlOutput, &output_listener, this);
}

nsWaylandDisplay::MonitorConfig::~MonitorConfig() {
  LOG("~MonitorConfig() delete ID %d", id);
  MozClearPointer(wlOutput, wl_output_release);
}

bool nsWaylandDisplay::RemoveMonitorConfig(int aId) {
  for (unsigned int i = 0; i < mMonitors.Length(); i++) {
    if (mMonitors[i]->id == aId) {
      LOG("nsWaylandDisplay remove Monitor ID %d num %d", aId, i);
      mMonitors.RemoveElementAt(i);
      return true;
    }
  }
  return false;
}

nsWaylandDisplay::MonitorConfig* nsWaylandDisplay::GetMonitorConfig(int x,
                                                                    int y) {
  for (const auto& monitor : mMonitors) {
    if (monitor->x == x && monitor->y == y) {
      LOG("nsWaylandDisplay::GetMonitorConfig() %d, %d matches", x, y);
      return monitor.get();
    }
  }
  LOG("nsWaylandDisplay::GetMonitorConfig() %d, %d missing!", x, y);
  return nullptr;
}

void nsWaylandDisplay::RefreshScreens() {
  LOG("nsWaylandDisplay::RefreshScreens()");
  for (unsigned int i = 0; i < mMonitors.Length(); i++) {
    if (mMonitors[i]->pendingChanges) {
      LOG("  monitor ID %d is not complete", mMonitors[i]->id);
      return;
    }
  }
  ScreenHelperGTK::RequestRefreshScreens();
}

static void global_registry_handler(void* data, wl_registry* registry,
                                    uint32_t id, const char* interface,
                                    uint32_t version) {
  auto* display = static_cast<nsWaylandDisplay*>(data);
  if (!display) {
    return;
  }

  nsDependentCString iface(interface);
  if (iface.EqualsLiteral("wl_shm")) {
    auto* shm = WaylandRegistryBind<wl_shm>(registry, id, &wl_shm_interface, 1);
    display->SetShm(shm);
  } else if (strcmp(interface, "wl_data_device_manager") == 0) {
    int data_device_manager_version = MIN(version, 3);
    auto* data_device_manager = WaylandRegistryBind<wl_data_device_manager>(
        registry, id, &wl_data_device_manager_interface,
        data_device_manager_version);
    display->SetDataDeviceManager(data_device_manager);
  } else if (strcmp(interface, "gtk_primary_selection_device_manager") == 0) {
    auto* primary_selection_device_manager =
        WaylandRegistryBind<gtk_primary_selection_device_manager>(
            registry, id, >k_primary_selection_device_manager_interface, 1);
    display->SetPrimarySelectionDeviceManager(primary_selection_device_manager);
  } else if (strcmp(interface, "zwp_primary_selection_device_manager_v1") ==
             0) {
    auto* primary_selection_device_manager =
        WaylandRegistryBind<zwp_primary_selection_device_manager_v1>(
            registry, id, &zwp_primary_selection_device_manager_v1_interface,
            1);
    display->SetPrimarySelectionDeviceManager(primary_selection_device_manager);
  } else if (iface.EqualsLiteral("zwp_idle_inhibit_manager_v1")) {
    auto* idle_inhibit_manager =
        WaylandRegistryBind<zwp_idle_inhibit_manager_v1>(
            registry, id, &zwp_idle_inhibit_manager_v1_interface, 1);
    display->SetIdleInhibitManager(idle_inhibit_manager);
  } else if (iface.EqualsLiteral("zwp_relative_pointer_manager_v1")) {
    auto* relative_pointer_manager =
        WaylandRegistryBind<zwp_relative_pointer_manager_v1>(
            registry, id, &zwp_relative_pointer_manager_v1_interface, 1);
    display->SetRelativePointerManager(relative_pointer_manager);
  } else if (iface.EqualsLiteral("zwp_pointer_constraints_v1")) {
    auto* pointer_constraints = WaylandRegistryBind<zwp_pointer_constraints_v1>(
        registry, id, &zwp_pointer_constraints_v1_interface, 1);
    display->SetPointerConstraints(pointer_constraints);
  } else if (iface.EqualsLiteral("wl_compositor") &&
             version >= WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION) {
    auto* compositor = WaylandRegistryBind<wl_compositor>(
        registry, id, &wl_compositor_interface,
        WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION);
    display->SetCompositor(compositor);
  } else if (iface.EqualsLiteral("wl_subcompositor")) {
    auto* subcompositor = WaylandRegistryBind<wl_subcompositor>(
        registry, id, &wl_subcompositor_interface, 1);
    display->SetSubcompositor(subcompositor);
  } else if (iface.EqualsLiteral("wp_viewporter")) {
    auto* viewporter = WaylandRegistryBind<wp_viewporter>(
        registry, id, &wp_viewporter_interface, 1);
    display->SetViewporter(viewporter);
  } else if (iface.EqualsLiteral("zwp_linux_dmabuf_v1")) {
    if (version < ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) {
      return;
    }
    int vers =
        MIN(version, ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION);
    auto* dmabuf = WaylandRegistryBind<zwp_linux_dmabuf_v1>(
        registry, id, &zwp_linux_dmabuf_v1_interface, vers);
    display->SetDmabuf(dmabuf, vers);
  } else if (iface.EqualsLiteral("xx_session_manager_v1")) {
    auto* sessionManager = WaylandRegistryBind<xx_session_manager_v1>(
        registry, id, &xx_session_manager_v1_interface, 1);
    display->SetSessionManager(sessionManager);
  } else if (iface.EqualsLiteral("xdg_activation_v1")) {
    auto* activation = WaylandRegistryBind<xdg_activation_v1>(
        registry, id, &xdg_activation_v1_interface, 1);
    display->SetXdgActivation(activation);
  } else if (iface.EqualsLiteral("org_kde_kwin_appmenu_manager")) {
    auto* appMenuManager = WaylandRegistryBind<org_kde_kwin_appmenu_manager>(
        registry, id, &org_kde_kwin_appmenu_manager_interface, MIN(version, 2));
    display->SetAppMenuManager(appMenuManager);
  } else if (iface.EqualsLiteral("wl_seat") &&
             version >= WL_POINTER_RELEASE_SINCE_VERSION) {
    auto* seat = WaylandRegistryBind<wl_seat>(
        registry, id, &wl_seat_interface,
        MIN(version, WL_POINTER_AXIS_VALUE120_SINCE_VERSION));
    display->SetSeat(seat, id);
  } else if (iface.EqualsLiteral("wp_fractional_scale_manager_v1")) {
    auto* manager = WaylandRegistryBind<wp_fractional_scale_manager_v1>(
        registry, id, &wp_fractional_scale_manager_v1_interface, 1);
    display->SetFractionalScaleManager(manager);
  } else if (iface.EqualsLiteral("xx_fractional_scale_manager_v2")) {
    auto* manager = WaylandRegistryBind<xx_fractional_scale_manager_v2>(
        registry, id, &xx_fractional_scale_manager_v2_interface, 1);
    display->SetFractionalScaleManagerV2(manager);
  } else if (iface.EqualsLiteral("zwp_pointer_gestures_v1") &&
             version >=
                 ZWP_POINTER_GESTURES_V1_GET_HOLD_GESTURE_SINCE_VERSION) {
    auto* gestures = WaylandRegistryBind<zwp_pointer_gestures_v1>(
        registry, id, &zwp_pointer_gestures_v1_interface,
        ZWP_POINTER_GESTURES_V1_GET_HOLD_GESTURE_SINCE_VERSION);
    display->SetPointerGestures(gestures);
  } else if (iface.EqualsLiteral("wp_color_manager_v1")) {
    auto* colorManager = WaylandRegistryBind<wp_color_manager_v1>(
        registry, id, &wp_color_manager_v1_interface, 1);
    display->SetColorManager(colorManager);
  } else if (iface.EqualsLiteral("wp_color_representation_manager_v1")) {
    auto* colorRepresentationManager =
        WaylandRegistryBind<wp_color_representation_manager_v1>(
            registry, id, &wp_color_representation_manager_v1_interface, 1);
    display->SetColorRepresentationManager(colorRepresentationManager);
  } else if (iface.EqualsLiteral("xx_pip_shell_v1")) {
    auto* pipShell = WaylandRegistryBind<xx_pip_shell_v1>(
        registry, id, &xx_pip_shell_v1_interface, 1);
    display->SetPipShell(pipShell);
  } else if (iface.EqualsLiteral("xdg_wm_base")) {
    uint32_t vers = MIN(version, (uint32_t)xdg_wm_base_interface.version);
    auto* xdgWm = WaylandRegistryBind<xdg_wm_base>(
        registry, id, &xdg_wm_base_interface, vers);
    display->SetXdgWm(xdgWm);
  } else if (iface.EqualsLiteral("wl_output") &&
             version >= WL_OUTPUT_RELEASE_SINCE_VERSION) {
    auto* output = WaylandRegistryBind<wl_output>(
        registry, id, &wl_output_interface, WL_OUTPUT_RELEASE_SINCE_VERSION);
    display->AddMonitorConfig(id, output);
  } else if (iface.EqualsLiteral("wl_fixes")) {
    // wl_fixes_interface was introduced in libwayland-client 1.24, but
    // Ubuntu 22.04 still ships 1.20.
    // We force wl_fixes v.1 interface.
    static auto* sWlFixesInterface =
        (wl_interface*)dlsym(RTLD_DEFAULT, "wl_fixes_interface");
    if (sWlFixesInterface) {
      auto* fixes =
          WaylandRegistryBind<wl_fixes>(registry, id, sWlFixesInterface, 1);
      display->SetFixes(fixes);
    } else {
      LOG("wl_fixes_interface is missing!");
    }
  }
}

static void global_registry_remover(void* data, wl_registry* registry,
                                    uint32_t id) {
  auto* display = static_cast<nsWaylandDisplay*>(data);
  if (!display) {
    return;
  }

  if (!display->RemoveMonitorConfig(id)) {
    display->RemoveSeat(id);
  }

  if (wl_fixes* fixes = display->GetFixes()) {
    if (wl_fixes_get_version(fixes) >=
        WL_FIXES_ACK_GLOBAL_REMOVE_SINCE_VERSION) {
      wl_fixes_ack_global_remove(fixes, registry, id);
    }
  }
}

static const struct wl_registry_listener registry_listener = {
    global_registry_handler, global_registry_remover};

nsWaylandDisplay::~nsWaylandDisplay() {
  g_list_free_full(mAsyncRoundtrips, (GDestroyNotify)wl_callback_destroy);
  MozClearPointer(mColorManager, wp_color_manager_v1_destroy);
  MozClearPointer(mColorRepresentationManager,
                  wp_color_representation_manager_v1_destroy);
  MozClearPointer(mWaylandSession, xx_session_v1_destroy);
  MozClearPointer(mSessionManager, xx_session_manager_v1_destroy);
}

void nsWaylandDisplay::AsyncRoundtripCallback(void* aData,
                                              wl_callback* aCallback,
                                              uint32_t aTime) {
  auto* display = static_cast<nsWaylandDisplay*>(aData);
  display->mAsyncRoundtrips =
      g_list_remove(display->mAsyncRoundtrips, aCallback);
  wl_callback_destroy(aCallback);
}

static const struct wl_callback_listener async_roundtrip_listener = {
    nsWaylandDisplay::AsyncRoundtripCallback};

void nsWaylandDisplay::RequestAsyncRoundtrip() {
  LOG("nsWaylandDisplay::RequestAsyncRoundtrip()");
  wl_callback* callback = wl_display_sync(mDisplay);
  wl_callback_add_listener(callback, &async_roundtrip_listener, this);
  mAsyncRoundtrips = g_list_append(mAsyncRoundtrips, callback);
  wl_display_flush(mDisplay);
}

void nsWaylandDisplay::WaitForAsyncRoundtrips() {
  LOG("nsWaylandDisplay::WaitForAsyncRoundtrips()");
  while (g_list_length(mAsyncRoundtrips) > 0) {
    if (wl_display_dispatch(mDisplay) < 0) {
      NS_WARNING("Failed to get events from Wayland display!");
      return;
    }
  }
}

void nsWaylandDisplay::RequestRoundtrip() {
  LOG("nsWaylandDisplay::RequestRoundtrip()");
  wl_display_roundtrip(mDisplay);
}

void nsWaylandDisplay::SessionCreate(void* aData, xx_session_v1* aSession,
                                     const char* aSessionId) {
  LOG("nsWaylandDisplay::SessionCreate() %s", aSessionId);

  auto* display = static_cast<nsWaylandDisplay*>(aData);
  display->mWaylandSessionId = aSessionId;
  Preferences::SetCString("widget.wayland.session-id", aSessionId);
}

void nsWaylandDisplay::SessionRestore(void* aData, xx_session_v1* aSession) {
#ifdef MOZ_LOGGING
  auto* display = static_cast<nsWaylandDisplay*>(aData);
  LOG("nsWaylandDisplay::SessionRestore() %s",
      display->mWaylandSessionId.get());
#endif
}

void nsWaylandDisplay::SessionReplace(void* aData, xx_session_v1* aSession) {
  LOG("nsWaylandDisplay::SessionReplace()");
  auto* display = static_cast<nsWaylandDisplay*>(aData);
  display->CreateSession();
}

static const xx_session_v1_listener sSessionListener = {
    nsWaylandDisplay::SessionCreate,
    nsWaylandDisplay::SessionRestore,
    nsWaylandDisplay::SessionReplace,
};

void nsWaylandDisplay::CreateSession(const char* aSessionId) {
  LOG("nsWaylandDisplay::CreateSession() ID %s", aSessionId);

  // TODO: WUniquePtr
  MozClearPointer(mWaylandSession, xx_session_v1_destroy);

  mWaylandSession = xx_session_manager_v1_get_session(
      mSessionManager,
      aSessionId ? XX_SESSION_MANAGER_V1_REASON_SESSION_RESTORE
                 : XX_SESSION_MANAGER_V1_REASON_LAUNCH,
      aSessionId);

  if (!mWaylandSession) {
    LOG("  failed to create session %s", aSessionId);
    return;
  }

  xx_session_v1_add_listener(mWaylandSession, &sSessionListener, this);
}

void nsWaylandDisplay::SetSessionManager(
    xx_session_manager_v1* aSessionManager) {
  mSessionManager = aSessionManager;
  LOG("nsWaylandDisplay::SetSessionManager()");

  nsAutoCString prevSessionId;
  Preferences::GetCString("widget.wayland.session-id", prevSessionId);

  CreateSession(prevSessionId.IsEmpty() ? nullptr : prevSessionId.get());
}

// Separate crash functions for different Wayland protocol error patterns.
// These functions are marked MOZ_NEVER_INLINE to ensure distinct crash
// signatures for different error types, making them easier to track and fix.
//
// Pattern analysis is based on 25 recent crash reports with the
// mozilla::widget::WlLogHandler signature. Frequencies are noted in comments
// below.

// 32% of crashes - Example: "unknown object (4278190083), message error(ous)"
MOZ_NEVER_INLINE static void WlLogHandler_UnknownObject(const char* error) {
  MOZ_CRASH_UNSAFE_PRINTF("(%s) %s Proxy: %s",
                          GetDesktopEnvironmentIdentifier().get(), error,
                          WaylandProxy::GetState());
}

// 20% of crashes - Example: "wp_viewport#296: error 2: source rectangle out
// of buffer bounds"
MOZ_NEVER_INLINE static void WlLogHandler_ViewportBufferBounds(
    const char* error) {
  MOZ_CRASH_UNSAFE_PRINTF("(%s) %s Proxy: %s",
                          GetDesktopEnvironmentIdentifier().get(), error,
                          WaylandProxy::GetState());
}

// 20% of crashes - Example: "wl_display#1: error 1: invalid arguments for
// wl_surface#730.frame"
MOZ_NEVER_INLINE static void WlLogHandler_InvalidSurfaceFrame(
    const char* error) {
  MOZ_CRASH_UNSAFE_PRINTF("(%s) %s Proxy: %s",
                          GetDesktopEnvironmentIdentifier().get(), error,
                          WaylandProxy::GetState());
}

// 12% of crashes - Example: "wp_color_manager_v1#44: error -1: Invalid output
// (2)"
MOZ_NEVER_INLINE static void WlLogHandler_ColorManagerInvalidOutput(
    const char* error) {
  MOZ_CRASH_UNSAFE_PRINTF("(%s) %s Proxy: %s",
                          GetDesktopEnvironmentIdentifier().get(), error,
                          WaylandProxy::GetState());
}

// 8% of crashes - Example: "Message length 22222 exceeds limit 4096"
MOZ_NEVER_INLINE static void WlLogHandler_MessageLengthExceeded(
    const char* error) {
  MOZ_CRASH_UNSAFE_PRINTF("(%s) %s Proxy: %s",
                          GetDesktopEnvironmentIdentifier().get(), error,
                          WaylandProxy::GetState());
}

// 4% of crashes - Example: "wl_display#1: error 0: invalid object 61246"
MOZ_NEVER_INLINE static void WlLogHandler_InvalidDisplayObject(
    const char* error) {
  MOZ_CRASH_UNSAFE_PRINTF("(%s) %s Proxy: %s",
                          GetDesktopEnvironmentIdentifier().get(), error,
                          WaylandProxy::GetState());
}

// 4% of crashes - Example: "failed to import supplied dmabufs: EGL failed to
// allocate resources"
MOZ_NEVER_INLINE static void WlLogHandler_DmabufImportFailed(
    const char* error) {
  MOZ_CRASH_UNSAFE_PRINTF("(%s) %s Proxy: %s",
                          GetDesktopEnvironmentIdentifier().get(), error,
                          WaylandProxy::GetState());
}

// 4% of crashes - Example: "wl_registry@36: error 0: invalid global
// wl_output (55)"
MOZ_NEVER_INLINE static void WlLogHandler_RegistryError(const char* error) {
  MOZ_CRASH_UNSAFE_PRINTF("(%s) %s Proxy: %s",
                          GetDesktopEnvironmentIdentifier().get(), error,
                          WaylandProxy::GetState());
}

// 4% of crashes - Example: "invalid object (277), type (wl_callback), message
// begin(3uuou)"
MOZ_NEVER_INLINE static void WlLogHandler_CallbackInvalid(const char* error) {
  MOZ_CRASH_UNSAFE_PRINTF("(%s) %s Proxy: %s",
                          GetDesktopEnvironmentIdentifier().get(), error,
                          WaylandProxy::GetState());
}

// 4% of crashes - Example: "error marshalling arguments for set_surface
// (signature o): null value passed for arg 0"
MOZ_NEVER_INLINE static void WlLogHandler_MarshallingError(const char* error) {
  MOZ_CRASH_UNSAFE_PRINTF("(%s) %s Proxy: %s",
                          GetDesktopEnvironmentIdentifier().get(), error,
                          WaylandProxy::GetState());
}

// xdg_surface buffer mismatch - Example: "dg_wm_base@17: error 4: xdg_surface
// buffer (1 x 1) is larger than the configured fullscreen state (0 x 0)"
MOZ_NEVER_INLINE static void WlLogHandler_XdgSurfaceBufferMismatch(
    const char* error) {
  MOZ_CRASH_UNSAFE_PRINTF("(%s) %s Proxy: %s",
                          GetDesktopEnvironmentIdentifier().get(), error,
                          WaylandProxy::GetState());
}

// Timestamp of the last "still attached" message ignored by WlLogHandler.
// Written on the main thread (libwayland event dispatch) with release ordering
// after writing sStillAttachedMessage, so any thread that observes the
// timestamp with acquire ordering is guaranteed to see the message too.
static std::atomic<clock_t> sStillAttachedTime{0};
static char sStillAttachedMessage[128];

static void WlLogHandler(const char* format, va_list args) {
  char error[1000];
  VsprintfLiteral(error, format, args);
  gfxCriticalNote << "(" << GetDesktopEnvironmentIdentifier().get()
                  << ") Wayland protocol error: " << error;

  // See Bug 1826583 and Bug 1844653 for reference.
  // "warning: queue %p destroyed while proxies still attached" and variants
  // like "zwp_linux_dmabuf_feedback_v1@%d still attached" are exceptions on
  // Wayland and non-fatal. They are triggered in certain versions of Mesa or
  // the proprietary Nvidia driver and we don't want to crash because of them.
  // Store the message and timestamp so ProcessFailure() can correlate this
  // event with a subsequent silent compositor disconnect.
  if (strstr(error, "still attached")) {
    // Sentinel: ensures a concurrent reader on the proxy thread never scans
    // past the end of the array if a longer new string overwrites the old
    // null terminator before placing its own.
    sStillAttachedMessage[sizeof(sStillAttachedMessage) - 1] = '\0';
    SprintfLiteral(sStillAttachedMessage, "%s", error);
    sStillAttachedTime.store(clock(), std::memory_order_release);
    return;
  }

  // Pattern matching to dispatch to specific crash functions.
  // Patterns ordered by frequency (most common first) for performance.

  // Pattern 1: Unknown object errors (32% of crashes)
  if (strstr(error, "unknown object") && strstr(error, "message error")) {
    WlLogHandler_UnknownObject(error);
  }

  // Pattern 2: wp_viewport buffer bounds errors (20% of crashes)
  if (strstr(error, "wp_viewport") &&
      strstr(error, "source rectangle out of buffer bounds")) {
    WlLogHandler_ViewportBufferBounds(error);
  }

  // Pattern 3: Invalid wl_surface.frame arguments (20% of crashes)
  if (strstr(error, "wl_display") && strstr(error, "invalid arguments") &&
      strstr(error, "wl_surface") && strstr(error, ".frame")) {
    WlLogHandler_InvalidSurfaceFrame(error);
  }

  // Pattern 4: wp_color_manager_v1 invalid output (12% of crashes)
  if (strstr(error, "wp_color_manager_v1") && strstr(error, "Invalid output")) {
    WlLogHandler_ColorManagerInvalidOutput(error);
  }

  // Pattern 5: Message length exceeded (8% of crashes)
  if ((strstr(error, "message length") || strstr(error, "Message length")) &&
      strstr(error, "exceeds")) {
    WlLogHandler_MessageLengthExceeded(error);
  }

  // Pattern 6: Invalid wl_display object (4% of crashes)
  if (strstr(error, "wl_display") && strstr(error, "invalid object") &&
      !strstr(error, "unknown object")) {
    WlLogHandler_InvalidDisplayObject(error);
  }

  // Pattern 7: dmabuf import failures (4% of crashes)
  if (strstr(error, "dmabuf") && strstr(error, "failed") &&
      strstr(error, "EGL")) {
    WlLogHandler_DmabufImportFailed(error);
  }

  // Pattern 8: wl_registry errors (4% of crashes)
  if (strstr(error, "wl_registry") && strstr(error, "wl_output")) {
    WlLogHandler_RegistryError(error);
  }

  // Pattern 9: wl_callback invalid (4% of crashes)
  if (strstr(error, "wl_callback") && strstr(error, "invalid object")) {
    WlLogHandler_CallbackInvalid(error);
  }

  // Pattern 10: Marshalling errors (4% of crashes)
  if (strstr(error, "error marshalling arguments")) {
    WlLogHandler_MarshallingError(error);
  }

  // Pattern 11: xdg_surface buffer mismatch with fullscreen state
  if (strstr(error, "xdg_surface") && strstr(error, "buffer") &&
      strstr(error, "fullscreen state")) {
    WlLogHandler_XdgSurfaceBufferMismatch(error);
  }

  // Fallback for unmatched patterns - use original inline code
  MOZ_CRASH_UNSAFE_PRINTF("(%s) %s Proxy: %s",
                          GetDesktopEnvironmentIdentifier().get(), error,
                          WaylandProxy::GetState());
}

void WlCompositorUnavailableHandler() {
  gfxCriticalNote << "Wayland compositor unavailable ("
                  << GetDesktopEnvironmentIdentifier().get()
                  << "), proxy: " << WaylandProxy::GetState()
                  << " - scheduling graceful shutdown";
  // Called from the WaylandProxy thread. Dispatch to the main thread to
  // trigger a clean Firefox shutdown instead of crashing.
  NS_DispatchToMainThread(
      NS_NewRunnableFunction("WlCompositorUnavailableHandler", []() {
        nsCOMPtr<nsIAppStartup> appStartup =
            do_GetService("@mozilla.org/toolkit/app-startup;1");
        if (appStartup) {
          bool userAllowedQuit = true;
          appStartup->Quit(nsIAppStartup::eForceQuit, 0, &userAllowedQuit);
        }
      }));
}

MOZ_NEVER_INLINE static void WlLogHandler_StillAttachedDisconnect(
    const char* error) {
  MOZ_CRASH_UNSAFE_PRINTF("(%s) %s Proxy: %s",
                          GetDesktopEnvironmentIdentifier().get(), error,
                          WaylandProxy::GetState());
}

void WlCompositorSilentDisconnectHandler(clock_t aFailureTime) {
  clock_t t = sStillAttachedTime.load(std::memory_order_acquire);
  if (t <= aFailureTime) {
    return;  // no still-attached event in this failure window
  }
  nsCString reason(sStillAttachedMessage);
  NS_DispatchToMainThread(NS_NewRunnableFunction(
      "WlCompositorSilentDisconnectHandler", [reason = std::move(reason)]() {
        WlLogHandler_StillAttachedDisconnect(reason.get());
      }));
}

nsWaylandDisplay::nsWaylandDisplay(wl_display* aDisplay)
    : mThreadId(PR_GetCurrentThread()), mDisplay(aDisplay) {
  MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
  for (auto& e : mSupportedTransfer) {
    e = -1;
  };
  for (auto& e : mSupportedPrimaries) {
    e = -1;
  };
}

void nsWaylandDisplay::Init() {
  // GTK sets the log handler on display creation, thus we overwrite it here
  // in a similar fashion
  wl_log_set_handler_client(WlLogHandler);

  LOG("nsWaylandDisplay::Init()");

  mFormats = new DMABufFormats();
  mRegistry = wl_display_get_registry(mDisplay);
  wl_registry_add_listener(mRegistry, ®istry_listener, this);
  wl_display_roundtrip(mDisplay);
  RequestAsyncRoundtrip();
  WaitForAsyncRoundtrips();
  EnsureDMABufFormats();

  LOG("  init finished");

  // Check we have critical Wayland interfaces.
  // Missing ones indicates a compositor bug/missing feature and
  // we can't continue.
  MOZ_RELEASE_ASSERT(GetShm(), "We're missing shm interface!");
  MOZ_RELEASE_ASSERT(GetCompositor(), "We're missing compositor interface!");
  MOZ_RELEASE_ASSERT(GetSubcompositor(),
                     "We're missing subcompositor interface!");
  if (!GetViewporter()) {
    NS_WARNING("Missing viewporter wayland protocol!");
  }
  if (!GetFractionalScaleManager()) {
    NS_WARNING("Missing wp_fractional_scale_v1 wayland protocol!");
  }
}

}  // namespace mozilla::widget

Messung V0.5 in Prozent
C=94 H=99 G=96

¤ Dauer der Verarbeitung: 0.19 Sekunden  (vorverarbeitet am  2026-09-29) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

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.