Bug Summary

File:var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp
Warning:line 2138, column 14
Value stored to 'tmp' during its initialization is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name nsWindow.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/widget/gtk -fcoverage-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/widget/gtk -resource-dir /usr/lib/llvm-18/lib/clang/18 -include /var/lib/jenkins/workspace/firefox-scan-build/config/gcc_hidden.h -include /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D CAIRO_GFX -D MOZ_APP_NAME="firefox" -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D STATIC_EXPORTABLE_JS_API -I /var/lib/jenkins/workspace/firefox-scan-build/widget/gtk -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/widget/gtk -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/ipc/ipdl/_ipdlheaders -I /var/lib/jenkins/workspace/firefox-scan-build/ipc/chromium/src -I /var/lib/jenkins/workspace/firefox-scan-build/layout/base -I /var/lib/jenkins/workspace/firefox-scan-build/layout/forms -I /var/lib/jenkins/workspace/firefox-scan-build/layout/generic -I /var/lib/jenkins/workspace/firefox-scan-build/layout/xul -I /var/lib/jenkins/workspace/firefox-scan-build/other-licenses/atk-1.0 -I /var/lib/jenkins/workspace/firefox-scan-build/third_party/cups/include -I /var/lib/jenkins/workspace/firefox-scan-build/widget -I /var/lib/jenkins/workspace/firefox-scan-build/widget/headless -I /var/lib/jenkins/workspace/firefox-scan-build/widget/x11 -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -I /usr/include/gtk-3.0 -I /usr/include/pango-1.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/x86_64-linux-gnu -I /usr/include/webp -I /usr/include/gio-unix-2.0 -I /usr/include/cloudproviders -I /usr/include/atk-1.0 -I /usr/include/at-spi2-atk/2.0 -I /usr/include/at-spi-2.0 -I /usr/include/dbus-1.0 -I /usr/lib/x86_64-linux-gnu/dbus-1.0/include -I /usr/include/gtk-3.0/unix-print -I /usr/include/gtk-3.0 -I /usr/include/pango-1.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/x86_64-linux-gnu -I /usr/include/webp -I /usr/include/gio-unix-2.0 -I /usr/include/cloudproviders -I /usr/include/atk-1.0 -I /usr/include/at-spi2-atk/2.0 -I /usr/include/at-spi-2.0 -I /usr/include/dbus-1.0 -I /usr/lib/x86_64-linux-gnu/dbus-1.0/include -I /usr/include/dbus-1.0 -I /usr/lib/x86_64-linux-gnu/dbus-1.0/include -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/x86_64-linux-gnu/c++/13 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/backward -internal-isystem /usr/lib/llvm-18/lib/clang/18/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-deprecated-this-capture -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -fdeprecated-macro -ferror-limit 19 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-aligned-allocation -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2024-05-16-034744-15991-1 -x c++ /var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp
1/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2/* vim:expandtab:shiftwidth=2:tabstop=2:
3 */
4/* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7
8#include "nsWindow.h"
9
10#include <algorithm>
11#include <cstdint>
12#include <dlfcn.h>
13#include <gdk/gdkkeysyms.h>
14#include <wchar.h>
15
16#include "VsyncSource.h"
17#include "gfx2DGlue.h"
18#include "gfxContext.h"
19#include "gfxImageSurface.h"
20#include "gfxPlatformGtk.h"
21#include "gfxUtils.h"
22#include "GLContextProvider.h"
23#include "GLContext.h"
24#include "GtkCompositorWidget.h"
25#include "gtkdrawing.h"
26#include "imgIContainer.h"
27#include "InputData.h"
28#include "mozilla/ArrayUtils.h"
29#include "mozilla/Assertions.h"
30#include "mozilla/Components.h"
31#include "mozilla/GRefPtr.h"
32#include "mozilla/dom/Document.h"
33#include "mozilla/dom/WheelEventBinding.h"
34#include "mozilla/gfx/2D.h"
35#include "mozilla/gfx/gfxVars.h"
36#include "mozilla/gfx/GPUProcessManager.h"
37#include "mozilla/gfx/HelpersCairo.h"
38#include "mozilla/layers/APZThreadUtils.h"
39#include "mozilla/layers/LayersTypes.h"
40#include "mozilla/layers/CompositorBridgeChild.h"
41#include "mozilla/layers/CompositorBridgeParent.h"
42#include "mozilla/layers/CompositorThread.h"
43#include "mozilla/layers/KnowsCompositor.h"
44#include "mozilla/layers/WebRenderBridgeChild.h"
45#include "mozilla/layers/WebRenderLayerManager.h"
46#include "mozilla/layers/APZInputBridge.h"
47#include "mozilla/layers/IAPZCTreeManager.h"
48#include "mozilla/Likely.h"
49#include "mozilla/MiscEvents.h"
50#include "mozilla/MouseEvents.h"
51#include "mozilla/NativeKeyBindingsType.h"
52#include "mozilla/Preferences.h"
53#include "mozilla/PresShell.h"
54#include "mozilla/ProfilerLabels.h"
55#include "mozilla/ScopeExit.h"
56#include "mozilla/StaticPrefs_apz.h"
57#include "mozilla/StaticPrefs_layout.h"
58#include "mozilla/StaticPrefs_mozilla.h"
59#include "mozilla/StaticPrefs_ui.h"
60#include "mozilla/StaticPrefs_widget.h"
61#include "mozilla/SwipeTracker.h"
62#include "mozilla/TextEventDispatcher.h"
63#include "mozilla/TextEvents.h"
64#include "mozilla/TimeStamp.h"
65#include "mozilla/UniquePtrExtensions.h"
66#include "mozilla/WidgetUtils.h"
67#include "mozilla/WritingModes.h"
68#ifdef MOZ_X111
69# include "mozilla/X11Util.h"
70#endif
71#include "mozilla/XREAppData.h"
72#include "NativeKeyBindings.h"
73#include "nsAppDirectoryServiceDefs.h"
74#include "nsAppRunner.h"
75#include "nsDragService.h"
76#include "nsGTKToolkit.h"
77#include "nsGtkKeyUtils.h"
78#include "nsGtkCursors.h"
79#include "nsGfxCIID.h"
80#include "nsGtkUtils.h"
81#include "nsIFile.h"
82#include "nsIGSettingsService.h"
83#include "nsIInterfaceRequestorUtils.h"
84#include "nsImageToPixbuf.h"
85#include "nsINode.h"
86#include "nsIRollupListener.h"
87#include "nsIScreenManager.h"
88#include "nsIUserIdleServiceInternal.h"
89#include "nsIWidgetListener.h"
90#include "nsLayoutUtils.h"
91#include "nsMenuPopupFrame.h"
92#include "nsPresContext.h"
93#include "nsShmImage.h"
94#include "nsString.h"
95#include "nsWidgetsCID.h"
96#include "nsViewManager.h"
97#include "nsXPLookAndFeel.h"
98#include "prlink.h"
99#include "Screen.h"
100#include "ScreenHelperGTK.h"
101#include "SystemTimeConverter.h"
102#include "WidgetUtilsGtk.h"
103#include "NativeMenuGtk.h"
104
105#ifdef ACCESSIBILITY1
106# include "mozilla/a11y/LocalAccessible.h"
107# include "mozilla/a11y/Platform.h"
108# include "nsAccessibilityService.h"
109#endif
110
111#ifdef MOZ_X111
112# include <gdk/gdkkeysyms-compat.h>
113# include <X11/Xatom.h>
114# include <X11/extensions/XShm.h>
115# include <X11/extensions/shape.h>
116# include "gfxXlibSurface.h"
117# include "GLContextGLX.h" // for GLContextGLX::FindVisual()
118# include "GLContextEGL.h" // for GLContextEGL::FindVisual()
119# include "WindowSurfaceX11Image.h"
120# include "WindowSurfaceX11SHM.h"
121#endif
122#ifdef MOZ_WAYLAND1
123# include <gdk/gdkkeysyms-compat.h>
124# include "nsIClipboard.h"
125# include "nsView.h"
126#endif
127
128using namespace mozilla;
129using namespace mozilla::gfx;
130using namespace mozilla::layers;
131using namespace mozilla::widget;
132#ifdef MOZ_X111
133using mozilla::gl::GLContextEGL;
134using mozilla::gl::GLContextGLX;
135#endif
136
137// Don't put more than this many rects in the dirty region, just fluff
138// out to the bounding-box if there are more
139#define MAX_RECTS_IN_REGION100 100
140
141#if !GTK_CHECK_VERSION(3, 18, 0)((3) > (3) || ((3) == (3) && (24) > (18)) || ((
3) == (3) && (24) == (18) && (41) >= (0)))
142
143struct _GdkEventTouchpadPinch {
144 GdkEventType type;
145 GdkWindow* window;
146 gint8 send_event;
147 gint8 phase;
148 gint8 n_fingers;
149 guint32 time;
150 gdouble x;
151 gdouble y;
152 gdouble dx;
153 gdouble dy;
154 gdouble angle_delta;
155 gdouble scale;
156 gdouble x_root, y_root;
157 guint state;
158};
159
160typedef enum {
161 GDK_TOUCHPAD_GESTURE_PHASE_BEGIN,
162 GDK_TOUCHPAD_GESTURE_PHASE_UPDATE,
163 GDK_TOUCHPAD_GESTURE_PHASE_END,
164 GDK_TOUCHPAD_GESTURE_PHASE_CANCEL
165} GdkTouchpadGesturePhase;
166
167gint GDK_TOUCHPAD_GESTURE_MASK = 1 << 24;
168GdkEventType GDK_TOUCHPAD_PINCH = static_cast<GdkEventType>(42);
169
170#endif
171
172const gint kEvents = GDK_TOUCHPAD_GESTURE_MASK | GDK_EXPOSURE_MASK |
173 GDK_STRUCTURE_MASK | GDK_VISIBILITY_NOTIFY_MASK |
174 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
175 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
176 GDK_SMOOTH_SCROLL_MASK | GDK_TOUCH_MASK | GDK_SCROLL_MASK |
177 GDK_POINTER_MOTION_MASK | GDK_PROPERTY_CHANGE_MASK;
178
179/* utility functions */
180static bool is_mouse_in_window(GdkWindow* aWindow, gdouble aMouseX,
181 gdouble aMouseY);
182static nsWindow* get_window_for_gtk_widget(GtkWidget* widget);
183static nsWindow* get_window_for_gdk_window(GdkWindow* window);
184static GtkWidget* get_gtk_widget_for_gdk_window(GdkWindow* window);
185static GdkCursor* get_gtk_cursor(nsCursor aCursor);
186
187/* callbacks from widgets */
188static gboolean expose_event_cb(GtkWidget* widget, cairo_t* cr);
189static gboolean configure_event_cb(GtkWidget* widget, GdkEventConfigure* event);
190static void size_allocate_cb(GtkWidget* widget, GtkAllocation* allocation);
191static void toplevel_window_size_allocate_cb(GtkWidget* widget,
192 GtkAllocation* allocation);
193static gboolean delete_event_cb(GtkWidget* widget, GdkEventAny* event);
194static gboolean enter_notify_event_cb(GtkWidget* widget,
195 GdkEventCrossing* event);
196static gboolean leave_notify_event_cb(GtkWidget* widget,
197 GdkEventCrossing* event);
198static gboolean motion_notify_event_cb(GtkWidget* widget,
199 GdkEventMotion* event);
200MOZ_CAN_RUN_SCRIPT static gboolean button_press_event_cb(GtkWidget* widget,
201 GdkEventButton* event);
202static gboolean button_release_event_cb(GtkWidget* widget,
203 GdkEventButton* event);
204static gboolean focus_in_event_cb(GtkWidget* widget, GdkEventFocus* event);
205static gboolean focus_out_event_cb(GtkWidget* widget, GdkEventFocus* event);
206static gboolean key_press_event_cb(GtkWidget* widget, GdkEventKey* event);
207static gboolean key_release_event_cb(GtkWidget* widget, GdkEventKey* event);
208static gboolean property_notify_event_cb(GtkWidget* widget,
209 GdkEventProperty* event);
210static gboolean scroll_event_cb(GtkWidget* widget, GdkEventScroll* event);
211static gboolean visibility_notify_event_cb(GtkWidget* widget,
212 GdkEventVisibility* event);
213static void hierarchy_changed_cb(GtkWidget* widget,
214 GtkWidget* previous_toplevel);
215static gboolean window_state_event_cb(GtkWidget* widget,
216 GdkEventWindowState* event);
217static void settings_xft_dpi_changed_cb(GtkSettings* settings,
218 GParamSpec* pspec, nsWindow* data);
219static void check_resize_cb(GtkContainer* container, gpointer user_data);
220static void screen_composited_changed_cb(GdkScreen* screen, gpointer user_data);
221static void widget_composited_changed_cb(GtkWidget* widget, gpointer user_data);
222
223static void scale_changed_cb(GtkWidget* widget, GParamSpec* aPSpec,
224 gpointer aPointer);
225static gboolean touch_event_cb(GtkWidget* aWidget, GdkEventTouch* aEvent);
226static gboolean generic_event_cb(GtkWidget* widget, GdkEvent* aEvent);
227
228static nsWindow* GetFirstNSWindowForGDKWindow(GdkWindow* aGdkWindow);
229
230#ifdef __cplusplus201703L
231extern "C" {
232#endif /* __cplusplus */
233#ifdef MOZ_X111
234static GdkFilterReturn popup_take_focus_filter(GdkXEvent* gdk_xevent,
235 GdkEvent* event, gpointer data);
236#endif /* MOZ_X11 */
237#ifdef __cplusplus201703L
238}
239#endif /* __cplusplus */
240
241static gboolean drag_motion_event_cb(GtkWidget* aWidget,
242 GdkDragContext* aDragContext, gint aX,
243 gint aY, guint aTime, gpointer aData);
244static void drag_leave_event_cb(GtkWidget* aWidget,
245 GdkDragContext* aDragContext, guint aTime,
246 gpointer aData);
247static gboolean drag_drop_event_cb(GtkWidget* aWidget,
248 GdkDragContext* aDragContext, gint aX,
249 gint aY, guint aTime, gpointer aData);
250static void drag_data_received_event_cb(GtkWidget* aWidget,
251 GdkDragContext* aDragContext, gint aX,
252 gint aY,
253 GtkSelectionData* aSelectionData,
254 guint aInfo, guint32 aTime,
255 gpointer aData);
256
257/* initialization static functions */
258static nsresult initialize_prefs(void);
259
260static guint32 sLastUserInputTime = GDK_CURRENT_TIME0L;
261
262static SystemTimeConverter<guint32>& TimeConverter() {
263 static SystemTimeConverter<guint32> sTimeConverterSingleton;
264 return sTimeConverterSingleton;
265}
266
267bool nsWindow::sTransparentMainWindow = false;
268
269// forward declare from mozgtk
270extern "C" MOZ_EXPORT__attribute__((visibility("default"))) void mozgtk_linker_holder();
271
272namespace mozilla {
273
274#ifdef MOZ_X111
275class CurrentX11TimeGetter {
276 public:
277 explicit CurrentX11TimeGetter(GdkWindow* aWindow) : mWindow(aWindow) {}
278
279 guint32 GetCurrentTime() const { return gdk_x11_get_server_time(mWindow); }
280
281 void GetTimeAsyncForPossibleBackwardsSkew(const TimeStamp& aNow) {
282 // Check for in-flight request
283 if (!mAsyncUpdateStart.IsNull()) {
284 return;
285 }
286 mAsyncUpdateStart = aNow;
287
288 Display* xDisplay = GDK_WINDOW_XDISPLAY(mWindow)((gdk_x11_display_get_xdisplay (gdk_window_get_display (mWindow
))))
;
289 Window xWindow = GDK_WINDOW_XID(mWindow)(gdk_x11_window_get_xid (mWindow));
290 unsigned char c = 'a';
291 Atom timeStampPropAtom = TimeStampPropAtom();
292 XChangeProperty(xDisplay, xWindow, timeStampPropAtom, timeStampPropAtom, 8,
293 PropModeReplace0, &c, 1);
294 XFlush(xDisplay);
295 }
296
297 gboolean PropertyNotifyHandler(GtkWidget* aWidget, GdkEventProperty* aEvent) {
298 if (aEvent->atom != gdk_x11_xatom_to_atom(TimeStampPropAtom())) {
299 return FALSE(0);
300 }
301
302 guint32 eventTime = aEvent->time;
303 TimeStamp lowerBound = mAsyncUpdateStart;
304
305 TimeConverter().CompensateForBackwardsSkew(eventTime, lowerBound);
306 mAsyncUpdateStart = TimeStamp();
307 return TRUE(!(0));
308 }
309
310 private:
311 static Atom TimeStampPropAtom() {
312 return gdk_x11_get_xatom_by_name_for_display(gdk_display_get_default(),
313 "GDK_TIMESTAMP_PROP");
314 }
315
316 // This is safe because this class is stored as a member of mWindow and
317 // won't outlive it.
318 GdkWindow* mWindow;
319 TimeStamp mAsyncUpdateStart;
320};
321#endif
322
323} // namespace mozilla
324
325// The window from which the focus manager asks us to dispatch key events.
326static nsWindow* gFocusWindow = nullptr;
327static bool gBlockActivateEvent = false;
328static bool gGlobalsInitialized = false;
329static bool gUseAspectRatio = true;
330static uint32_t gLastTouchID = 0;
331// See Bug 1777269 for details. We don't know if the suspected leave notify
332// event is a correct one when we get it.
333// Store it and issue it later from enter notify event if it's correct,
334// throw it away otherwise.
335static GUniquePtr<GdkEventCrossing> sStoredLeaveNotifyEvent;
336
337#define NS_WINDOW_TITLE_MAX_LENGTH4095 4095
338
339// cursor cache
340static GdkCursor* gCursorCache[eCursorCount];
341
342// Sometimes this actually also includes the state of the modifier keys, but
343// only the button state bits are used.
344static guint gButtonState;
345
346static inline int32_t GetBitmapStride(int32_t width) {
347#if defined(MOZ_X111)
348 return (width + 7) / 8;
349#else
350 return cairo_format_stride_for_width(CAIRO_FORMAT_A1, width);
351#endif
352}
353
354static inline bool TimestampIsNewerThan(guint32 a, guint32 b) {
355 // Timestamps are just the least significant bits of a monotonically
356 // increasing function, and so the use of unsigned overflow arithmetic.
357 return a - b <= G_MAXUINT32((guint32) 0xffffffff) / 2;
358}
359
360static void UpdateLastInputEventTime(void* aGdkEvent) {
361 nsCOMPtr<nsIUserIdleServiceInternal> idleService =
362 do_GetService("@mozilla.org/widget/useridleservice;1");
363 if (idleService) {
364 idleService->ResetIdleTimeOut(0);
365 }
366
367 guint timestamp = gdk_event_get_time(static_cast<GdkEvent*>(aGdkEvent));
368 if (timestamp == GDK_CURRENT_TIME0L) {
369 return;
370 }
371
372 sLastUserInputTime = timestamp;
373}
374
375// Don't set parent (transient for) if nothing changes.
376// gtk_window_set_transient_for() blows up wl_subsurfaces used by aWindow
377// even if aParent is the same.
378static void GtkWindowSetTransientFor(GtkWindow* aWindow, GtkWindow* aParent) {
379 GtkWindow* parent = gtk_window_get_transient_for(aWindow);
380 if (parent != aParent) {
381 gtk_window_set_transient_for(aWindow, aParent){ do { static_assert( mozilla::detail::AssertionConditionType
<decltype(false)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"gtk_window_set_transient_for() can't be used directly." ")"
, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 381); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "gtk_window_set_transient_for() can't be used directly."
")"); do { *((volatile int*)__null) = 381; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); }
;
382 }
383}
384
385#define gtk_window_set_transient_for(a, b){ do { static_assert( mozilla::detail::AssertionConditionType
<decltype(false)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"gtk_window_set_transient_for() can't be used directly." ")"
, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 385); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "gtk_window_set_transient_for() can't be used directly."
")"); do { *((volatile int*)__null) = 385; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); }
\
386 { \
387 MOZ_ASSERT_UNREACHABLE( \do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"gtk_window_set_transient_for() can't be used directly." ")"
, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 388); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "gtk_window_set_transient_for() can't be used directly."
")"); do { *((volatile int*)__null) = 388; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
388 "gtk_window_set_transient_for() can't be used directly.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"gtk_window_set_transient_for() can't be used directly." ")"
, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 388); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "gtk_window_set_transient_for() can't be used directly."
")"); do { *((volatile int*)__null) = 388; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
; \
389 }
390
391nsWindow::nsWindow()
392 : mWindowVisibilityMutex("nsWindow::mWindowVisibilityMutex"),
393 mIsMapped(false),
394 mIsDestroyed(false),
395 mIsShown(false),
396 mNeedsShow(false),
397 mEnabled(true),
398 mCreated(false),
399 mHandleTouchEvent(false),
400 mIsDragPopup(false),
401 mCompositedScreen(gdk_screen_is_composited(gdk_screen_get_default())),
402 mIsAccelerated(false),
403 mIsAlert(false),
404 mWindowShouldStartDragging(false),
405 mHasMappedToplevel(false),
406 mRetryPointerGrab(false),
407 mPanInProgress(false),
408 mTitlebarBackdropState(false),
409 mIsChildWindow(false),
410 mAlwaysOnTop(false),
411 mNoAutoHide(false),
412 mIsTransparent(false),
413 mHasReceivedSizeAllocate(false),
414 mWidgetCursorLocked(false),
415 mUndecorated(false),
416 mPopupTrackInHierarchy(false),
417 mPopupTrackInHierarchyConfigured(false),
418 mHiddenPopupPositioned(false),
419 mTransparencyBitmapForTitlebar(false),
420 mHasAlphaVisual(false),
421 mPopupAnchored(false),
422 mPopupContextMenu(false),
423 mPopupMatchesLayout(false),
424 mPopupChanged(false),
425 mPopupTemporaryHidden(false),
426 mPopupClosed(false),
427 mPopupUseMoveToRect(false),
428 mWaitingForMoveToRectCallback(false),
429 mMovedAfterMoveToRect(false),
430 mResizedAfterMoveToRect(false),
431 mConfiguredClearColor(false),
432 mGotNonBlankPaint(false),
433 mNeedsToRetryCapturingMouse(false) {
434 mWindowType = WindowType::Child;
435 mSizeConstraints.mMaxSize = GetSafeWindowSize(mSizeConstraints.mMaxSize);
436
437 if (!gGlobalsInitialized) {
438 gGlobalsInitialized = true;
439
440 // It's OK if either of these fail, but it may not be one day.
441 initialize_prefs();
442
443#ifdef MOZ_WAYLAND1
444 // Wayland provides clipboard data to application on focus-in event
445 // so we need to init our clipboard hooks before we create window
446 // and get focus.
447 if (GdkIsWaylandDisplay()) {
448 nsCOMPtr<nsIClipboard> clipboard =
449 do_GetService("@mozilla.org/widget/clipboard;1");
450 NS_ASSERTION(clipboard, "Failed to init clipboard!")do { if (!(clipboard)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Failed to init clipboard!"
, "clipboard", "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 450); MOZ_PretendNoReturn(); } } while (0)
;
451 }
452#endif
453 }
454 // Dummy call to mozgtk to prevent the linker from removing
455 // the dependency with --as-needed.
456 // see toolkit/library/moz.build for details.
457 mozgtk_linker_holder();
458}
459
460nsWindow::~nsWindow() {
461 LOG("nsWindow::~nsWindow()")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::~nsWindow()", GetDebugTag
().get()); } } while (0)
;
462 Destroy();
463}
464
465/* static */
466void nsWindow::ReleaseGlobals() {
467 for (auto& cursor : gCursorCache) {
468 if (cursor) {
469 g_object_unref(cursor);
470 cursor = nullptr;
471 }
472 }
473}
474
475void nsWindow::DispatchActivateEvent(void) {
476#ifdef ACCESSIBILITY1
477 DispatchActivateEventAccessible();
478#endif // ACCESSIBILITY
479
480 if (mWidgetListener) mWidgetListener->WindowActivated();
481}
482
483void nsWindow::DispatchDeactivateEvent() {
484 if (mWidgetListener) {
485 mWidgetListener->WindowDeactivated();
486 }
487
488#ifdef ACCESSIBILITY1
489 DispatchDeactivateEventAccessible();
490#endif // ACCESSIBILITY
491}
492
493void nsWindow::DispatchResized() {
494 LOG("nsWindow::DispatchResized() size [%d, %d]", (int)(mBounds.width),do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::DispatchResized() size [%d, %d]"
, GetDebugTag().get(), (int)(mBounds.width), (int)(mBounds.height
)); } } while (0)
495 (int)(mBounds.height))do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::DispatchResized() size [%d, %d]"
, GetDebugTag().get(), (int)(mBounds.width), (int)(mBounds.height
)); } } while (0)
;
496
497 mNeedsDispatchSize = LayoutDeviceIntSize(-1, -1);
498 if (mWidgetListener) {
499 mWidgetListener->WindowResized(this, mBounds.width, mBounds.height);
500 }
501 if (mAttachedWidgetListener) {
502 mAttachedWidgetListener->WindowResized(this, mBounds.width, mBounds.height);
503 }
504}
505
506void nsWindow::MaybeDispatchResized() {
507 if (mNeedsDispatchSize != LayoutDeviceIntSize(-1, -1) && !mIsDestroyed) {
508 mBounds.SizeTo(mNeedsDispatchSize);
509 // Check mBounds size
510 if (mCompositorSession &&
511 !wr::WindowSizeSanityCheck(mBounds.width, mBounds.height)) {
512 gfxCriticalNoteOncestatic mozilla::gfx::CriticalLog sOnceAtLine512 = mozilla::gfx
::CriticalLog(mozilla::gfx::CriticalLog::DefaultOptions(false
))
<< "Invalid mBounds in MaybeDispatchResized "
513 << mBounds << " size state " << mSizeMode;
514 }
515
516 // Notify the GtkCompositorWidget of a ClientSizeChange
517 if (mCompositorWidgetDelegate) {
518 mCompositorWidgetDelegate->NotifyClientSizeChanged(GetClientSize());
519 }
520
521 DispatchResized();
522 }
523}
524
525nsIWidgetListener* nsWindow::GetListener() {
526 return mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener;
527}
528
529nsresult nsWindow::DispatchEvent(WidgetGUIEvent* aEvent,
530 nsEventStatus& aStatus) {
531#ifdef DEBUG1
532 debug_DumpEvent(stdoutstdout, aEvent->mWidget, aEvent, "something", 0);
533#endif
534 aStatus = nsEventStatus_eIgnore;
535 nsIWidgetListener* listener = GetListener();
536 if (listener) {
537 aStatus = listener->HandleEvent(aEvent, mUseAttachedEvents);
538 }
539
540 return NS_OK;
541}
542
543void nsWindow::OnDestroy(void) {
544 if (mOnDestroyCalled) {
545 return;
546 }
547
548 mOnDestroyCalled = true;
549
550 // Prevent deletion.
551 nsCOMPtr<nsIWidget> kungFuDeathGrip = this;
552
553 // release references to children, device context, toolkit + app shell
554 nsBaseWidget::OnDestroy();
555
556 // Remove association between this object and its parent and siblings.
557 nsBaseWidget::Destroy();
558 mParent = nullptr;
559
560 NotifyWindowDestroyed();
561}
562
563bool nsWindow::AreBoundsSane() {
564 // Check requested size, as mBounds might not have been updated.
565 return !mLastSizeRequest.IsEmpty();
566}
567
568void nsWindow::Destroy() {
569 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(NS_IsMainThread())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(NS_IsMainThread()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("NS_IsMainThread()"
, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 569); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "NS_IsMainThread()"
")"); do { *((volatile int*)__null) = 569; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
570
571 if (mIsDestroyed || !mCreated) {
572 return;
573 }
574
575 LOG("nsWindow::Destroy\n")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::Destroy\n", GetDebugTag(
).get()); } } while (0)
;
576
577 mIsDestroyed = true;
578 mCreated = false;
579
580 MozClearHandleID(mCompositorPauseTimeoutID, g_source_remove);
581
582#ifdef MOZ_WAYLAND1
583 // Shut down our local vsync source
584 if (mWaylandVsyncSource) {
585 mWaylandVsyncSource->Shutdown();
586 mWaylandVsyncSource = nullptr;
587 }
588 mWaylandVsyncDispatcher = nullptr;
589 UnlockNativePointer();
590#endif
591
592 // dragService will be null after shutdown of the service manager.
593 RefPtr<nsDragService> dragService = nsDragService::GetInstance();
594 if (dragService && this == dragService->GetMostRecentDestWindow()) {
595 dragService->ScheduleLeaveEvent();
596 }
597
598 nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
599 if (rollupListener) {
600 nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget();
601 if (static_cast<nsIWidget*>(this) == rollupWidget) {
602 rollupListener->Rollup({});
603 }
604 }
605
606 NativeShow(false);
607
608 MOZ_ASSERT(!gtk_widget_get_mapped(mShell))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!gtk_widget_get_mapped(mShell))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!gtk_widget_get_mapped(mShell
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!gtk_widget_get_mapped(mShell)", "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 608); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!gtk_widget_get_mapped(mShell)"
")"); do { *((volatile int*)__null) = 608; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
609 MOZ_ASSERT(!gtk_widget_get_mapped(GTK_WIDGET(mContainer)))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!gtk_widget_get_mapped(((((GtkWidget*) (void *) ((mContainer
)))))))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!gtk_widget_get_mapped(((((GtkWidget*) (void *) ((mContainer
))))))))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!gtk_widget_get_mapped(((((GtkWidget*) (void *) ((mContainer))))))"
, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 609); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!gtk_widget_get_mapped(((((GtkWidget*) (void *) ((mContainer))))))"
")"); do { *((volatile int*)__null) = 609; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
610
611 ClearTransparencyBitmap();
612
613 DestroyLayerManager();
614
615 // mSurfaceProvider holds reference to this nsWindow so we need to explicitly
616 // clear it here to avoid nsWindow leak.
617 mSurfaceProvider.CleanupResources();
618
619 g_signal_handlers_disconnect_by_data(gtk_settings_get_default(), this)g_signal_handlers_disconnect_matched ((gtk_settings_get_default
()), G_SIGNAL_MATCH_DATA, 0, 0, __null, __null, (this))
;
620
621 if (mIMContext) {
622 mIMContext->OnDestroyWindow(this);
623 }
624
625 // make sure that we remove ourself as the focus window
626 if (gFocusWindow == this) {
627 LOG("automatically losing focus...\n")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "automatically losing focus...\n", GetDebugTag
().get()); } } while (0)
;
628 gFocusWindow = nullptr;
629 }
630
631 if (sStoredLeaveNotifyEvent) {
632 nsWindow* window =
633 get_window_for_gdk_window(sStoredLeaveNotifyEvent->window);
634 if (window == this) {
635 sStoredLeaveNotifyEvent = nullptr;
636 }
637 }
638
639 // We need to detach accessible object here because mContainer is a custom
640 // widget and doesn't call gtk_widget_real_destroy() from destroy handler
641 // as regular widgets.
642 if (AtkObject* ac = gtk_widget_get_accessible(GTK_WIDGET(mContainer)((((GtkWidget*) (void *) ((mContainer))))))) {
643 gtk_accessible_set_widget(GTK_ACCESSIBLE(ac)((((GtkAccessible*) (void *) ((ac))))), nullptr);
644 }
645
646 gtk_widget_destroy(mShell);
647 mShell = nullptr;
648 mContainer = nullptr;
649
650 MOZ_ASSERT(!mGdkWindow,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mGdkWindow)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mGdkWindow))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("!mGdkWindow" " ("
"mGdkWindow should be NULL when mContainer is destroyed" ")"
, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 651); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mGdkWindow"
") (" "mGdkWindow should be NULL when mContainer is destroyed"
")"); do { *((volatile int*)__null) = 651; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
651 "mGdkWindow should be NULL when mContainer is destroyed")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mGdkWindow)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mGdkWindow))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("!mGdkWindow" " ("
"mGdkWindow should be NULL when mContainer is destroyed" ")"
, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 651); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mGdkWindow"
") (" "mGdkWindow should be NULL when mContainer is destroyed"
")"); do { *((volatile int*)__null) = 651; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
652
653#ifdef ACCESSIBILITY1
654 if (mRootAccessible) {
655 mRootAccessible = nullptr;
656 }
657#endif
658
659 // Save until last because OnDestroy() may cause us to be deleted.
660 OnDestroy();
661}
662
663nsIWidget* nsWindow::GetParent() { return mParent; }
664
665float nsWindow::GetDPI() {
666 float dpi = 96.0f;
667 nsCOMPtr<nsIScreen> screen = GetWidgetScreen();
668 if (screen) {
669 screen->GetDpi(&dpi);
670 }
671 return dpi;
672}
673
674double nsWindow::GetDefaultScaleInternal() { return FractionalScaleFactor(); }
675
676DesktopToLayoutDeviceScale nsWindow::GetDesktopToDeviceScale() {
677#ifdef MOZ_WAYLAND1
678 if (GdkIsWaylandDisplay()) {
679 return DesktopToLayoutDeviceScale(FractionalScaleFactor());
680 }
681#endif
682
683 // In Gtk/X11, we manage windows using device pixels.
684 return DesktopToLayoutDeviceScale(1.0);
685}
686
687DesktopToLayoutDeviceScale nsWindow::GetDesktopToDeviceScaleByScreen() {
688#ifdef MOZ_WAYLAND1
689 // In Wayland there's no way to get absolute position of the window and use it
690 // to determine the screen factor of the monitor on which the window is
691 // placed. The window is notified of the current scale factor but not at this
692 // point, so the GdkScaleFactor can return wrong value which can lead to wrong
693 // popup placement. We need to use parent's window scale factor for the new
694 // one.
695 if (GdkIsWaylandDisplay()) {
696 nsView* view = nsView::GetViewFor(this);
697 if (view) {
698 nsView* parentView = view->GetParent();
699 if (parentView) {
700 nsIWidget* parentWidget = parentView->GetNearestWidget(nullptr);
701 if (parentWidget) {
702 return DesktopToLayoutDeviceScale(
703 parentWidget->RoundsWidgetCoordinatesTo());
704 }
705 NS_WARNING("Widget has no parent")NS_DebugBreak(NS_DEBUG_WARNING, "Widget has no parent", nullptr
, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 705)
;
706 }
707 } else {
708 NS_WARNING("Cannot find widget view")NS_DebugBreak(NS_DEBUG_WARNING, "Cannot find widget view", nullptr
, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 708)
;
709 }
710 }
711#endif
712 return nsBaseWidget::GetDesktopToDeviceScale();
713}
714
715// Reparent a child window to a new parent.
716void nsWindow::SetParent(nsIWidget* aNewParent) {
717 LOG("nsWindow::SetParent() new parent %p", aNewParent)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::SetParent() new parent %p"
, GetDebugTag().get(), aNewParent); } } while (0)
;
718 if (!mIsChildWindow) {
719 NS_WARNING("Used by child widgets only")NS_DebugBreak(NS_DEBUG_WARNING, "Used by child widgets only",
nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 719)
;
720 return;
721 }
722
723 nsCOMPtr<nsIWidget> kungFuDeathGrip = this;
724 if (mParent) {
725 mParent->RemoveChild(this);
726 }
727 mParent = aNewParent;
728
729 // We're already deleted, quit.
730 if (!mGdkWindow || mIsDestroyed || !aNewParent) {
731 return;
732 }
733 aNewParent->AddChild(this);
734
735 auto* newParent = static_cast<nsWindow*>(aNewParent);
736
737 // New parent is deleted, quit.
738 if (newParent->mIsDestroyed) {
739 Destroy();
740 return;
741 }
742
743 GdkWindow* window = GetToplevelGdkWindow();
744 GdkWindow* parentWindow = newParent->GetToplevelGdkWindow();
745 LOG(" child GdkWindow %p set parent GdkWindow %p", window, parentWindow)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " child GdkWindow %p set parent GdkWindow %p"
, GetDebugTag().get(), window, parentWindow); } } while (0)
;
746 gdk_window_reparent(window, parentWindow, 0, 0);
747 SetHasMappedToplevel(newParent && newParent->mHasMappedToplevel);
748}
749
750bool nsWindow::WidgetTypeSupportsAcceleration() {
751 if (mWindowType == WindowType::Invisible) {
752 return false;
753 }
754
755 if (IsSmallPopup()) {
756 return false;
757 }
758 // Workaround for Bug 1479135
759 // We draw transparent popups on non-compositing screens by SW as we don't
760 // implement X shape masks in WebRender.
761 if (mWindowType == WindowType::Popup) {
762 return HasRemoteContent() && mCompositedScreen;
763 }
764
765 return true;
766}
767
768void nsWindow::ReparentNativeWidget(nsIWidget* aNewParent) {
769 MOZ_ASSERT(aNewParent, "null widget")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNewParent)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aNewParent))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("aNewParent" " (" "null widget"
")", "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 769); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aNewParent" ") ("
"null widget" ")"); do { *((volatile int*)__null) = 769; __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
;
770 MOZ_ASSERT(!mIsDestroyed, "")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mIsDestroyed)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mIsDestroyed))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!mIsDestroyed" " ("
"" ")", "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 770); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mIsDestroyed"
") (" "" ")"); do { *((volatile int*)__null) = 770; __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
;
771 MOZ_ASSERT(!static_cast<nsWindow*>(aNewParent)->mIsDestroyed, "")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!static_cast<nsWindow*>(aNewParent)->mIsDestroyed
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!static_cast<nsWindow*>(aNewParent)->mIsDestroyed
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"!static_cast<nsWindow*>(aNewParent)->mIsDestroyed" " ("
"" ")", "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 771); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!static_cast<nsWindow*>(aNewParent)->mIsDestroyed"
") (" "" ")"); do { *((volatile int*)__null) = 771; __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
;
772 MOZ_ASSERT(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mParent)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mParent))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!mParent" " (" "nsWindow::ReparentNativeWidget() works on toplevel windows only."
")", "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 774); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mParent" ") ("
"nsWindow::ReparentNativeWidget() works on toplevel windows only."
")"); do { *((volatile int*)__null) = 774; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
773 !mParent,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mParent)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mParent))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!mParent" " (" "nsWindow::ReparentNativeWidget() works on toplevel windows only."
")", "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 774); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mParent" ") ("
"nsWindow::ReparentNativeWidget() works on toplevel windows only."
")"); do { *((volatile int*)__null) = 774; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
774 "nsWindow::ReparentNativeWidget() works on toplevel windows only.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mParent)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mParent))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!mParent" " (" "nsWindow::ReparentNativeWidget() works on toplevel windows only."
")", "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 774); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mParent" ") ("
"nsWindow::ReparentNativeWidget() works on toplevel windows only."
")"); do { *((volatile int*)__null) = 774; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
775
776 auto* newParent = static_cast<nsWindow*>(aNewParent);
777 GtkWindow* newParentWidget = GTK_WINDOW(newParent->GetGtkWidget())((((GtkWindow*) (void *) ((newParent->GetGtkWidget())))));
778
779 LOG("nsWindow::ReparentNativeWidget new parent %p\n", newParent)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::ReparentNativeWidget new parent %p\n"
, GetDebugTag().get(), newParent); } } while (0)
;
780 GtkWindowSetTransientFor(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))), newParentWidget);
781}
782
783void nsWindow::SetModal(bool aModal) {
784 LOG("nsWindow::SetModal %d\n", aModal)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::SetModal %d\n", GetDebugTag
().get(), aModal); } } while (0)
;
785 if (mIsDestroyed) {
786 return;
787 }
788
789 gtk_window_set_modal(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))), aModal ? TRUE(!(0)) : FALSE(0));
790}
791
792// nsIWidget method, which means IsShown.
793bool nsWindow::IsVisible() const { return mIsShown; }
794
795bool nsWindow::IsMapped() const { return mIsMapped; }
796
797void nsWindow::RegisterTouchWindow() {
798 mHandleTouchEvent = true;
799 mTouches.Clear();
800}
801
802LayoutDeviceIntPoint nsWindow::GetScreenEdgeSlop() {
803 if (DrawsToCSDTitlebar()) {
804 return GetClientOffset();
805 }
806 return {};
807}
808
809void nsWindow::ConstrainPosition(DesktopIntPoint& aPoint) {
810 if (!mShell || GdkIsWaylandDisplay()) {
811 return;
812 }
813
814 double dpiScale = GetDefaultScale().scale;
815
816 // we need to use the window size in logical screen pixels
817 int32_t logWidth = std::max(NSToIntRound(mBounds.width / dpiScale), 1);
818 int32_t logHeight = std::max(NSToIntRound(mBounds.height / dpiScale), 1);
819
820 /* get our playing field. use the current screen, or failing that
821 for any reason, use device caps for the default screen. */
822 nsCOMPtr<nsIScreenManager> screenmgr =
823 do_GetService("@mozilla.org/gfx/screenmanager;1");
824 if (!screenmgr) {
825 return;
826 }
827 nsCOMPtr<nsIScreen> screen;
828 screenmgr->ScreenForRect(aPoint.x, aPoint.y, logWidth, logHeight,
829 getter_AddRefs(screen));
830 // We don't have any screen so leave the coordinates as is
831 if (!screen) {
832 return;
833 }
834
835 // For normalized windows, use the desktop work area.
836 // For full screen windows, use the desktop.
837 DesktopIntRect screenRect = mSizeMode == nsSizeMode_Fullscreen
838 ? screen->GetRectDisplayPix()
839 : screen->GetAvailRectDisplayPix();
840
841 // Expand for the decoration size if needed.
842 auto slop =
843 DesktopIntPoint::Round(GetScreenEdgeSlop() / GetDesktopToDeviceScale());
844 screenRect.Inflate(slop.x, slop.y);
845
846 if (aPoint.x < screenRect.x) {
847 aPoint.x = screenRect.x;
848 } else if (aPoint.x >= screenRect.XMost() - logWidth) {
849 aPoint.x = screenRect.XMost() - logWidth;
850 }
851
852 if (aPoint.y < screenRect.y) {
853 aPoint.y = screenRect.y;
854 } else if (aPoint.y >= screenRect.YMost() - logHeight) {
855 aPoint.y = screenRect.YMost() - logHeight;
856 }
857}
858
859void nsWindow::SetSizeConstraints(const SizeConstraints& aConstraints) {
860 mSizeConstraints.mMinSize = GetSafeWindowSize(aConstraints.mMinSize);
861 mSizeConstraints.mMaxSize = GetSafeWindowSize(aConstraints.mMaxSize);
862
863 ApplySizeConstraints();
864}
865
866bool nsWindow::DrawsToCSDTitlebar() const {
867 return mSizeMode == nsSizeMode_Normal &&
868 mGtkWindowDecoration == GTK_DECORATION_CLIENT && mDrawInTitlebar;
869}
870
871void nsWindow::AddCSDDecorationSize(int* aWidth, int* aHeight) {
872 if (mSizeMode != nsSizeMode_Normal || mUndecorated ||
873 mGtkWindowDecoration != GTK_DECORATION_CLIENT || !GdkIsWaylandDisplay() ||
874 !IsGnomeDesktopEnvironment()) {
875 return;
876 }
877
878 GtkBorder decorationSize = GetCSDDecorationSize(IsPopup());
879 *aWidth += decorationSize.left + decorationSize.right;
880 *aHeight += decorationSize.top + decorationSize.bottom;
881}
882
883#ifdef MOZ_WAYLAND1
884bool nsWindow::GetCSDDecorationOffset(int* aDx, int* aDy) {
885 if (!DrawsToCSDTitlebar()) {
886 return false;
887 }
888 GtkBorder decorationSize = GetCSDDecorationSize(IsPopup());
889 *aDx = decorationSize.left;
890 *aDy = decorationSize.top;
891 return true;
892}
893#endif
894
895void nsWindow::ApplySizeConstraints() {
896 if (mShell) {
897 GdkGeometry geometry;
898 geometry.min_width =
899 DevicePixelsToGdkCoordRoundUp(mSizeConstraints.mMinSize.width);
900 geometry.min_height =
901 DevicePixelsToGdkCoordRoundUp(mSizeConstraints.mMinSize.height);
902 geometry.max_width =
903 DevicePixelsToGdkCoordRoundDown(mSizeConstraints.mMaxSize.width);
904 geometry.max_height =
905 DevicePixelsToGdkCoordRoundDown(mSizeConstraints.mMaxSize.height);
906
907 uint32_t hints = 0;
908 if (mSizeConstraints.mMinSize != LayoutDeviceIntSize()) {
909 if (GdkIsWaylandDisplay()) {
910 gtk_widget_set_size_request(GTK_WIDGET(mContainer)((((GtkWidget*) (void *) ((mContainer))))), geometry.min_width,
911 geometry.min_height);
912 }
913 AddCSDDecorationSize(&geometry.min_width, &geometry.min_height);
914 hints |= GDK_HINT_MIN_SIZE;
915 }
916 if (mSizeConstraints.mMaxSize !=
917 LayoutDeviceIntSize(NS_MAXSIZE, NS_MAXSIZE)) {
918 AddCSDDecorationSize(&geometry.max_width, &geometry.max_height);
919 hints |= GDK_HINT_MAX_SIZE;
920 }
921
922 if (mAspectRatio != 0.0f && !mAspectResizer) {
923 geometry.min_aspect = mAspectRatio;
924 geometry.max_aspect = mAspectRatio;
925 hints |= GDK_HINT_ASPECT;
926 }
927
928 gtk_window_set_geometry_hints(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))), nullptr, &geometry,
929 GdkWindowHints(hints));
930 }
931}
932
933void nsWindow::Show(bool aState) {
934 if (aState == mIsShown) {
935 return;
936 }
937
938 mIsShown = aState;
939
940#ifdef MOZ_LOGGING1
941 LOG("nsWindow::Show state %d frame %s\n", aState, GetFrameTag().get())do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::Show state %d frame %s\n"
, GetDebugTag().get(), aState, GetFrameTag().get()); } } while
(0)
;
942 if (!aState && mSourceDragContext && GdkIsWaylandDisplay()) {
943 LOG(" closing Drag&Drop source window, D&D will be canceled!")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " closing Drag&Drop source window, D&D will be canceled!"
, GetDebugTag().get()); } } while (0)
;
944 }
945#endif
946
947 // Ok, someone called show on a window that isn't sized to a sane
948 // value. Mark this window as needing to have Show() called on it
949 // and return.
950 if ((aState && !AreBoundsSane()) || !mCreated) {
951 LOG("\tbounds are insane or window hasn't been created yet\n")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "\tbounds are insane or window hasn't been created yet\n"
, GetDebugTag().get()); } } while (0)
;
952 mNeedsShow = true;
953 return;
954 }
955
956 // If someone is hiding this widget, clear any needing show flag.
957 if (!aState) mNeedsShow = false;
958
959#ifdef ACCESSIBILITY1
960 if (aState && a11y::ShouldA11yBeEnabled()) CreateRootAccessible();
961#endif
962
963 NativeShow(aState);
964 RefreshWindowClass();
965}
966
967void nsWindow::ResizeInt(const Maybe<LayoutDeviceIntPoint>& aMove,
968 LayoutDeviceIntSize aSize) {
969 LOG("nsWindow::ResizeInt w:%d h:%d\n", aSize.width, aSize.height)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::ResizeInt w:%d h:%d\n", GetDebugTag
().get(), aSize.width, aSize.height); } } while (0)
;
970 const bool moved = aMove && *aMove != mBounds.TopLeft();
971 if (moved) {
972 mBounds.MoveTo(*aMove);
973 LOG(" with move to left:%d top:%d", aMove->x.value, aMove->y.value)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " with move to left:%d top:%d", GetDebugTag
().get(), aMove->x.value, aMove->y.value); } } while (0
)
;
974 }
975
976 ConstrainSize(&aSize.width, &aSize.height);
977 LOG(" ConstrainSize: w:%d h;%d\n", aSize.width, aSize.height)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " ConstrainSize: w:%d h;%d\n", GetDebugTag
().get(), aSize.width, aSize.height); } } while (0)
;
978
979 const bool resized = aSize != mLastSizeRequest || mBounds.Size() != aSize;
980#if MOZ_LOGGING1
981 LOG(" resized %d aSize [%d, %d] mLastSizeRequest [%d, %d] mBounds [%d, %d]",do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " resized %d aSize [%d, %d] mLastSizeRequest [%d, %d] mBounds [%d, %d]"
, GetDebugTag().get(), resized, aSize.width, aSize.height, mLastSizeRequest
.width, mLastSizeRequest.height, mBounds.width, mBounds.height
); } } while (0)
982 resized, aSize.width, aSize.height, mLastSizeRequest.width,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " resized %d aSize [%d, %d] mLastSizeRequest [%d, %d] mBounds [%d, %d]"
, GetDebugTag().get(), resized, aSize.width, aSize.height, mLastSizeRequest
.width, mLastSizeRequest.height, mBounds.width, mBounds.height
); } } while (0)
983 mLastSizeRequest.height, mBounds.width, mBounds.height)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " resized %d aSize [%d, %d] mLastSizeRequest [%d, %d] mBounds [%d, %d]"
, GetDebugTag().get(), resized, aSize.width, aSize.height, mLastSizeRequest
.width, mLastSizeRequest.height, mBounds.width, mBounds.height
); } } while (0)
;
984#endif
985
986 // For top-level windows, aSize should possibly be
987 // interpreted as frame bounds, but NativeMoveResize treats these as window
988 // bounds (Bug 581866).
989 mLastSizeRequest = aSize;
990 // Check size
991 if (mCompositorSession &&
992 !wr::WindowSizeSanityCheck(aSize.width, aSize.height)) {
993 gfxCriticalNoteOncestatic mozilla::gfx::CriticalLog sOnceAtLine993 = mozilla::gfx
::CriticalLog(mozilla::gfx::CriticalLog::DefaultOptions(false
))
<< "Invalid aSize in ResizeInt " << aSize
994 << " size state " << mSizeMode;
995 }
996
997 // Recalculate aspect ratio when resized from DOM
998 if (mAspectRatio != 0.0) {
999 LockAspectRatio(true);
1000 }
1001
1002 if (!mCreated) {
1003 return;
1004 }
1005
1006 if (!moved && !resized) {
1007 LOG(" not moved or resized, quit")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " not moved or resized, quit", GetDebugTag
().get()); } } while (0)
;
1008 return;
1009 }
1010
1011 NativeMoveResize(moved, resized);
1012
1013 // We optimistically assume size changes immediately in two cases:
1014 // 1. Override-redirect window: Size is controlled by only us.
1015 // 2. Managed window that has not not yet received a size-allocate event:
1016 // Resize() Callers expect initial sizes to be applied synchronously.
1017 // If the size request is not honored, then we'll correct in
1018 // OnSizeAllocate().
1019 //
1020 // When a managed window has already received a size-allocate, we cannot
1021 // assume we'll always get a notification if our request does not get
1022 // honored: "If the configure request has not changed, we don't ever resend
1023 // it, because it could mean fighting the user or window manager."
1024 // https://gitlab.gnome.org/GNOME/gtk/-/blob/3.24.31/gtk/gtkwindow.c#L9782
1025 // So we don't update mBounds until OnSizeAllocate() when we know the
1026 // request is granted.
1027 bool isOrWillBeVisible = mHasReceivedSizeAllocate || mNeedsShow || mIsShown;
1028 if (!isOrWillBeVisible ||
1029 gtk_window_get_window_type(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell)))))) == GTK_WINDOW_POPUP) {
1030 mBounds.SizeTo(aSize);
1031 if (mCompositorWidgetDelegate) {
1032 mCompositorWidgetDelegate->NotifyClientSizeChanged(aSize);
1033 }
1034 DispatchResized();
1035 }
1036}
1037
1038void nsWindow::Resize(double aWidth, double aHeight, bool aRepaint) {
1039 LOG("nsWindow::Resize %f %f\n", aWidth, aHeight)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::Resize %f %f\n", GetDebugTag
().get(), aWidth, aHeight); } } while (0)
;
1040
1041 double scale =
1042 BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
1043 auto size = LayoutDeviceIntSize::Round(scale * aWidth, scale * aHeight);
1044
1045 ResizeInt(Nothing(), size);
1046}
1047
1048void nsWindow::Resize(double aX, double aY, double aWidth, double aHeight,
1049 bool aRepaint) {
1050 LOG("nsWindow::Resize [%f,%f] -> [%f x %f] repaint %d\n", aX, aY, aWidth,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::Resize [%f,%f] -> [%f x %f] repaint %d\n"
, GetDebugTag().get(), aX, aY, aWidth, aHeight, aRepaint); } }
while (0)
1051 aHeight, aRepaint)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::Resize [%f,%f] -> [%f x %f] repaint %d\n"
, GetDebugTag().get(), aX, aY, aWidth, aHeight, aRepaint); } }
while (0)
;
1052
1053 double scale =
1054 BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
1055 auto size = LayoutDeviceIntSize::Round(scale * aWidth, scale * aHeight);
1056 auto topLeft = LayoutDeviceIntPoint::Round(scale * aX, scale * aY);
1057
1058 ResizeInt(Some(topLeft), size);
1059}
1060
1061void nsWindow::Enable(bool aState) { mEnabled = aState; }
1062
1063bool nsWindow::IsEnabled() const { return mEnabled; }
1064
1065void nsWindow::Move(double aX, double aY) {
1066 double scale =
1067 BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
1068 int32_t x = NSToIntRound(aX * scale);
1069 int32_t y = NSToIntRound(aY * scale);
1070
1071 LOG("nsWindow::Move to %d x %d\n", x, y)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::Move to %d x %d\n", GetDebugTag
().get(), x, y); } } while (0)
;
1072
1073 if (mSizeMode != nsSizeMode_Normal && IsTopLevelWindowType()) {
1074 LOG(" size state is not normal, bailing")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " size state is not normal, bailing"
, GetDebugTag().get()); } } while (0)
;
1075 return;
1076 }
1077
1078 // Since a popup window's x/y coordinates are in relation to to
1079 // the parent, the parent might have moved so we always move a
1080 // popup window.
1081 LOG(" bounds %d x %d\n", mBounds.x, mBounds.y)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " bounds %d x %d\n", GetDebugTag()
.get(), mBounds.x, mBounds.y); } } while (0)
;
1082 if (x == mBounds.x && y == mBounds.y && mWindowType != WindowType::Popup) {
1083 LOG(" position is the same, return\n")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " position is the same, return\n",
GetDebugTag().get()); } } while (0)
;
1084 return;
1085 }
1086
1087 // XXX Should we do some AreBoundsSane check here?
1088
1089 mBounds.x = x;
1090 mBounds.y = y;
1091
1092 if (!mCreated) {
1093 LOG(" is not created, return.\n")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " is not created, return.\n", GetDebugTag
().get()); } } while (0)
;
1094 return;
1095 }
1096
1097 NativeMoveResize(/* move */ true, /* resize */ false);
1098}
1099
1100bool nsWindow::IsPopup() const { return mWindowType == WindowType::Popup; }
1101
1102bool nsWindow::IsWaylandPopup() const {
1103 return GdkIsWaylandDisplay() && IsPopup();
1104}
1105
1106static nsMenuPopupFrame* GetMenuPopupFrame(nsIFrame* aFrame) {
1107 return do_QueryFrame(aFrame);
1108}
1109
1110void nsWindow::AppendPopupToHierarchyList(nsWindow* aToplevelWindow) {
1111 mWaylandToplevel = aToplevelWindow;
1112
1113 nsWindow* popup = aToplevelWindow;
1114 while (popup && popup->mWaylandPopupNext) {
1115 popup = popup->mWaylandPopupNext;
1116 }
1117 popup->mWaylandPopupNext = this;
1118
1119 mWaylandPopupPrev = popup;
1120 mWaylandPopupNext = nullptr;
1121 mPopupChanged = true;
1122 mPopupClosed = false;
1123}
1124
1125void nsWindow::RemovePopupFromHierarchyList() {
1126 // We're already removed from the popup hierarchy
1127 if (!IsInPopupHierarchy()) {
1128 return;
1129 }
1130 mWaylandPopupPrev->mWaylandPopupNext = mWaylandPopupNext;
1131 if (mWaylandPopupNext) {
1132 mWaylandPopupNext->mWaylandPopupPrev = mWaylandPopupPrev;
1133 mWaylandPopupNext->mPopupChanged = true;
1134 }
1135 mWaylandPopupNext = mWaylandPopupPrev = nullptr;
1136}
1137
1138// Gtk refuses to map popup window with x < 0 && y < 0 relative coordinates
1139// see https://gitlab.gnome.org/GNOME/gtk/-/issues/4071
1140// as a workaround just fool around and place the popup temporary to 0,0.
1141bool nsWindow::WaylandPopupRemoveNegativePosition(int* aX, int* aY) {
1142 // https://gitlab.gnome.org/GNOME/gtk/-/issues/4071 applies to temporary
1143 // windows only
1144 GdkWindow* window = GetToplevelGdkWindow();
1145 if (!window || gdk_window_get_window_type(window) != GDK_WINDOW_TEMP) {
1146 return false;
1147 }
1148
1149 LOG("nsWindow::WaylandPopupRemoveNegativePosition()")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::WaylandPopupRemoveNegativePosition()"
, GetDebugTag().get()); } } while (0)
;
1150
1151 int x, y;
1152 gtk_window_get_position(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))), &x, &y);
1153 bool moveBack = (x < 0 && y < 0);
1154 if (moveBack) {
1155 gtk_window_move(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))), 0, 0);
1156 if (aX) {
1157 *aX = x;
1158 }
1159 if (aY) {
1160 *aY = y;
1161 }
1162 }
1163
1164 gdk_window_get_geometry(window, &x, &y, nullptr, nullptr);
1165 if (x < 0 && y < 0) {
1166 gdk_window_move(window, 0, 0);
1167 }
1168
1169 return moveBack;
1170}
1171
1172void nsWindow::ShowWaylandPopupWindow() {
1173 LOG("nsWindow::ShowWaylandPopupWindow. Expected to see visible.")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::ShowWaylandPopupWindow. Expected to see visible."
, GetDebugTag().get()); } } while (0)
;
1174 MOZ_ASSERT(IsWaylandPopup())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsWaylandPopup())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsWaylandPopup()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("IsWaylandPopup()"
, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 1174); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsWaylandPopup()"
")"); do { *((volatile int*)__null) = 1174; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1175
1176 if (!mPopupTrackInHierarchy) {
1177 LOG(" popup is not tracked in popup hierarchy, show it now")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup is not tracked in popup hierarchy, show it now"
, GetDebugTag().get()); } } while (0)
;
1178 gtk_widget_show(mShell);
1179 return;
1180 }
1181
1182 // Popup position was checked before gdk_window_move_to_rect() callback
1183 // so just show it.
1184 if (mPopupUseMoveToRect && mWaitingForMoveToRectCallback) {
1185 LOG(" active move-to-rect callback, show it as is")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " active move-to-rect callback, show it as is"
, GetDebugTag().get()); } } while (0)
;
1186 gtk_widget_show(mShell);
1187 return;
1188 }
1189
1190 if (gtk_widget_is_visible(mShell)) {
1191 LOG(" is already visible, quit")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " is already visible, quit", GetDebugTag
().get()); } } while (0)
;
1192 return;
1193 }
1194
1195 int x, y;
1196 bool moved = WaylandPopupRemoveNegativePosition(&x, &y);
1197 gtk_widget_show(mShell);
1198 if (moved) {
1199 LOG(" move back to (%d, %d) and show", x, y)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " move back to (%d, %d) and show",
GetDebugTag().get(), x, y); } } while (0)
;
1200 gtk_window_move(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))), x, y);
1201 }
1202}
1203
1204void nsWindow::WaylandPopupMarkAsClosed() {
1205 LOG("nsWindow::WaylandPopupMarkAsClosed: [%p]\n", this)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::WaylandPopupMarkAsClosed: [%p]\n"
, GetDebugTag().get(), this); } } while (0)
;
1206 mPopupClosed = true;
1207 // If we have any child popup window notify it about
1208 // parent switch.
1209 if (mWaylandPopupNext) {
1210 mWaylandPopupNext->mPopupChanged = true;
1211 }
1212}
1213
1214nsWindow* nsWindow::WaylandPopupFindLast(nsWindow* aPopup) {
1215 while (aPopup && aPopup->mWaylandPopupNext) {
1216 aPopup = aPopup->mWaylandPopupNext;
1217 }
1218 return aPopup;
1219}
1220
1221// Hide and potentially removes popup from popup hierarchy.
1222void nsWindow::HideWaylandPopupWindow(bool aTemporaryHide,
1223 bool aRemoveFromPopupList) {
1224 LOG("nsWindow::HideWaylandPopupWindow: remove from list %d\n",do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::HideWaylandPopupWindow: remove from list %d\n"
, GetDebugTag().get(), aRemoveFromPopupList); } } while (0)
1225 aRemoveFromPopupList)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::HideWaylandPopupWindow: remove from list %d\n"
, GetDebugTag().get(), aRemoveFromPopupList); } } while (0)
;
1226 if (aRemoveFromPopupList) {
1227 RemovePopupFromHierarchyList();
1228 }
1229
1230 if (!mPopupClosed) {
1231 mPopupClosed = !aTemporaryHide;
1232 }
1233
1234 bool visible = gtk_widget_is_visible(mShell);
1235 LOG(" gtk_widget_is_visible() = %d\n", visible)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " gtk_widget_is_visible() = %d\n",
GetDebugTag().get(), visible); } } while (0)
;
1236
1237 // Restore only popups which are really visible
1238 mPopupTemporaryHidden = aTemporaryHide && visible;
1239
1240 // Hide only visible popups or popups closed pernamently.
1241 if (visible) {
1242 gtk_widget_hide(mShell);
1243
1244 // If there's pending Move-To-Rect callback and we hide the popup
1245 // the callback won't be called any more.
1246 mWaitingForMoveToRectCallback = false;
1247 }
1248
1249 if (mPopupClosed) {
1250 LOG(" Clearing mMoveToRectPopupSize\n")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " Clearing mMoveToRectPopupSize\n"
, GetDebugTag().get()); } } while (0)
;
1251 mMoveToRectPopupSize = {};
1252#ifdef MOZ_WAYLAND1
1253 if (moz_container_wayland_is_waiting_to_show(mContainer)) {
1254 // We need to clear rendering queue, see Bug 1782948.
1255 LOG(" popup failed to show by Wayland compositor, clear rendering "do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup failed to show by Wayland compositor, clear rendering "
"queue.", GetDebugTag().get()); } } while (0)
1256 "queue.")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup failed to show by Wayland compositor, clear rendering "
"queue.", GetDebugTag().get()); } } while (0)
;
1257 moz_container_wayland_clear_waiting_to_show_flag(mContainer);
1258 ClearRenderingQueue();
1259 }
1260#endif
1261 }
1262}
1263
1264void nsWindow::HideWaylandToplevelWindow() {
1265 LOG("nsWindow::HideWaylandToplevelWindow: [%p]\n", this)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::HideWaylandToplevelWindow: [%p]\n"
, GetDebugTag().get(), this); } } while (0)
;
1266 if (mWaylandPopupNext) {
1267 nsWindow* popup = WaylandPopupFindLast(mWaylandPopupNext);
1268 while (popup->mWaylandToplevel != nullptr) {
1269 nsWindow* prev = popup->mWaylandPopupPrev;
1270 popup->HideWaylandPopupWindow(/* aTemporaryHide */ false,
1271 /* aRemoveFromPopupList */ true);
1272 popup = prev;
1273 }
1274 }
1275 WaylandStopVsync();
1276 gtk_widget_hide(mShell);
1277}
1278
1279void nsWindow::ShowWaylandToplevelWindow() {
1280 MOZ_ASSERT(!IsWaylandPopup())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsWaylandPopup())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsWaylandPopup()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("!IsWaylandPopup()"
, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 1280); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsWaylandPopup()"
")"); do { *((volatile int*)__null) = 1280; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1281 LOG("nsWindow::ShowWaylandToplevelWindow")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::ShowWaylandToplevelWindow"
, GetDebugTag().get()); } } while (0)
;
1282 gtk_widget_show(mShell);
1283}
1284
1285void nsWindow::WaylandPopupRemoveClosedPopups() {
1286 LOG("nsWindow::WaylandPopupRemoveClosedPopups()")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::WaylandPopupRemoveClosedPopups()"
, GetDebugTag().get()); } } while (0)
;
1287 nsWindow* popup = this;
1288 while (popup) {
1289 nsWindow* next = popup->mWaylandPopupNext;
1290 if (popup->mPopupClosed) {
1291 popup->HideWaylandPopupWindow(/* aTemporaryHide */ false,
1292 /* aRemoveFromPopupList */ true);
1293 }
1294 popup = next;
1295 }
1296}
1297
1298// Hide all tooltips except the latest one.
1299void nsWindow::WaylandPopupHideTooltips() {
1300 LOG("nsWindow::WaylandPopupHideTooltips")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::WaylandPopupHideTooltips"
, GetDebugTag().get()); } } while (0)
;
1301 MOZ_ASSERT(mWaylandToplevel == nullptr, "Should be called on toplevel only!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mWaylandToplevel == nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mWaylandToplevel == nullptr)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("mWaylandToplevel == nullptr"
" (" "Should be called on toplevel only!" ")", "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 1301); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mWaylandToplevel == nullptr"
") (" "Should be called on toplevel only!" ")"); do { *((volatile
int*)__null) = 1301; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
1302
1303 nsWindow* popup = mWaylandPopupNext;
1304 while (popup && popup->mWaylandPopupNext) {
1305 if (popup->mPopupType == PopupType::Tooltip) {
1306 LOG(" hidding tooltip [%p]", popup)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " hidding tooltip [%p]", GetDebugTag
().get(), popup); } } while (0)
;
1307 popup->WaylandPopupMarkAsClosed();
1308 }
1309 popup = popup->mWaylandPopupNext;
1310 }
1311}
1312
1313void nsWindow::WaylandPopupCloseOrphanedPopups() {
1314#ifdef MOZ_WAYLAND1
1315 LOG("nsWindow::WaylandPopupCloseOrphanedPopups")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::WaylandPopupCloseOrphanedPopups"
, GetDebugTag().get()); } } while (0)
;
1316 MOZ_ASSERT(mWaylandToplevel == nullptr, "Should be called on toplevel only!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mWaylandToplevel == nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mWaylandToplevel == nullptr)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("mWaylandToplevel == nullptr"
" (" "Should be called on toplevel only!" ")", "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 1316); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mWaylandToplevel == nullptr"
") (" "Should be called on toplevel only!" ")"); do { *((volatile
int*)__null) = 1316; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
1317
1318 nsWindow* popup = mWaylandPopupNext;
1319 bool dangling = false;
1320 while (popup) {
1321 if (!dangling &&
1322 moz_container_wayland_is_waiting_to_show(popup->GetMozContainer())) {
1323 LOG(" popup [%p] is waiting to show, close all child popups", popup)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup [%p] is waiting to show, close all child popups"
, GetDebugTag().get(), popup); } } while (0)
;
1324 dangling = true;
1325 } else if (dangling) {
1326 popup->WaylandPopupMarkAsClosed();
1327 }
1328 popup = popup->mWaylandPopupNext;
1329 }
1330#endif
1331}
1332
1333// We can't show popups with remote content or overflow popups
1334// on top of regular ones.
1335// If there's any remote popup opened, close all parent popups of it.
1336void nsWindow::CloseAllPopupsBeforeRemotePopup() {
1337 LOG("nsWindow::CloseAllPopupsBeforeRemotePopup")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::CloseAllPopupsBeforeRemotePopup"
, GetDebugTag().get()); } } while (0)
;
1338 MOZ_ASSERT(mWaylandToplevel == nullptr, "Should be called on toplevel only!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mWaylandToplevel == nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mWaylandToplevel == nullptr)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("mWaylandToplevel == nullptr"
" (" "Should be called on toplevel only!" ")", "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 1338); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mWaylandToplevel == nullptr"
") (" "Should be called on toplevel only!" ")"); do { *((volatile
int*)__null) = 1338; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
1339
1340 // Don't waste time when there's only one popup opened.
1341 if (!mWaylandPopupNext || mWaylandPopupNext->mWaylandPopupNext == nullptr) {
1342 return;
1343 }
1344
1345 // Find the first opened remote content popup
1346 nsWindow* remotePopup = mWaylandPopupNext;
1347 while (remotePopup) {
1348 if (remotePopup->HasRemoteContent() ||
1349 remotePopup->IsWidgetOverflowWindow()) {
1350 LOG(" remote popup [%p]", remotePopup)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " remote popup [%p]", GetDebugTag(
).get(), remotePopup); } } while (0)
;
1351 break;
1352 }
1353 remotePopup = remotePopup->mWaylandPopupNext;
1354 }
1355
1356 if (!remotePopup) {
1357 return;
1358 }
1359
1360 // ...hide opened popups before the remote one.
1361 nsWindow* popup = mWaylandPopupNext;
1362 while (popup && popup != remotePopup) {
1363 LOG(" hidding popup [%p]", popup)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " hidding popup [%p]", GetDebugTag
().get(), popup); } } while (0)
;
1364 popup->WaylandPopupMarkAsClosed();
1365 popup = popup->mWaylandPopupNext;
1366 }
1367}
1368
1369static void GetLayoutPopupWidgetChain(
1370 nsTArray<nsIWidget*>* aLayoutWidgetHierarchy) {
1371 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
1372 pm->GetSubmenuWidgetChain(aLayoutWidgetHierarchy);
1373 aLayoutWidgetHierarchy->Reverse();
1374}
1375
1376// Compare 'this' popup position in Wayland widget hierarchy
1377// (mWaylandPopupPrev/mWaylandPopupNext) with
1378// 'this' popup position in layout hierarchy.
1379//
1380// When aMustMatchParent is true we also request
1381// 'this' parents match, i.e. 'this' has the same parent in
1382// both layout and widget hierarchy.
1383bool nsWindow::IsPopupInLayoutPopupChain(
1384 nsTArray<nsIWidget*>* aLayoutWidgetHierarchy, bool aMustMatchParent) {
1385 int len = (int)aLayoutWidgetHierarchy->Length();
1386 for (int i = 0; i < len; i++) {
1387 if (this == (*aLayoutWidgetHierarchy)[i]) {
1388 if (!aMustMatchParent) {
1389 return true;
1390 }
1391
1392 // Find correct parent popup for 'this' according to widget
1393 // hierarchy. That means we need to skip closed popups.
1394 nsWindow* parentPopup = nullptr;
1395 if (mWaylandPopupPrev != mWaylandToplevel) {
1396 parentPopup = mWaylandPopupPrev;
1397 while (parentPopup != mWaylandToplevel && parentPopup->mPopupClosed) {
1398 parentPopup = parentPopup->mWaylandPopupPrev;
1399 }
1400 }
1401
1402 if (i == 0) {
1403 // We found 'this' popups as a first popup in layout hierarchy.
1404 // It matches layout hierarchy if it's first widget also in
1405 // wayland widget hierarchy (i.e. parent is null).
1406 return parentPopup == nullptr;
1407 }
1408
1409 return parentPopup == (*aLayoutWidgetHierarchy)[i - 1];
1410 }
1411 }
1412 return false;
1413}
1414
1415// Hide popups which are not in popup chain.
1416void nsWindow::WaylandPopupHierarchyHideByLayout(
1417 nsTArray<nsIWidget*>* aLayoutWidgetHierarchy) {
1418 LOG("nsWindow::WaylandPopupHierarchyHideByLayout")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::WaylandPopupHierarchyHideByLayout"
, GetDebugTag().get()); } } while (0)
;
1419 MOZ_ASSERT(mWaylandToplevel == nullptr, "Should be called on toplevel only!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mWaylandToplevel == nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mWaylandToplevel == nullptr)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("mWaylandToplevel == nullptr"
" (" "Should be called on toplevel only!" ")", "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 1419); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mWaylandToplevel == nullptr"
") (" "Should be called on toplevel only!" ")"); do { *((volatile
int*)__null) = 1419; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
1420
1421 // Hide all popups which are not in layout popup chain
1422 nsWindow* popup = mWaylandPopupNext;
1423 while (popup) {
1424 // Don't check closed popups and drag source popups and tooltips.
1425 if (!popup->mPopupClosed && popup->mPopupType != PopupType::Tooltip &&
1426 !popup->mSourceDragContext) {
1427 if (!popup->IsPopupInLayoutPopupChain(aLayoutWidgetHierarchy,
1428 /* aMustMatchParent */ false)) {
1429 LOG(" hidding popup [%p]", popup)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " hidding popup [%p]", GetDebugTag
().get(), popup); } } while (0)
;
1430 popup->WaylandPopupMarkAsClosed();
1431 }
1432 }
1433 popup = popup->mWaylandPopupNext;
1434 }
1435}
1436
1437// Mark popups outside of layout hierarchy
1438void nsWindow::WaylandPopupHierarchyValidateByLayout(
1439 nsTArray<nsIWidget*>* aLayoutWidgetHierarchy) {
1440 LOG("nsWindow::WaylandPopupHierarchyValidateByLayout")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::WaylandPopupHierarchyValidateByLayout"
, GetDebugTag().get()); } } while (0)
;
1441 nsWindow* popup = mWaylandPopupNext;
1442 while (popup) {
1443 if (popup->mPopupType == PopupType::Tooltip) {
1444 popup->mPopupMatchesLayout = true;
1445 } else if (!popup->mPopupClosed) {
1446 popup->mPopupMatchesLayout = popup->IsPopupInLayoutPopupChain(
1447 aLayoutWidgetHierarchy, /* aMustMatchParent */ true);
1448 LOG(" popup [%p] parent window [%p] matches layout %d\n", (void*)popup,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup [%p] parent window [%p] matches layout %d\n"
, GetDebugTag().get(), (void*)popup, (void*)popup->mWaylandPopupPrev
, popup->mPopupMatchesLayout); } } while (0)
1449 (void*)popup->mWaylandPopupPrev, popup->mPopupMatchesLayout)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup [%p] parent window [%p] matches layout %d\n"
, GetDebugTag().get(), (void*)popup, (void*)popup->mWaylandPopupPrev
, popup->mPopupMatchesLayout); } } while (0)
;
1450 }
1451 popup = popup->mWaylandPopupNext;
1452 }
1453}
1454
1455void nsWindow::WaylandPopupHierarchyHideTemporary() {
1456 LOG("nsWindow::WaylandPopupHierarchyHideTemporary()")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::WaylandPopupHierarchyHideTemporary()"
, GetDebugTag().get()); } } while (0)
;
1457 nsWindow* popup = WaylandPopupFindLast(this);
1458 while (popup && popup != this) {
1459 LOG(" temporary hidding popup [%p]", popup)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " temporary hidding popup [%p]", GetDebugTag
().get(), popup); } } while (0)
;
1460 nsWindow* prev = popup->mWaylandPopupPrev;
1461 popup->HideWaylandPopupWindow(/* aTemporaryHide */ true,
1462 /* aRemoveFromPopupList */ false);
1463 popup = prev;
1464 }
1465}
1466
1467void nsWindow::WaylandPopupHierarchyShowTemporaryHidden() {
1468 LOG("nsWindow::WaylandPopupHierarchyShowTemporaryHidden()")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::WaylandPopupHierarchyShowTemporaryHidden()"
, GetDebugTag().get()); } } while (0)
;
1469 nsWindow* popup = this;
1470 while (popup) {
1471 if (popup->mPopupTemporaryHidden) {
1472 popup->mPopupTemporaryHidden = false;
1473 LOG(" showing temporary hidden popup [%p]", popup)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " showing temporary hidden popup [%p]"
, GetDebugTag().get(), popup); } } while (0)
;
1474 popup->ShowWaylandPopupWindow();
1475 }
1476 popup = popup->mWaylandPopupNext;
1477 }
1478}
1479
1480void nsWindow::WaylandPopupHierarchyCalculatePositions() {
1481 LOG("nsWindow::WaylandPopupHierarchyCalculatePositions()")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::WaylandPopupHierarchyCalculatePositions()"
, GetDebugTag().get()); } } while (0)
;
1482
1483 // Set widget hierarchy in Gtk
1484 nsWindow* popup = mWaylandToplevel->mWaylandPopupNext;
1485 while (popup) {
1486 LOG(" popup [%p] set parent window [%p]", (void*)popup,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup [%p] set parent window [%p]"
, GetDebugTag().get(), (void*)popup, (void*)popup->mWaylandPopupPrev
); } } while (0)
1487 (void*)popup->mWaylandPopupPrev)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup [%p] set parent window [%p]"
, GetDebugTag().get(), (void*)popup, (void*)popup->mWaylandPopupPrev
); } } while (0)
;
1488 GtkWindowSetTransientFor(GTK_WINDOW(popup->mShell)((((GtkWindow*) (void *) ((popup->mShell))))),
1489 GTK_WINDOW(popup->mWaylandPopupPrev->mShell)((((GtkWindow*) (void *) ((popup->mWaylandPopupPrev->mShell
)))))
);
1490 popup = popup->mWaylandPopupNext;
1491 }
1492
1493 popup = this;
1494 while (popup) {
1495 // Anchored window has mPopupPosition already calculated against
1496 // its parent, no need to recalculate.
1497 LOG(" popup [%p] bounds [%d, %d] -> [%d x %d]", popup,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup [%p] bounds [%d, %d] -> [%d x %d]"
, GetDebugTag().get(), popup, (int)(popup->mBounds.x / FractionalScaleFactor
()), (int)(popup->mBounds.y / FractionalScaleFactor()), (int
)(popup->mBounds.width / FractionalScaleFactor()), (int)(popup
->mBounds.height / FractionalScaleFactor())); } } while (0
)
1498 (int)(popup->mBounds.x / FractionalScaleFactor()),do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup [%p] bounds [%d, %d] -> [%d x %d]"
, GetDebugTag().get(), popup, (int)(popup->mBounds.x / FractionalScaleFactor
()), (int)(popup->mBounds.y / FractionalScaleFactor()), (int
)(popup->mBounds.width / FractionalScaleFactor()), (int)(popup
->mBounds.height / FractionalScaleFactor())); } } while (0
)
1499 (int)(popup->mBounds.y / FractionalScaleFactor()),do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup [%p] bounds [%d, %d] -> [%d x %d]"
, GetDebugTag().get(), popup, (int)(popup->mBounds.x / FractionalScaleFactor
()), (int)(popup->mBounds.y / FractionalScaleFactor()), (int
)(popup->mBounds.width / FractionalScaleFactor()), (int)(popup
->mBounds.height / FractionalScaleFactor())); } } while (0
)
1500 (int)(popup->mBounds.width / FractionalScaleFactor()),do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup [%p] bounds [%d, %d] -> [%d x %d]"
, GetDebugTag().get(), popup, (int)(popup->mBounds.x / FractionalScaleFactor
()), (int)(popup->mBounds.y / FractionalScaleFactor()), (int
)(popup->mBounds.width / FractionalScaleFactor()), (int)(popup
->mBounds.height / FractionalScaleFactor())); } } while (0
)
1501 (int)(popup->mBounds.height / FractionalScaleFactor()))do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup [%p] bounds [%d, %d] -> [%d x %d]"
, GetDebugTag().get(), popup, (int)(popup->mBounds.x / FractionalScaleFactor
()), (int)(popup->mBounds.y / FractionalScaleFactor()), (int
)(popup->mBounds.width / FractionalScaleFactor()), (int)(popup
->mBounds.height / FractionalScaleFactor())); } } while (0
)
;
1502#ifdef MOZ_LOGGING1
1503 if (LOG_ENABLED()((__builtin_expect(!!(mozilla::detail::log_test(gWidgetPopupLog
, mozilla::LogLevel::Debug)), 0)) || (__builtin_expect(!!(mozilla
::detail::log_test(gWidgetLog, mozilla::LogLevel::Debug)), 0)
))
) {
1504 if (nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame())) {
1505 auto r = LayoutDeviceRect::FromAppUnitsRounded(
1506 popupFrame->GetRect(),
1507 popupFrame->PresContext()->AppUnitsPerDevPixel());
1508 LOG(" popup [%p] layout [%d, %d] -> [%d x %d]", popup, r.x, r.y,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup [%p] layout [%d, %d] -> [%d x %d]"
, GetDebugTag().get(), popup, r.x, r.y, r.width, r.height); }
} while (0)
1509 r.width, r.height)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup [%p] layout [%d, %d] -> [%d x %d]"
, GetDebugTag().get(), popup, r.x, r.y, r.width, r.height); }
} while (0)
;
1510 }
1511 }
1512#endif
1513 if (popup->WaylandPopupIsFirst()) {
1514 LOG(" popup [%p] has toplevel as parent", popup)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup [%p] has toplevel as parent"
, GetDebugTag().get(), popup); } } while (0)
;
1515 popup->mRelativePopupPosition = popup->mPopupPosition;
1516 } else {
1517 if (popup->mPopupAnchored) {
1518 LOG(" popup [%p] is anchored", popup)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup [%p] is anchored", GetDebugTag
().get(), popup); } } while (0)
;
1519 if (!popup->mPopupMatchesLayout) {
1520 NS_WARNING("Anchored popup does not match layout!")NS_DebugBreak(NS_DEBUG_WARNING, "Anchored popup does not match layout!"
, nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 1520)
;
1521 }
1522 }
1523 GdkPoint parent = popup->WaylandGetParentPosition();
1524
1525 LOG(" popup [%p] uses transformed coordinates\n", popup)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup [%p] uses transformed coordinates\n"
, GetDebugTag().get(), popup); } } while (0)
;
1526 LOG(" parent position [%d, %d]\n", parent.x, parent.y)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " parent position [%d, %d]\n", GetDebugTag
().get(), parent.x, parent.y); } } while (0)
;
1527 LOG(" popup position [%d, %d]\n", popup->mPopupPosition.x,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup position [%d, %d]\n", GetDebugTag
().get(), popup->mPopupPosition.x, popup->mPopupPosition
.y); } } while (0)
1528 popup->mPopupPosition.y)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup position [%d, %d]\n", GetDebugTag
().get(), popup->mPopupPosition.x, popup->mPopupPosition
.y); } } while (0)
;
1529
1530 popup->mRelativePopupPosition.x = popup->mPopupPosition.x - parent.x;
1531 popup->mRelativePopupPosition.y = popup->mPopupPosition.y - parent.y;
1532 }
1533 LOG(" popup [%p] transformed popup coordinates from [%d, %d] to [%d, %d]",do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup [%p] transformed popup coordinates from [%d, %d] to [%d, %d]"
, GetDebugTag().get(), popup, popup->mPopupPosition.x, popup
->mPopupPosition.y, popup->mRelativePopupPosition.x, popup
->mRelativePopupPosition.y); } } while (0)
1534 popup, popup->mPopupPosition.x, popup->mPopupPosition.y,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup [%p] transformed popup coordinates from [%d, %d] to [%d, %d]"
, GetDebugTag().get(), popup, popup->mPopupPosition.x, popup
->mPopupPosition.y, popup->mRelativePopupPosition.x, popup
->mRelativePopupPosition.y); } } while (0)
1535 popup->mRelativePopupPosition.x, popup->mRelativePopupPosition.y)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup [%p] transformed popup coordinates from [%d, %d] to [%d, %d]"
, GetDebugTag().get(), popup, popup->mPopupPosition.x, popup
->mPopupPosition.y, popup->mRelativePopupPosition.x, popup
->mRelativePopupPosition.y); } } while (0)
;
1536 popup = popup->mWaylandPopupNext;
1537 }
1538}
1539
1540// The MenuList popups are used as dropdown menus for example in WebRTC
1541// microphone/camera chooser or autocomplete widgets.
1542bool nsWindow::WaylandPopupIsMenu() {
1543 nsMenuPopupFrame* menuPopupFrame = GetMenuPopupFrame(GetFrame());
1544 if (menuPopupFrame) {
1545 return mPopupType == PopupType::Menu && !menuPopupFrame->IsMenuList();
1546 }
1547 return false;
1548}
1549
1550bool nsWindow::WaylandPopupIsContextMenu() {
1551 nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
1552 if (!popupFrame) {
1553 return false;
1554 }
1555 return popupFrame->IsContextMenu();
1556}
1557
1558bool nsWindow::WaylandPopupIsPermanent() {
1559 nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
1560 if (!popupFrame) {
1561 // We can always hide popups without frames.
1562 return false;
1563 }
1564 return popupFrame->IsNoAutoHide();
1565}
1566
1567bool nsWindow::WaylandPopupIsAnchored() {
1568 nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
1569 if (!popupFrame) {
1570 // We can always hide popups without frames.
1571 return false;
1572 }
1573 return !!popupFrame->GetAnchor();
1574}
1575
1576bool nsWindow::IsWidgetOverflowWindow() {
1577 if (this->GetFrame() && this->GetFrame()->GetContent()->GetID()) {
1578 nsCString nodeId;
1579 this->GetFrame()->GetContent()->GetID()->ToUTF8String(nodeId);
1580 return nodeId.Equals("widget-overflow");
1581 }
1582 return false;
1583}
1584
1585bool nsWindow::WaylandPopupIsFirst() {
1586 return !mWaylandPopupPrev || !mWaylandPopupPrev->mWaylandToplevel;
1587}
1588
1589nsWindow* nsWindow::GetEffectiveParent() {
1590 GtkWindow* parentGtkWindow = gtk_window_get_transient_for(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))));
1591 if (!parentGtkWindow || !GTK_IS_WIDGET(parentGtkWindow)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(parentGtkWindow)); GType __t = ((gtk_widget_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; }))))
) {
1592 return nullptr;
1593 }
1594 return get_window_for_gtk_widget(GTK_WIDGET(parentGtkWindow)((((GtkWidget*) (void *) ((parentGtkWindow))))));
1595}
1596
1597GdkPoint nsWindow::WaylandGetParentPosition() {
1598 GdkPoint topLeft = {0, 0};
1599 nsWindow* window = GetEffectiveParent();
1600 if (window->IsPopup()) {
1601 topLeft = DevicePixelsToGdkPointRoundDown(window->mBounds.TopLeft());
1602 }
1603 LOG("nsWindow::WaylandGetParentPosition() [%d, %d]\n", topLeft.x, topLeft.y)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::WaylandGetParentPosition() [%d, %d]\n"
, GetDebugTag().get(), topLeft.x, topLeft.y); } } while (0)
;
1604 return topLeft;
1605}
1606
1607#ifdef MOZ_LOGGING1
1608void nsWindow::LogPopupHierarchy() {
1609 if (!LOG_ENABLED()((__builtin_expect(!!(mozilla::detail::log_test(gWidgetPopupLog
, mozilla::LogLevel::Debug)), 0)) || (__builtin_expect(!!(mozilla
::detail::log_test(gWidgetLog, mozilla::LogLevel::Debug)), 0)
))
) {
1610 return;
1611 }
1612
1613 LOG("Widget Popup Hierarchy:\n")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "Widget Popup Hierarchy:\n", GetDebugTag
().get()); } } while (0)
;
1614 if (!mWaylandToplevel->mWaylandPopupNext) {
1615 LOG(" Empty\n")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " Empty\n", GetDebugTag().get())
; } } while (0)
;
1616 } else {
1617 int indent = 4;
1618 nsWindow* popup = mWaylandToplevel->mWaylandPopupNext;
1619 while (popup) {
1620 nsPrintfCString indentString("%*s", indent, " ");
1621 LOG("%s %s %s nsWindow [%p] Menu %d Permanent %d ContextMenu %d "do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "%s %s %s nsWindow [%p] Menu %d Permanent %d ContextMenu %d "
"Anchored %d Visible %d MovedByRect %d\n", GetDebugTag().get
(), 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); } } while (0)
1622 "Anchored %d Visible %d MovedByRect %d\n",do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "%s %s %s nsWindow [%p] Menu %d Permanent %d ContextMenu %d "
"Anchored %d Visible %d MovedByRect %d\n", GetDebugTag().get
(), 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); } } while (0)
1623 indentString.get(), popup->GetFrameTag().get(),do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "%s %s %s nsWindow [%p] Menu %d Permanent %d ContextMenu %d "
"Anchored %d Visible %d MovedByRect %d\n", GetDebugTag().get
(), 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); } } while (0)
1624 popup->GetPopupTypeName().get(), popup, popup->WaylandPopupIsMenu(),do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "%s %s %s nsWindow [%p] Menu %d Permanent %d ContextMenu %d "
"Anchored %d Visible %d MovedByRect %d\n", GetDebugTag().get
(), 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); } } while (0)
1625 popup->WaylandPopupIsPermanent(), popup->mPopupContextMenu,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "%s %s %s nsWindow [%p] Menu %d Permanent %d ContextMenu %d "
"Anchored %d Visible %d MovedByRect %d\n", GetDebugTag().get
(), 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); } } while (0)
1626 popup->mPopupAnchored, gtk_widget_is_visible(popup->mShell),do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "%s %s %s nsWindow [%p] Menu %d Permanent %d ContextMenu %d "
"Anchored %d Visible %d MovedByRect %d\n", GetDebugTag().get
(), 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); } } while (0)
1627 popup->mPopupUseMoveToRect)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "%s %s %s nsWindow [%p] Menu %d Permanent %d ContextMenu %d "
"Anchored %d Visible %d MovedByRect %d\n", GetDebugTag().get
(), 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); } } while (0)
;
1628 indent += 4;
1629 popup = popup->mWaylandPopupNext;
1630 }
1631 }
1632
1633 LOG("Layout Popup Hierarchy:\n")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "Layout Popup Hierarchy:\n", GetDebugTag
().get()); } } while (0)
;
1634 AutoTArray<nsIWidget*, 5> widgetChain;
1635 GetLayoutPopupWidgetChain(&widgetChain);
1636 if (widgetChain.Length() == 0) {
1637 LOG(" Empty\n")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " Empty\n", GetDebugTag().get())
; } } while (0)
;
1638 } else {
1639 for (unsigned long i = 0; i < widgetChain.Length(); i++) {
1640 nsWindow* window = static_cast<nsWindow*>(widgetChain[i]);
1641 nsPrintfCString indentString("%*s", (int)(i + 1) * 4, " ");
1642 if (window) {
1643 LOG("%s %s %s nsWindow [%p] Menu %d Permanent %d ContextMenu %d "do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "%s %s %s nsWindow [%p] Menu %d Permanent %d ContextMenu %d "
"Anchored %d Visible %d MovedByRect %d\n", GetDebugTag().get
(), 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); } } while (0)
1644 "Anchored %d Visible %d MovedByRect %d\n",do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "%s %s %s nsWindow [%p] Menu %d Permanent %d ContextMenu %d "
"Anchored %d Visible %d MovedByRect %d\n", GetDebugTag().get
(), 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); } } while (0)
1645 indentString.get(), window->GetFrameTag().get(),do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "%s %s %s nsWindow [%p] Menu %d Permanent %d ContextMenu %d "
"Anchored %d Visible %d MovedByRect %d\n", GetDebugTag().get
(), 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); } } while (0)
1646 window->GetPopupTypeName().get(), window,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "%s %s %s nsWindow [%p] Menu %d Permanent %d ContextMenu %d "
"Anchored %d Visible %d MovedByRect %d\n", GetDebugTag().get
(), 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); } } while (0)
1647 window->WaylandPopupIsMenu(), window->WaylandPopupIsPermanent(),do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "%s %s %s nsWindow [%p] Menu %d Permanent %d ContextMenu %d "
"Anchored %d Visible %d MovedByRect %d\n", GetDebugTag().get
(), 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); } } while (0)
1648 window->mPopupContextMenu, window->mPopupAnchored,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "%s %s %s nsWindow [%p] Menu %d Permanent %d ContextMenu %d "
"Anchored %d Visible %d MovedByRect %d\n", GetDebugTag().get
(), 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); } } while (0)
1649 gtk_widget_is_visible(window->mShell), window->mPopupUseMoveToRect)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "%s %s %s nsWindow [%p] Menu %d Permanent %d ContextMenu %d "
"Anchored %d Visible %d MovedByRect %d\n", GetDebugTag().get
(), 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); } } while (0)
;
1650 } else {
1651 LOG("%s null window\n", indentString.get())do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "%s null window\n", GetDebugTag().get
(), indentString.get()); } } while (0)
;
1652 }
1653 }
1654 }
1655}
1656#endif
1657
1658nsWindow* nsWindow::GetTopmostWindow() {
1659 if (nsView* view = nsView::GetViewFor(this)) {
1660 if (nsView* parentView = view->GetParent()) {
1661 if (nsIWidget* parentWidget = parentView->GetNearestWidget(nullptr)) {
1662 return static_cast<nsWindow*>(parentWidget);
1663 }
1664 }
1665 }
1666 return nullptr;
1667}
1668
1669// Configure Wayland popup. If true is returned we need to track popup
1670// in popup hierarchy. Otherwise we just show it as is.
1671bool nsWindow::WaylandPopupConfigure() {
1672 if (mIsDragPopup) {
1673 return false;
1674 }
1675
1676 // Don't track popups without frame
1677 nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
1678 if (!popupFrame) {
1679 return false;
1680 }
1681
1682 // Popup state can be changed, see Bug 1728952.
1683 bool permanentStateMatches =
1684 mPopupTrackInHierarchy == !WaylandPopupIsPermanent();
1685
1686 // Popup permanent state (noautohide attribute) can change during popup life.
1687 if (mPopupTrackInHierarchyConfigured && permanentStateMatches) {
1688 return mPopupTrackInHierarchy;
1689 }
1690
1691 // Configure persistent popup params only once.
1692 // WaylandPopupIsAnchored() can give it wrong value after
1693 // nsMenuPopupFrame::MoveTo() call which we use in move-to-rect callback
1694 // to position popup after wayland position change.
1695 if (!mPopupTrackInHierarchyConfigured) {
1696 mPopupAnchored = WaylandPopupIsAnchored();
1697 mPopupContextMenu = WaylandPopupIsContextMenu();
1698 }
1699
1700 LOG("nsWindow::WaylandPopupConfigure tracked %d anchored %d hint %d\n",do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::WaylandPopupConfigure tracked %d anchored %d hint %d\n"
, GetDebugTag().get(), mPopupTrackInHierarchy, mPopupAnchored
, int(mPopupType)); } } while (0)
1701 mPopupTrackInHierarchy, mPopupAnchored, int(mPopupType))do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::WaylandPopupConfigure tracked %d anchored %d hint %d\n"
, GetDebugTag().get(), mPopupTrackInHierarchy, mPopupAnchored
, int(mPopupType)); } } while (0)
;
1702
1703 // Permanent state changed and popup is mapped.
1704 // We need to switch popup type but that's done when popup is mapped
1705 // by Gtk so we need to unmap the popup here.
1706 // It will be mapped again by gtk_widget_show().
1707 if (!permanentStateMatches && mIsMapped) {
1708 LOG(" permanent state change from %d to %d, unmapping",do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " permanent state change from %d to %d, unmapping"
, GetDebugTag().get(), mPopupTrackInHierarchy, !WaylandPopupIsPermanent
()); } } while (0)
1709 mPopupTrackInHierarchy, !WaylandPopupIsPermanent())do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " permanent state change from %d to %d, unmapping"
, GetDebugTag().get(), mPopupTrackInHierarchy, !WaylandPopupIsPermanent
()); } } while (0)
;
1710 gtk_widget_unmap(mShell);
1711 }
1712
1713 mPopupTrackInHierarchy = !WaylandPopupIsPermanent();
1714 LOG(" tracked in hierarchy %d\n", mPopupTrackInHierarchy)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " tracked in hierarchy %d\n", GetDebugTag
().get(), mPopupTrackInHierarchy); } } while (0)
;
1715
1716 // See gdkwindow-wayland.c and
1717 // should_map_as_popup()/should_map_as_subsurface()
1718 GdkWindowTypeHint gtkTypeHint;
1719 switch (mPopupType) {
1720 case PopupType::Menu:
1721 // GDK_WINDOW_TYPE_HINT_POPUP_MENU is mapped as xdg_popup by default.
1722 // We use this type for all menu popups.
1723 gtkTypeHint = GDK_WINDOW_TYPE_HINT_POPUP_MENU;
1724 LOG(" popup type Menu")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup type Menu", GetDebugTag().
get()); } } while (0)
;
1725 break;
1726 case PopupType::Tooltip:
1727 gtkTypeHint = GDK_WINDOW_TYPE_HINT_TOOLTIP;
1728 LOG(" popup type Tooltip")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup type Tooltip", GetDebugTag
().get()); } } while (0)
;
1729 break;
1730 default:
1731 gtkTypeHint = GDK_WINDOW_TYPE_HINT_UTILITY;
1732 LOG(" popup type Utility")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup type Utility", GetDebugTag
().get()); } } while (0)
;
1733 break;
1734 }
1735
1736 if (!mPopupTrackInHierarchy) {
1737 // GDK_WINDOW_TYPE_HINT_UTILITY is mapped as wl_subsurface
1738 // by default.
1739 LOG(" not tracked in popup hierarchy, switch to Utility")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " not tracked in popup hierarchy, switch to Utility"
, GetDebugTag().get()); } } while (0)
;
1740 gtkTypeHint = GDK_WINDOW_TYPE_HINT_UTILITY;
1741 }
1742 gtk_window_set_type_hint(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))), gtkTypeHint);
1743
1744 mPopupTrackInHierarchyConfigured = true;
1745 return mPopupTrackInHierarchy;
1746}
1747
1748bool nsWindow::IsInPopupHierarchy() {
1749 return mPopupTrackInHierarchy && mWaylandToplevel && mWaylandPopupPrev;
1750}
1751
1752void nsWindow::AddWindowToPopupHierarchy() {
1753 LOG("nsWindow::AddWindowToPopupHierarchy\n")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::AddWindowToPopupHierarchy\n"
, GetDebugTag().get()); } } while (0)
;
1754 if (!GetFrame()) {
1755 LOG(" Window without frame cannot be added as popup!\n")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " Window without frame cannot be added as popup!\n"
, GetDebugTag().get()); } } while (0)
;
1756 return;
1757 }
1758
1759 // Check if we're already in the hierarchy
1760 if (!IsInPopupHierarchy()) {
1761 mWaylandToplevel = GetTopmostWindow();
1762 AppendPopupToHierarchyList(mWaylandToplevel);
1763 }
1764}
1765
1766// Wayland keeps strong popup window hierarchy. We need to track active
1767// (visible) popup windows and make sure we hide popup on the same level
1768// before we open another one on that level. It means that every open
1769// popup needs to have an unique parent.
1770void nsWindow::UpdateWaylandPopupHierarchy() {
1771 LOG("nsWindow::UpdateWaylandPopupHierarchy\n")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::UpdateWaylandPopupHierarchy\n"
, GetDebugTag().get()); } } while (0)
;
1772
1773 // This popup hasn't been added to popup hierarchy yet so no need to
1774 // do any configurations.
1775 if (!IsInPopupHierarchy()) {
1776 LOG(" popup isn't in hierarchy\n")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup isn't in hierarchy\n", GetDebugTag
().get()); } } while (0)
;
1777 return;
1778 }
1779
1780#ifdef MOZ_LOGGING1
1781 LogPopupHierarchy();
1782 auto printPopupHierarchy = MakeScopeExit([&] { LogPopupHierarchy(); });
1783#endif
1784
1785 // Hide all tooltips without the last one. Tooltip can't be popup parent.
1786 mWaylandToplevel->WaylandPopupHideTooltips();
1787
1788 // See Bug 1709254 / https://gitlab.gnome.org/GNOME/gtk/-/issues/5092
1789 // It's possible that Wayland compositor refuses to show
1790 // a popup although Gtk claims it's visible.
1791 // We don't know if the popup is shown or not.
1792 // To avoid application crash refuse to create any child of such invisible
1793 // popup and close any child of it now.
1794 mWaylandToplevel->WaylandPopupCloseOrphanedPopups();
1795
1796 // Check if we have any remote content / overflow window in hierarchy.
1797 // We can't attach such widget on top of other popup.
1798 mWaylandToplevel->CloseAllPopupsBeforeRemotePopup();
1799
1800 // Check if your popup hierarchy matches layout hierarchy.
1801 // For instance we should not connect hamburger menu on top
1802 // of context menu.
1803 // Close all popups from different layout chains if possible.
1804 AutoTArray<nsIWidget*, 5> layoutPopupWidgetChain;
1805 GetLayoutPopupWidgetChain(&layoutPopupWidgetChain);
1806
1807 mWaylandToplevel->WaylandPopupHierarchyHideByLayout(&layoutPopupWidgetChain);
1808 mWaylandToplevel->WaylandPopupHierarchyValidateByLayout(
1809 &layoutPopupWidgetChain);
1810
1811 // Now we have Popup hierarchy complete.
1812 // Find first unchanged (and still open) popup to start with hierarchy
1813 // changes.
1814 nsWindow* changedPopup = mWaylandToplevel->mWaylandPopupNext;
1815 while (changedPopup) {
1816 // Stop when parent of this popup was changed and we need to recalc
1817 // popup position.
1818 if (changedPopup->mPopupChanged) {
1819 break;
1820 }
1821 // Stop when this popup is closed.
1822 if (changedPopup->mPopupClosed) {
1823 break;
1824 }
1825 changedPopup = changedPopup->mWaylandPopupNext;
1826 }
1827
1828 // We don't need to recompute popup positions, quit now.
1829 if (!changedPopup) {
1830 LOG(" changed Popup is null, quit.\n")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " changed Popup is null, quit.\n",
GetDebugTag().get()); } } while (0)
;
1831 return;
1832 }
1833
1834 LOG(" first changed popup [%p]\n", (void*)changedPopup)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " first changed popup [%p]\n", GetDebugTag
().get(), (void*)changedPopup); } } while (0)
;
1835
1836 // Hide parent popups if necessary (there are layout discontinuity)
1837 // reposition the popup and show them again.
1838 changedPopup->WaylandPopupHierarchyHideTemporary();
1839
1840 nsWindow* parentOfchangedPopup = nullptr;
1841 if (changedPopup->mPopupClosed) {
1842 parentOfchangedPopup = changedPopup->mWaylandPopupPrev;
1843 }
1844 changedPopup->WaylandPopupRemoveClosedPopups();
1845
1846 // It's possible that changedPopup was removed from widget hierarchy,
1847 // in such case use child popup of the removed one if there's any.
1848 if (!changedPopup->IsInPopupHierarchy()) {
1849 if (!parentOfchangedPopup || !parentOfchangedPopup->mWaylandPopupNext) {
1850 LOG(" last popup was removed, quit.\n")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " last popup was removed, quit.\n"
, GetDebugTag().get()); } } while (0)
;
1851 return;
1852 }
1853 changedPopup = parentOfchangedPopup->mWaylandPopupNext;
1854 }
1855
1856 GetLayoutPopupWidgetChain(&layoutPopupWidgetChain);
1857 mWaylandToplevel->WaylandPopupHierarchyValidateByLayout(
1858 &layoutPopupWidgetChain);
1859
1860 changedPopup->WaylandPopupHierarchyCalculatePositions();
1861
1862 nsWindow* popup = changedPopup;
1863 while (popup) {
1864 const bool useMoveToRect = [&] {
1865 if (!StaticPrefs::widget_wayland_use_move_to_rect_AtStartup()) {
1866 return false; // Not available.
1867 }
1868 if (!popup->mPopupMatchesLayout) {
1869 // We can use move_to_rect only when popups in popup hierarchy matches
1870 // layout hierarchy as move_to_rect request that parent/child
1871 // popups are adjacent.
1872 return false;
1873 }
1874 if (popup->mPopupType == PopupType::Panel &&
1875 popup->WaylandPopupIsFirst() &&
1876 popup->WaylandPopupFitsToplevelWindow(/* aMove */ true)) {
1877 // Workaround for https://gitlab.gnome.org/GNOME/gtk/-/issues/1986
1878 //
1879 // PopupType::Panel types are used for extension popups which may be
1880 // resized. If such popup uses move-to-rect, we need to hide it before
1881 // resize and show it again. That leads to massive flickering
1882 // so use plain move if possible to avoid it.
1883 //
1884 // Bug 1760276 - don't use move-to-rect when popup is inside main
1885 // Firefox window.
1886 //
1887 // Use it for first popups only due to another mutter bug
1888 // https://gitlab.gnome.org/GNOME/gtk/-/issues/5089
1889 // https://bugzilla.mozilla.org/show_bug.cgi?id=1784873
1890 return false;
1891 }
1892 if (!popup->WaylandPopupIsFirst() &&
1893 !popup->mWaylandPopupPrev->WaylandPopupIsFirst() &&
1894 !popup->mWaylandPopupPrev->mPopupUseMoveToRect) {
1895 // We can't use move-to-rect if there are more parents of
1896 // wl_subsurface popups types.
1897 //
1898 // It's because wl_subsurface is ignored by xgd_popup
1899 // (created by move-to-rect) so our popup scenario:
1900 //
1901 // toplevel -> xgd_popup(1) -> wl_subsurface(2) -> xgd_popup(3)
1902 //
1903 // looks for Wayland compositor as:
1904 //
1905 // toplevel -> xgd_popup(1) -> xgd_popup(3)
1906 //
1907 // If xgd_popup(1) and xgd_popup(3) are not connected
1908 // move-to-rect applied to xgd_popup(3) fails and we get missing popup.
1909 return false;
1910 }
1911 return true;
1912 }();
1913
1914 LOG(" popup [%p] matches layout [%d] anchored [%d] first popup [%d] use "do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup [%p] matches layout [%d] anchored [%d] first popup [%d] use "
"move-to-rect %d\n", GetDebugTag().get(), popup, popup->mPopupMatchesLayout
, popup->mPopupAnchored, popup->WaylandPopupIsFirst(), useMoveToRect
); } } while (0)
1915 "move-to-rect %d\n",do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup [%p] matches layout [%d] anchored [%d] first popup [%d] use "
"move-to-rect %d\n", GetDebugTag().get(), popup, popup->mPopupMatchesLayout
, popup->mPopupAnchored, popup->WaylandPopupIsFirst(), useMoveToRect
); } } while (0)
1916 popup, popup->mPopupMatchesLayout, popup->mPopupAnchored,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup [%p] matches layout [%d] anchored [%d] first popup [%d] use "
"move-to-rect %d\n", GetDebugTag().get(), popup, popup->mPopupMatchesLayout
, popup->mPopupAnchored, popup->WaylandPopupIsFirst(), useMoveToRect
); } } while (0)
1917 popup->WaylandPopupIsFirst(), useMoveToRect)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup [%p] matches layout [%d] anchored [%d] first popup [%d] use "
"move-to-rect %d\n", GetDebugTag().get(), popup, popup->mPopupMatchesLayout
, popup->mPopupAnchored, popup->WaylandPopupIsFirst(), useMoveToRect
); } } while (0)
;
1918
1919 popup->mPopupUseMoveToRect = useMoveToRect;
1920 popup->WaylandPopupMoveImpl();
1921 popup->mPopupChanged = false;
1922 popup = popup->mWaylandPopupNext;
1923 }
1924
1925 changedPopup->WaylandPopupHierarchyShowTemporaryHidden();
1926}
1927
1928static void NativeMoveResizeCallback(GdkWindow* window,
1929 const GdkRectangle* flipped_rect,
1930 const GdkRectangle* final_rect,
1931 gboolean flipped_x, gboolean flipped_y,
1932 void* aWindow) {
1933 LOG_POPUP("[%p] NativeMoveResizeCallback flipped_x %d flipped_y %d\n",do { const ::mozilla::LogModule* moz_real_module = gWidgetPopupLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "[%p] NativeMoveResizeCallback flipped_x %d flipped_y %d\n"
, aWindow, flipped_x, flipped_y); } } while (0)
1934 aWindow, flipped_x, flipped_y)do { const ::mozilla::LogModule* moz_real_module = gWidgetPopupLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "[%p] NativeMoveResizeCallback flipped_x %d flipped_y %d\n"
, aWindow, flipped_x, flipped_y); } } while (0)
;
1935 LOG_POPUP("[%p] new position [%d, %d] -> [%d x %d]", aWindow,do { const ::mozilla::LogModule* moz_real_module = gWidgetPopupLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "[%p] new position [%d, %d] -> [%d x %d]"
, aWindow, final_rect->x, final_rect->y, final_rect->
width, final_rect->height); } } while (0)
1936 final_rect->x, final_rect->y, final_rect->width,do { const ::mozilla::LogModule* moz_real_module = gWidgetPopupLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "[%p] new position [%d, %d] -> [%d x %d]"
, aWindow, final_rect->x, final_rect->y, final_rect->
width, final_rect->height); } } while (0)
1937 final_rect->height)do { const ::mozilla::LogModule* moz_real_module = gWidgetPopupLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "[%p] new position [%d, %d] -> [%d x %d]"
, aWindow, final_rect->x, final_rect->y, final_rect->
width, final_rect->height); } } while (0)
;
1938 nsWindow* wnd = get_window_for_gdk_window(window);
1939
1940 wnd->NativeMoveResizeWaylandPopupCallback(final_rect, flipped_x, flipped_y);
1941}
1942
1943// When popup is repositioned by widget code, we need to notify
1944// layout about it. It's because we control popup placement
1945// on widget on Wayland so layout may have old popup size/coordinates.
1946void nsWindow::WaylandPopupPropagateChangesToLayout(bool aMove, bool aResize) {
1947 LOG("nsWindow::WaylandPopupPropagateChangesToLayout()")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::WaylandPopupPropagateChangesToLayout()"
, GetDebugTag().get()); } } while (0)
;
1948
1949 if (aResize) {
1950 LOG(" needSizeUpdate\n")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " needSizeUpdate\n", GetDebugTag()
.get()); } } while (0)
;
1951 if (nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame())) {
1952 RefPtr<PresShell> presShell = popupFrame->PresShell();
1953 presShell->FrameNeedsReflow(popupFrame, IntrinsicDirty::None,
1954 NS_FRAME_IS_DIRTY);
1955 }
1956 }
1957 if (aMove) {
1958 LOG(" needPositionUpdate, bounds [%d, %d]", mBounds.x, mBounds.y)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " needPositionUpdate, bounds [%d, %d]"
, GetDebugTag().get(), mBounds.x, mBounds.y); } } while (0)
;
1959 NotifyWindowMoved(mBounds.x, mBounds.y, ByMoveToRect::Yes);
1960 }
1961}
1962
1963void nsWindow::NativeMoveResizeWaylandPopupCallback(
1964 const GdkRectangle* aFinalSize, bool aFlippedX, bool aFlippedY) {
1965 // We're getting move-to-rect callback without move-to-rect call.
1966 // That indicates a compositor bug. It happens when a window is hidden and
1967 // shown again before move-to-rect callback is fired.
1968 // It may lead to incorrect popup placement as we may call
1969 // gtk_window_move() between hide & show.
1970 // See Bug 1777919, 1789581.
1971#if MOZ_LOGGING1
1972 if (!mWaitingForMoveToRectCallback) {
1973 LOG(" Bogus move-to-rect callback! Expect wrong popup coordinates.")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " Bogus move-to-rect callback! Expect wrong popup coordinates."
, GetDebugTag().get()); } } while (0)
;
1974 }
1975#endif
1976
1977 mWaitingForMoveToRectCallback = false;
1978
1979 bool movedByLayout = mMovedAfterMoveToRect;
1980 bool resizedByLayout = mResizedAfterMoveToRect;
1981
1982 // Popup was moved between move-to-rect call and move-to-rect callback
1983 // and the coordinates from move-to-rect callback are outdated.
1984 if (movedByLayout || resizedByLayout) {
1985 LOG(" Another move/resize called during waiting for callback\n")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " Another move/resize called during waiting for callback\n"
, GetDebugTag().get()); } } while (0)
;
1986 mMovedAfterMoveToRect = false;
1987 mResizedAfterMoveToRect = false;
1988 // Fire another round of move/resize to reflect latest request
1989 // from layout.
1990 NativeMoveResize(movedByLayout, resizedByLayout);
1991 return;
1992 }
1993
1994 LOG(" orig mBounds [%d, %d] -> [%d x %d]\n", mBounds.x, mBounds.y,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " orig mBounds [%d, %d] -> [%d x %d]\n"
, GetDebugTag().get(), mBounds.x, mBounds.y, mBounds.width, mBounds
.height); } } while (0)
1995 mBounds.width, mBounds.height)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " orig mBounds [%d, %d] -> [%d x %d]\n"
, GetDebugTag().get(), mBounds.x, mBounds.y, mBounds.width, mBounds
.height); } } while (0)
;
1996
1997 LayoutDeviceIntRect newBounds = [&] {
1998 GdkRectangle finalRect = *aFinalSize;
1999 GdkPoint parent = WaylandGetParentPosition();
2000 finalRect.x += parent.x;
2001 finalRect.y += parent.y;
2002 return GdkRectToDevicePixels(finalRect);
2003 }();
2004
2005 LOG(" new mBounds [%d, %d] -> [%d x %d]", newBounds.x, newBounds.y,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " new mBounds [%d, %d] -> [%d x %d]"
, GetDebugTag().get(), newBounds.x, newBounds.y, newBounds.width
, newBounds.height); } } while (0)
2006 newBounds.width, newBounds.height)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " new mBounds [%d, %d] -> [%d x %d]"
, GetDebugTag().get(), newBounds.x, newBounds.y, newBounds.width
, newBounds.height); } } while (0)
;
2007
2008 bool needsPositionUpdate = newBounds.TopLeft() != mBounds.TopLeft();
2009 bool needsSizeUpdate = newBounds.Size() != mLastSizeRequest;
2010
2011 if (needsSizeUpdate) {
2012 // Wayland compositor changed popup size request from layout.
2013 // Set the constraints to use them in nsMenuPopupFrame::SetPopupPosition().
2014 // Beware that gtk_window_resize() requests sizes asynchronously and so
2015 // newBounds might not have the size from the most recent
2016 // gtk_window_resize().
2017 if (newBounds.width < mLastSizeRequest.width) {
2018 mMoveToRectPopupSize.width = newBounds.width;
2019 }
2020 if (newBounds.height < mLastSizeRequest.height) {
2021 mMoveToRectPopupSize.height = newBounds.height;
2022 }
2023 LOG(" mMoveToRectPopupSize set to [%d, %d]", mMoveToRectPopupSize.width,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " mMoveToRectPopupSize set to [%d, %d]"
, GetDebugTag().get(), mMoveToRectPopupSize.width, mMoveToRectPopupSize
.height); } } while (0)
2024 mMoveToRectPopupSize.height)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " mMoveToRectPopupSize set to [%d, %d]"
, GetDebugTag().get(), mMoveToRectPopupSize.width, mMoveToRectPopupSize
.height); } } while (0)
;
2025 }
2026 mBounds = newBounds;
2027 // Check mBounds size
2028 if (mCompositorSession &&
2029 !wr::WindowSizeSanityCheck(mBounds.width, mBounds.height)) {
2030 gfxCriticalNoteOncestatic mozilla::gfx::CriticalLog sOnceAtLine2030 = mozilla::gfx
::CriticalLog(mozilla::gfx::CriticalLog::DefaultOptions(false
))
<< "Invalid mBounds in PopupCallback " << mBounds
2031 << " size state " << mSizeMode;
2032 }
2033 WaylandPopupPropagateChangesToLayout(needsPositionUpdate, needsSizeUpdate);
2034}
2035
2036static GdkGravity PopupAlignmentToGdkGravity(int8_t aAlignment) {
2037 switch (aAlignment) {
2038 case POPUPALIGNMENT_NONE0:
2039 return GDK_GRAVITY_NORTH_WEST;
2040 case POPUPALIGNMENT_TOPLEFT1:
2041 return GDK_GRAVITY_NORTH_WEST;
2042 case POPUPALIGNMENT_TOPRIGHT-1:
2043 return GDK_GRAVITY_NORTH_EAST;
2044 case POPUPALIGNMENT_BOTTOMLEFT2:
2045 return GDK_GRAVITY_SOUTH_WEST;
2046 case POPUPALIGNMENT_BOTTOMRIGHT-2:
2047 return GDK_GRAVITY_SOUTH_EAST;
2048 case POPUPALIGNMENT_LEFTCENTER16:
2049 return GDK_GRAVITY_WEST;
2050 case POPUPALIGNMENT_RIGHTCENTER-16:
2051 return GDK_GRAVITY_EAST;
2052 case POPUPALIGNMENT_TOPCENTER17:
2053 return GDK_GRAVITY_NORTH;
2054 case POPUPALIGNMENT_BOTTOMCENTER18:
2055 return GDK_GRAVITY_SOUTH;
2056 }
2057 return GDK_GRAVITY_STATIC;
2058}
2059
2060bool nsWindow::IsPopupDirectionRTL() {
2061 nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
2062 return popupFrame && popupFrame->IsDirectionRTL();
2063}
2064
2065// Position the popup directly by gtk_window_move() and try to keep it
2066// on screen by just moving it in scope of it's parent window.
2067//
2068// It's used when we position noautihode popup and we don't use xdg_positioner.
2069// See Bug 1718867
2070void nsWindow::WaylandPopupSetDirectPosition() {
2071 GdkPoint topLeft = DevicePixelsToGdkPointRoundDown(mBounds.TopLeft());
2072 GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mLastSizeRequest);
2073
2074 LOG("nsWindow::WaylandPopupSetDirectPosition %d,%d -> %d x %d\n", topLeft.x,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::WaylandPopupSetDirectPosition %d,%d -> %d x %d\n"
, GetDebugTag().get(), topLeft.x, topLeft.y, size.width, size
.height); } } while (0)
2075 topLeft.y, size.width, size.height)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::WaylandPopupSetDirectPosition %d,%d -> %d x %d\n"
, GetDebugTag().get(), topLeft.x, topLeft.y, size.width, size
.height); } } while (0)
;
2076
2077 mPopupPosition = {topLeft.x, topLeft.y};
2078
2079 if (mIsDragPopup) {
2080 gtk_window_move(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))), topLeft.x, topLeft.y);
2081 gtk_window_resize(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))), size.width, size.height);
2082 // DND window is placed inside container so we need to make hard size
2083 // request to ensure parent container is resized too.
2084 gtk_widget_set_size_request(GTK_WIDGET(mShell)((((GtkWidget*) (void *) ((mShell))))), size.width, size.height);
2085 return;
2086 }
2087
2088 GtkWindow* parentGtkWindow = gtk_window_get_transient_for(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))));
2089 nsWindow* window = get_window_for_gtk_widget(GTK_WIDGET(parentGtkWindow)((((GtkWidget*) (void *) ((parentGtkWindow))))));
2090 if (!window) {
2091 return;
2092 }
2093 GdkWindow* gdkWindow = window->GetGdkWindow();
2094 if (!gdkWindow) {
2095 return;
2096 }
2097
2098 int parentWidth = gdk_window_get_width(gdkWindow);
2099 int popupWidth = size.width;
2100
2101 int x;
2102 gdk_window_get_position(gdkWindow, &x, nullptr);
2103
2104 // If popup is bigger than main window just center it.
2105 if (popupWidth > parentWidth) {
2106 mPopupPosition.x = -(parentWidth - popupWidth) / 2 + x;
2107 } else {
2108 if (IsPopupDirectionRTL()) {
2109 // Stick with right window edge
2110 if (mPopupPosition.x < x) {
2111 mPopupPosition.x = x;
2112 }
2113 } else {
2114 // Stick with left window edge
2115 if (mPopupPosition.x + popupWidth > parentWidth + x) {
2116 mPopupPosition.x = parentWidth + x - popupWidth;
2117 }
2118 }
2119 }
2120
2121 LOG(" set position [%d, %d]\n", mPopupPosition.x, mPopupPosition.y)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " set position [%d, %d]\n", GetDebugTag
().get(), mPopupPosition.x, mPopupPosition.y); } } while (0)
;
2122 gtk_window_move(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))), mPopupPosition.x, mPopupPosition.y);
2123
2124 LOG(" set size [%d, %d]\n", size.width, size.height)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " set size [%d, %d]\n", GetDebugTag
().get(), size.width, size.height); } } while (0)
;
2125 gtk_window_resize(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))), size.width, size.height);
2126
2127 if (mPopupPosition.x != topLeft.x) {
2128 mBounds.MoveTo(GdkPointToDevicePixels(mPopupPosition));
2129 LOG(" setting new bounds [%d, %d]\n", mBounds.x, mBounds.y)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " setting new bounds [%d, %d]\n", GetDebugTag
().get(), mBounds.x, mBounds.y); } } while (0)
;
2130 WaylandPopupPropagateChangesToLayout(/* move */ true, /* resize */ false);
2131 }
2132}
2133
2134bool nsWindow::WaylandPopupFitsToplevelWindow(bool aMove) {
2135 LOG("nsWindow::WaylandPopupFitsToplevelWindow() move %d", aMove)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::WaylandPopupFitsToplevelWindow() move %d"
, GetDebugTag().get(), aMove); } } while (0)
;
2136
2137 GtkWindow* parent = gtk_window_get_transient_for(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))));
2138 GtkWindow* tmp = parent;
Value stored to 'tmp' during its initialization is never read
2139 while ((tmp = gtk_window_get_transient_for(GTK_WINDOW(parent)((((GtkWindow*) (void *) ((parent)))))))) {
2140 parent = tmp;
2141 }
2142 GdkWindow* toplevelGdkWindow = gtk_widget_get_window(GTK_WIDGET(parent)((((GtkWidget*) (void *) ((parent))))));
2143 if (!toplevelGdkWindow) {
2144 NS_WARNING("Toplevel widget without GdkWindow?")NS_DebugBreak(NS_DEBUG_WARNING, "Toplevel widget without GdkWindow?"
, nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 2144)
;
2145 return false;
2146 }
2147
2148 int parentWidth = gdk_window_get_width(toplevelGdkWindow);
2149 int parentHeight = gdk_window_get_height(toplevelGdkWindow);
2150 LOG(" parent size %d x %d", parentWidth, parentHeight)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " parent size %d x %d", GetDebugTag
().get(), parentWidth, parentHeight); } } while (0)
;
2151
2152 GdkPoint topLeft = aMove ? mPopupPosition
2153 : DevicePixelsToGdkPointRoundDown(mBounds.TopLeft());
2154 GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mLastSizeRequest);
2155 LOG(" popup topleft %d, %d size %d x %d", topLeft.x, topLeft.y, size.width,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup topleft %d, %d size %d x %d"
, GetDebugTag().get(), topLeft.x, topLeft.y, size.width, size
.height); } } while (0)
2156 size.height)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup topleft %d, %d size %d x %d"
, GetDebugTag().get(), topLeft.x, topLeft.y, size.width, size
.height); } } while (0)
;
2157 int fits = topLeft.x >= 0 && topLeft.y >= 0 &&
2158 topLeft.x + size.width <= parentWidth &&
2159 topLeft.y + size.height <= parentHeight;
2160
2161 LOG(" fits %d", fits)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " fits %d", GetDebugTag().get(), fits
); } } while (0)
;
2162 return fits;
2163}
2164
2165void nsWindow::NativeMoveResizeWaylandPopup(bool aMove, bool aResize) {
2166 GdkPoint topLeft = DevicePixelsToGdkPointRoundDown(mBounds.TopLeft());
2167 GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mLastSizeRequest);
2168
2169 LOG("nsWindow::NativeMoveResizeWaylandPopup Bounds %d,%d -> %d x %d move %d "do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::NativeMoveResizeWaylandPopup Bounds %d,%d -> %d x %d move %d "
"resize %d\n", GetDebugTag().get(), topLeft.x, topLeft.y, size
.width, size.height, aMove, aResize); } } while (0)
2170 "resize %d\n",do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::NativeMoveResizeWaylandPopup Bounds %d,%d -> %d x %d move %d "
"resize %d\n", GetDebugTag().get(), topLeft.x, topLeft.y, size
.width, size.height, aMove, aResize); } } while (0)
2171 topLeft.x, topLeft.y, size.width, size.height, aMove, aResize)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::NativeMoveResizeWaylandPopup Bounds %d,%d -> %d x %d move %d "
"resize %d\n", GetDebugTag().get(), topLeft.x, topLeft.y, size
.width, size.height, aMove, aResize); } } while (0)
;
2172
2173 // Compositor may be confused by windows with width/height = 0
2174 // and positioning such windows leads to Bug 1555866.
2175 if (!AreBoundsSane()) {
2176 LOG(" Bounds are not sane (width: %d height: %d)\n",do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " Bounds are not sane (width: %d height: %d)\n"
, GetDebugTag().get(), mLastSizeRequest.width, mLastSizeRequest
.height); } } while (0)
2177 mLastSizeRequest.width, mLastSizeRequest.height)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " Bounds are not sane (width: %d height: %d)\n"
, GetDebugTag().get(), mLastSizeRequest.width, mLastSizeRequest
.height); } } while (0)
;
2178 return;
2179 }
2180
2181 if (mWaitingForMoveToRectCallback) {
2182 LOG(" waiting for move to rect, scheduling")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " waiting for move to rect, scheduling"
, GetDebugTag().get()); } } while (0)
;
2183 // mBounds position must not be overwritten before it is applied.
2184 // OnConfigureEvent() will not set mBounds to an old position for
2185 // GTK_WINDOW_POPUP.
2186 MOZ_ASSERT(gtk_window_get_window_type(GTK_WINDOW(mShell)) ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gtk_window_get_window_type(((((GtkWindow*) (void *) (
(mShell)))))) == GTK_WINDOW_POPUP)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gtk_window_get_window_type((
(((GtkWindow*) (void *) ((mShell)))))) == GTK_WINDOW_POPUP)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("gtk_window_get_window_type(((((GtkWindow*) (void *) ((mShell)))))) == GTK_WINDOW_POPUP"
, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 2187); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gtk_window_get_window_type(((((GtkWindow*) (void *) ((mShell)))))) == GTK_WINDOW_POPUP"
")"); do { *((volatile int*)__null) = 2187; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2187 GTK_WINDOW_POPUP)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gtk_window_get_window_type(((((GtkWindow*) (void *) (
(mShell)))))) == GTK_WINDOW_POPUP)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gtk_window_get_window_type((
(((GtkWindow*) (void *) ((mShell)))))) == GTK_WINDOW_POPUP)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("gtk_window_get_window_type(((((GtkWindow*) (void *) ((mShell)))))) == GTK_WINDOW_POPUP"
, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 2187); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gtk_window_get_window_type(((((GtkWindow*) (void *) ((mShell)))))) == GTK_WINDOW_POPUP"
")"); do { *((volatile int*)__null) = 2187; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2188 mMovedAfterMoveToRect = aMove;
2189 mResizedAfterMoveToRect = aResize;
2190 return;
2191 }
2192
2193 mMovedAfterMoveToRect = false;
2194 mResizedAfterMoveToRect = false;
2195
2196 bool trackedInHierarchy = WaylandPopupConfigure();
2197
2198 // Read popup position from layout if it was moved or newly created.
2199 // This position is used by move-to-rect method as we need anchor and other
2200 // info to place popup correctly.
2201 // We need WaylandPopupConfigure() to be called before to have all needed
2202 // popup info in place (mainly the anchored flag).
2203 if (aMove) {
2204 mPopupMoveToRectParams = WaylandPopupGetPositionFromLayout();
2205 }
2206
2207 if (!trackedInHierarchy) {
2208 WaylandPopupSetDirectPosition();
2209 return;
2210 }
2211
2212 if (aResize) {
2213 LOG(" set size [%d, %d]\n", size.width, size.height)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " set size [%d, %d]\n", GetDebugTag
().get(), size.width, size.height); } } while (0)
;
2214 gtk_window_resize(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))), size.width, size.height);
2215 }
2216
2217 if (!aMove && WaylandPopupFitsToplevelWindow(aMove)) {
2218 // Popup position has not been changed and its position/size fits
2219 // parent window so no need to reposition the window.
2220 LOG(" fits parent window size, just resize\n")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " fits parent window size, just resize\n"
, GetDebugTag().get()); } } while (0)
;
2221 return;
2222 }
2223
2224 // Mark popup as changed as we're updating position/size.
2225 mPopupChanged = true;
2226
2227 // Save popup position for former re-calculations when popup hierarchy
2228 // is changed.
2229 LOG(" popup position changed from [%d, %d] to [%d, %d]\n", mPopupPosition.x,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup position changed from [%d, %d] to [%d, %d]\n"
, GetDebugTag().get(), mPopupPosition.x, mPopupPosition.y, topLeft
.x, topLeft.y); } } while (0)
2230 mPopupPosition.y, topLeft.x, topLeft.y)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup position changed from [%d, %d] to [%d, %d]\n"
, GetDebugTag().get(), mPopupPosition.x, mPopupPosition.y, topLeft
.x, topLeft.y); } } while (0)
;
2231 mPopupPosition = {topLeft.x, topLeft.y};
2232
2233 UpdateWaylandPopupHierarchy();
2234}
2235
2236struct PopupSides {
2237 Maybe<Side> mVertical;
2238 Maybe<Side> mHorizontal;
2239};
2240
2241static PopupSides SidesForPopupAlignment(int8_t aAlignment) {
2242 switch (aAlignment) {
2243 case POPUPALIGNMENT_NONE0:
2244 break;
2245 case POPUPALIGNMENT_TOPLEFT1:
2246 return {Some(eSideTop), Some(eSideLeft)};
2247 case POPUPALIGNMENT_TOPRIGHT-1:
2248 return {Some(eSideTop), Some(eSideRight)};
2249 case POPUPALIGNMENT_BOTTOMLEFT2:
2250 return {Some(eSideBottom), Some(eSideLeft)};
2251 case POPUPALIGNMENT_BOTTOMRIGHT-2:
2252 return {Some(eSideBottom), Some(eSideRight)};
2253 case POPUPALIGNMENT_LEFTCENTER16:
2254 return {Nothing(), Some(eSideLeft)};
2255 case POPUPALIGNMENT_RIGHTCENTER-16:
2256 return {Nothing(), Some(eSideRight)};
2257 case POPUPALIGNMENT_TOPCENTER17:
2258 return {Some(eSideTop), Nothing()};
2259 case POPUPALIGNMENT_BOTTOMCENTER18:
2260 return {Some(eSideBottom), Nothing()};
2261 }
2262 return {};
2263}
2264
2265// We want to apply margins based on popup alignment (which would generally be
2266// just an offset to apply to the popup). However, to deal with flipping
2267// correctly, we apply the margin to the anchor when possible.
2268struct ResolvedPopupMargin {
2269 // A margin to be applied to the anchor.
2270 nsMargin mAnchorMargin;
2271 // An offset in app units to be applied to the popup for when we need to tell
2272 // GTK to center inside the anchor precisely (so we can't really do better in
2273 // presence of flips).
2274 nsPoint mPopupOffset;
2275};
2276
2277static ResolvedPopupMargin ResolveMargin(nsMenuPopupFrame* aFrame,
2278 int8_t aPopupAlign,
2279 int8_t aAnchorAlign,
2280 bool aAnchoredToPoint,
2281 bool aIsContextMenu) {
2282 nsMargin margin = aFrame->GetMargin();
2283 nsPoint offset;
2284
2285 if (aAnchoredToPoint) {
2286 // Since GTK doesn't allow us to specify margins itself, when anchored to a
2287 // point we can just assume we'll be aligned correctly... This is kind of
2288 // annoying but alas.
2289 //
2290 // This calculation must match the relevant unanchored popup calculation in
2291 // nsMenuPopupFrame::SetPopupPosition(), which should itself be the inverse
2292 // inverse of nsMenuPopupFrame::MoveTo().
2293 if (aIsContextMenu && aFrame->IsDirectionRTL()) {
2294 offset.x = -margin.right;
2295 } else {
2296 offset.x = margin.left;
2297 }
2298 offset.y = margin.top;
2299 return {nsMargin(), offset};
2300 }
2301
2302 auto popupSides = SidesForPopupAlignment(aPopupAlign);
2303 auto anchorSides = SidesForPopupAlignment(aAnchorAlign);
2304 // Matched sides: Invert the margin, so that we pull in the right direction.
2305 // Popup not aligned to any anchor side: We give up and use the offset,
2306 // applying the margin from the popup side.
2307 // Mismatched sides: We swap the margins so that we pull in the right
2308 // direction, e.g. margin-left: -10px should shrink 10px the _right_ of the
2309 // box, not the left of the box.
2310 if (popupSides.mHorizontal == anchorSides.mHorizontal) {
2311 margin.left = -margin.left;
2312 margin.right = -margin.right;
2313 } else if (!anchorSides.mHorizontal) {
2314 auto popupSide = *popupSides.mHorizontal;
2315 offset.x += popupSide == eSideRight ? -margin.Side(popupSide)
2316 : margin.Side(popupSide);
2317 margin.left = margin.right = 0;
2318 } else {
2319 std::swap(margin.left, margin.right);
2320 }
2321
2322 // Same logic as above, but in the vertical direction.
2323 if (popupSides.mVertical == anchorSides.mVertical) {
2324 margin.top = -margin.top;
2325 margin.bottom = -margin.bottom;
2326 } else if (!anchorSides.mVertical) {
2327 auto popupSide = *popupSides.mVertical;
2328 offset.y += popupSide == eSideBottom ? -margin.Side(popupSide)
2329 : margin.Side(popupSide);
2330 margin.top = margin.bottom = 0;
2331 } else {
2332 std::swap(margin.top, margin.bottom);
2333 }
2334
2335 return {margin, offset};
2336}
2337
2338#ifdef MOZ_LOGGING1
2339void nsWindow::LogPopupAnchorHints(int aHints) {
2340 static struct hints_ {
2341 int hint;
2342 char name[100];
2343 } hints[] = {
2344 {GDK_ANCHOR_FLIP_X, "GDK_ANCHOR_FLIP_X"},
2345 {GDK_ANCHOR_FLIP_Y, "GDK_ANCHOR_FLIP_Y"},
2346 {GDK_ANCHOR_SLIDE_X, "GDK_ANCHOR_SLIDE_X"},
2347 {GDK_ANCHOR_SLIDE_Y, "GDK_ANCHOR_SLIDE_Y"},
2348 {GDK_ANCHOR_RESIZE_X, "GDK_ANCHOR_RESIZE_X"},
2349 {GDK_ANCHOR_RESIZE_Y, "GDK_ANCHOR_RESIZE_X"},
2350 };
2351
2352 LOG(" PopupAnchorHints")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " PopupAnchorHints", GetDebugTag()
.get()); } } while (0)
;
2353 for (const auto& hint : hints) {
2354 if (hint.hint & aHints) {
2355 LOG(" %s", hint.name)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " %s", GetDebugTag().get(), hint
.name); } } while (0)
;
2356 }
2357 }
2358}
2359
2360void nsWindow::LogPopupGravity(GdkGravity aGravity) {
2361 static char gravity[][100]{"NONE",
2362 "GDK_GRAVITY_NORTH_WEST",
2363 "GDK_GRAVITY_NORTH",
2364 "GDK_GRAVITY_NORTH_EAST",
2365 "GDK_GRAVITY_WEST",
2366 "GDK_GRAVITY_CENTER",
2367 "GDK_GRAVITY_EAST",
2368 "GDK_GRAVITY_SOUTH_WEST",
2369 "GDK_GRAVITY_SOUTH",
2370 "GDK_GRAVITY_SOUTH_EAST",
2371 "GDK_GRAVITY_STATIC"};
2372 LOG(" %s", gravity[aGravity])do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " %s", GetDebugTag().get(), gravity
[aGravity]); } } while (0)
;
2373}
2374#endif
2375
2376const nsWindow::WaylandPopupMoveToRectParams
2377nsWindow::WaylandPopupGetPositionFromLayout() {
2378 LOG("nsWindow::WaylandPopupGetPositionFromLayout\n")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::WaylandPopupGetPositionFromLayout\n"
, GetDebugTag().get()); } } while (0)
;
2379
2380 nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
2381
2382 const bool isTopContextMenu = mPopupContextMenu && !mPopupAnchored;
2383 const bool isRTL = IsPopupDirectionRTL();
2384 const bool anchored = popupFrame->IsAnchored();
2385 int8_t popupAlign = POPUPALIGNMENT_TOPLEFT1;
2386 int8_t anchorAlign = POPUPALIGNMENT_BOTTOMRIGHT-2;
2387 if (anchored) {
2388 // See nsMenuPopupFrame::AdjustPositionForAnchorAlign.
2389 popupAlign = popupFrame->GetPopupAlignment();
2390 anchorAlign = popupFrame->GetPopupAnchor();
2391 }
2392 if (isRTL && (anchored || isTopContextMenu)) {
2393 popupAlign = -popupAlign;
2394 anchorAlign = -anchorAlign;
2395 }
2396
2397 // Although we have mPopupPosition / mRelativePopupPosition here
2398 // we can't use it. move-to-rect needs anchor rectangle to position a popup
2399 // but we have only a point from Resize().
2400 //
2401 // So we need to extract popup position from nsMenuPopupFrame() and duplicate
2402 // the layout work here.
2403 LayoutDeviceIntRect anchorRect;
2404 ResolvedPopupMargin popupMargin;
2405 {
2406 nsRect anchorRectAppUnits = popupFrame->GetUntransformedAnchorRect();
2407 // This is a somewhat hacky way of applying the popup margin. We don't know
2408 // if GTK will end up flipping the popup, in which case the offset we
2409 // compute is just wrong / applied to the wrong side.
2410 //
2411 // Instead, we tell it to anchor us at a smaller or bigger rect depending on
2412 // the margin, which achieves the same result if the popup is positioned
2413 // correctly, but doesn't misposition the popup when flipped across the
2414 // anchor.
2415 popupMargin = ResolveMargin(popupFrame, popupAlign, anchorAlign,
2416 anchorRectAppUnits.IsEmpty(), isTopContextMenu);
2417 LOG(" layout popup CSS anchor (%d, %d) %s, margin %s offset %s\n",do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " layout popup CSS anchor (%d, %d) %s, margin %s offset %s\n"
, GetDebugTag().get(), popupAlign, anchorAlign, ToString(anchorRectAppUnits
).c_str(), ToString(popupMargin.mAnchorMargin).c_str(), ToString
(popupMargin.mPopupOffset).c_str()); } } while (0)
2418 popupAlign, anchorAlign, ToString(anchorRectAppUnits).c_str(),do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " layout popup CSS anchor (%d, %d) %s, margin %s offset %s\n"
, GetDebugTag().get(), popupAlign, anchorAlign, ToString(anchorRectAppUnits
).c_str(), ToString(popupMargin.mAnchorMargin).c_str(), ToString
(popupMargin.mPopupOffset).c_str()); } } while (0)
2419 ToString(popupMargin.mAnchorMargin).c_str(),do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " layout popup CSS anchor (%d, %d) %s, margin %s offset %s\n"
, GetDebugTag().get(), popupAlign, anchorAlign, ToString(anchorRectAppUnits
).c_str(), ToString(popupMargin.mAnchorMargin).c_str(), ToString
(popupMargin.mPopupOffset).c_str()); } } while (0)
2420 ToString(popupMargin.mPopupOffset).c_str())do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " layout popup CSS anchor (%d, %d) %s, margin %s offset %s\n"
, GetDebugTag().get(), popupAlign, anchorAlign, ToString(anchorRectAppUnits
).c_str(), ToString(popupMargin.mAnchorMargin).c_str(), ToString
(popupMargin.mPopupOffset).c_str()); } } while (0)
;
2421 anchorRectAppUnits.Inflate(popupMargin.mAnchorMargin);
2422 LOG(" after margins %s\n", ToString(anchorRectAppUnits).c_str())do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " after margins %s\n", GetDebugTag
().get(), ToString(anchorRectAppUnits).c_str()); } } while (0
)
;
2423 nscoord auPerDev = popupFrame->PresContext()->AppUnitsPerDevPixel();
2424 anchorRect = LayoutDeviceIntRect::FromAppUnitsToNearest(anchorRectAppUnits,
2425 auPerDev);
2426 if (anchorRect.width < 0) {
2427 auto w = -anchorRect.width;
2428 anchorRect.width += w + 1;
2429 anchorRect.x += w;
2430 }
2431 LOG(" final %s\n", ToString(anchorRect).c_str())do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " final %s\n", GetDebugTag().get
(), ToString(anchorRect).c_str()); } } while (0)
;
2432 }
2433
2434 LOG(" relative popup rect position [%d, %d] -> [%d x %d]\n", anchorRect.x,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " relative popup rect position [%d, %d] -> [%d x %d]\n"
, GetDebugTag().get(), anchorRect.x, anchorRect.y, anchorRect
.width, anchorRect.height); } } while (0)
2435 anchorRect.y, anchorRect.width, anchorRect.height)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " relative popup rect position [%d, %d] -> [%d x %d]\n"
, GetDebugTag().get(), anchorRect.x, anchorRect.y, anchorRect
.width, anchorRect.height); } } while (0)
;
2436
2437 // Get gravity and flip type
2438 GdkGravity rectAnchor = PopupAlignmentToGdkGravity(anchorAlign);
2439 GdkGravity menuAnchor = PopupAlignmentToGdkGravity(popupAlign);
2440
2441 LOG(" parentRect gravity: %d anchor gravity: %d\n", rectAnchor, menuAnchor)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " parentRect gravity: %d anchor gravity: %d\n"
, GetDebugTag().get(), rectAnchor, menuAnchor); } } while (0)
;
2442
2443 // Gtk default is: GDK_ANCHOR_FLIP | GDK_ANCHOR_SLIDE | GDK_ANCHOR_RESIZE.
2444 // We want to SLIDE_X menu on the dual monitor setup rather than resize it
2445 // on the other monitor.
2446 GdkAnchorHints hints =
2447 GdkAnchorHints(GDK_ANCHOR_FLIP | GDK_ANCHOR_SLIDE_X | GDK_ANCHOR_RESIZE);
2448
2449 // slideHorizontal from nsMenuPopupFrame::SetPopupPosition
2450 int8_t position = popupFrame->GetAlignmentPosition();
2451 if (position >= POPUPPOSITION_BEFORESTART0 &&
2452 position <= POPUPPOSITION_AFTEREND3) {
2453 hints = GdkAnchorHints(hints | GDK_ANCHOR_SLIDE_X);
2454 }
2455 // slideVertical from nsMenuPopupFrame::SetPopupPosition
2456 if (position >= POPUPPOSITION_STARTBEFORE4 &&
2457 position <= POPUPPOSITION_ENDAFTER7) {
2458 hints = GdkAnchorHints(hints | GDK_ANCHOR_SLIDE_Y);
2459 }
2460
2461 FlipType flipType = popupFrame->GetFlipType();
2462 if (rectAnchor == GDK_GRAVITY_CENTER && menuAnchor == GDK_GRAVITY_CENTER) {
2463 // only slide
2464 hints = GdkAnchorHints(hints | GDK_ANCHOR_SLIDE);
2465 } else {
2466 switch (flipType) {
2467 case FlipType_Both:
2468 hints = GdkAnchorHints(hints | GDK_ANCHOR_FLIP);
2469 break;
2470 case FlipType_Slide:
2471 hints = GdkAnchorHints(hints | GDK_ANCHOR_SLIDE);
2472 break;
2473 case FlipType_Default:
2474 hints = GdkAnchorHints(hints | GDK_ANCHOR_FLIP);
2475 break;
2476 default:
2477 break;
2478 }
2479 }
2480 if (!WaylandPopupIsMenu()) {
2481 // we don't want to slide menus to fit the screen rather resize them
2482 hints = GdkAnchorHints(hints | GDK_ANCHOR_SLIDE);
2483 }
2484
2485 // We want tooltips to flip verticaly or slide only.
2486 // See nsMenuPopupFrame::SetPopupPosition().
2487 // https://searchfox.org/mozilla-central/rev/d0f5bc50aff3462c9d1546b88d60c5cb020eb15c/layout/xul/nsMenuPopupFrame.cpp#1603
2488 if (mPopupType == PopupType::Tooltip) {
2489 hints = GdkAnchorHints(GDK_ANCHOR_FLIP_Y | GDK_ANCHOR_SLIDE);
2490 }
2491
2492 return {
2493 anchorRect,
2494 rectAnchor,
2495 menuAnchor,
2496 hints,
2497 DevicePixelsToGdkPointRoundDown(LayoutDevicePoint::FromAppUnitsToNearest(
2498 popupMargin.mPopupOffset,
2499 popupFrame->PresContext()->AppUnitsPerDevPixel())),
2500 true};
2501}
2502
2503bool nsWindow::WaylandPopupAnchorAdjustForParentPopup(
2504 GdkRectangle* aPopupAnchor, GdkPoint* aOffset) {
2505 LOG("nsWindow::WaylandPopupAnchorAdjustForParentPopup")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::WaylandPopupAnchorAdjustForParentPopup"
, GetDebugTag().get()); } } while (0)
;
2506
2507 GtkWindow* parentGtkWindow = gtk_window_get_transient_for(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))));
2508 if (!parentGtkWindow || !GTK_IS_WIDGET(parentGtkWindow)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(parentGtkWindow)); GType __t = ((gtk_widget_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; }))))
) {
2509 NS_WARNING("Popup has no parent!")NS_DebugBreak(NS_DEBUG_WARNING, "Popup has no parent!", nullptr
, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 2509)
;
2510 return false;
2511 }
2512 GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(parentGtkWindow)((((GtkWidget*) (void *) ((parentGtkWindow))))));
2513 if (!window) {
2514 NS_WARNING("Popup parrent is not mapped!")NS_DebugBreak(NS_DEBUG_WARNING, "Popup parrent is not mapped!"
, nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 2514)
;
2515 return false;
2516 }
2517
2518 GdkRectangle parentWindowRect = {0, 0, gdk_window_get_width(window),
2519 gdk_window_get_height(window)};
2520 LOG(" parent window size %d x %d", parentWindowRect.width,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " parent window size %d x %d", GetDebugTag
().get(), parentWindowRect.width, parentWindowRect.height); }
} while (0)
2521 parentWindowRect.height)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " parent window size %d x %d", GetDebugTag
().get(), parentWindowRect.width, parentWindowRect.height); }
} while (0)
;
2522
2523 // We can't have rectangle anchor with zero width/height.
2524 if (!aPopupAnchor->width) {
2525 aPopupAnchor->width = 1;
2526 }
2527 if (!aPopupAnchor->height) {
2528 aPopupAnchor->height = 1;
2529 }
2530
2531 GdkRectangle finalRect;
2532 if (!gdk_rectangle_intersect(aPopupAnchor, &parentWindowRect, &finalRect)) {
2533 return false;
2534 }
2535 *aPopupAnchor = finalRect;
2536 LOG(" anchor is correct %d,%d -> %d x %d", finalRect.x, finalRect.y,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " anchor is correct %d,%d -> %d x %d"
, GetDebugTag().get(), finalRect.x, finalRect.y, finalRect.width
, finalRect.height); } } while (0)
2537 finalRect.width, finalRect.height)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " anchor is correct %d,%d -> %d x %d"
, GetDebugTag().get(), finalRect.x, finalRect.y, finalRect.width
, finalRect.height); } } while (0)
;
2538
2539 *aOffset = mPopupMoveToRectParams.mOffset;
2540 LOG(" anchor offset %d, %d", aOffset->x, aOffset->y)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " anchor offset %d, %d", GetDebugTag
().get(), aOffset->x, aOffset->y); } } while (0)
;
2541 return true;
2542}
2543
2544bool nsWindow::WaylandPopupCheckAndGetAnchor(GdkRectangle* aPopupAnchor,
2545 GdkPoint* aOffset) {
2546 LOG("nsWindow::WaylandPopupCheckAndGetAnchor")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::WaylandPopupCheckAndGetAnchor"
, GetDebugTag().get()); } } while (0)
;
2547
2548 GdkWindow* gdkWindow = GetToplevelGdkWindow();
2549 nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
2550 if (!gdkWindow || !popupFrame) {
2551 LOG(" can't use move-to-rect due missing gdkWindow or popupFrame")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " can't use move-to-rect due missing gdkWindow or popupFrame"
, GetDebugTag().get()); } } while (0)
;
2552 return false;
2553 }
2554
2555 if (popupFrame->IsConstrainedByLayout()) {
2556 LOG(" can't use move-to-rect, flipped / constrained by layout")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " can't use move-to-rect, flipped / constrained by layout"
, GetDebugTag().get()); } } while (0)
;
2557 return false;
2558 }
2559
2560 if (!mPopupMoveToRectParams.mAnchorSet) {
2561 LOG(" can't use move-to-rect due missing anchor")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " can't use move-to-rect due missing anchor"
, GetDebugTag().get()); } } while (0)
;
2562 return false;
2563 }
2564 // Update popup layout coordinates from layout by recent popup hierarchy
2565 // (calculate correct position according to parent window)
2566 // and convert to Gtk coordinates.
2567 LayoutDeviceIntRect anchorRect = mPopupMoveToRectParams.mAnchorRect;
2568 if (!WaylandPopupIsFirst()) {
2569 GdkPoint parent = WaylandGetParentPosition();
2570 LOG(" subtract parent position from anchor [%d, %d]\n", parent.x,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " subtract parent position from anchor [%d, %d]\n"
, GetDebugTag().get(), parent.x, parent.y); } } while (0)
2571 parent.y)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " subtract parent position from anchor [%d, %d]\n"
, GetDebugTag().get(), parent.x, parent.y); } } while (0)
;
2572 anchorRect.MoveBy(-GdkPointToDevicePixels(parent));
2573 }
2574
2575 *aPopupAnchor = DevicePixelsToGdkRectRoundOut(anchorRect);
2576 LOG(" anchored to rectangle [%d, %d] -> [%d x %d]", aPopupAnchor->x,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " anchored to rectangle [%d, %d] -> [%d x %d]"
, GetDebugTag().get(), aPopupAnchor->x, aPopupAnchor->y
, aPopupAnchor->width, aPopupAnchor->height); } } while
(0)
2577 aPopupAnchor->y, aPopupAnchor->width, aPopupAnchor->height)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " anchored to rectangle [%d, %d] -> [%d x %d]"
, GetDebugTag().get(), aPopupAnchor->x, aPopupAnchor->y
, aPopupAnchor->width, aPopupAnchor->height); } } while
(0)
;
2578
2579 if (!WaylandPopupAnchorAdjustForParentPopup(aPopupAnchor, aOffset)) {
2580 LOG(" can't use move-to-rect, anchor is not placed inside of parent "do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " can't use move-to-rect, anchor is not placed inside of parent "
"window", GetDebugTag().get()); } } while (0)
2581 "window")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " can't use move-to-rect, anchor is not placed inside of parent "
"window", GetDebugTag().get()); } } while (0)
;
2582 return false;
2583 }
2584
2585 return true;
2586}
2587
2588void nsWindow::WaylandPopupPrepareForMove() {
2589 LOG("nsWindow::WaylandPopupPrepareForMove()")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::WaylandPopupPrepareForMove()"
, GetDebugTag().get()); } } while (0)
;
2590
2591 if (mPopupType == PopupType::Tooltip) {
2592 // Don't fiddle with tooltips type, just hide it before move-to-rect
2593 if (mPopupUseMoveToRect && gtk_widget_is_visible(mShell)) {
2594 HideWaylandPopupWindow(/* aTemporaryHide */ true,
2595 /* aRemoveFromPopupList */ false);
2596 }
2597 LOG(" it's tooltip, quit")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " it's tooltip, quit", GetDebugTag
().get()); } } while (0)
;
2598 return;
2599 }
2600
2601 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1785185#c8
2602 // gtk_window_move() needs GDK_WINDOW_TYPE_HINT_UTILITY popup type.
2603 // move-to-rect requires GDK_WINDOW_TYPE_HINT_POPUP_MENU popups type.
2604 // We need to set it before map event when popup is hidden.
2605 const GdkWindowTypeHint currentType =
2606 gtk_window_get_type_hint(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))));
2607 const GdkWindowTypeHint requiredType = mPopupUseMoveToRect
2608 ? GDK_WINDOW_TYPE_HINT_POPUP_MENU
2609 : GDK_WINDOW_TYPE_HINT_UTILITY;
2610
2611 if (!mPopupUseMoveToRect && currentType == requiredType) {
2612 LOG(" type matches and we're not forced to hide it, quit.")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " type matches and we're not forced to hide it, quit."
, GetDebugTag().get()); } } while (0)
;
2613 return;
2614 }
2615
2616 if (gtk_widget_is_visible(mShell)) {
2617 HideWaylandPopupWindow(/* aTemporaryHide */ true,
2618 /* aRemoveFromPopupList */ false);
2619 }
2620
2621 if (currentType != requiredType) {
2622 LOG(" set type %s",do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " set type %s", GetDebugTag().get(
), requiredType == GDK_WINDOW_TYPE_HINT_POPUP_MENU ? "MENU" :
"UTILITY"); } } while (0)
2623 requiredType == GDK_WINDOW_TYPE_HINT_POPUP_MENU ? "MENU" : "UTILITY")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " set type %s", GetDebugTag().get(
), requiredType == GDK_WINDOW_TYPE_HINT_POPUP_MENU ? "MENU" :
"UTILITY"); } } while (0)
;
2624 gtk_window_set_type_hint(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))), requiredType);
2625 }
2626}
2627
2628// Plain popup move on Wayland - simply place popup on given location.
2629// We can't just call gtk_window_move() as it's not effective on visible
2630// popups.
2631void nsWindow::WaylandPopupMovePlain(int aX, int aY) {
2632 LOG("nsWindow::WaylandPopupMovePlain(%d, %d)", aX, aY)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::WaylandPopupMovePlain(%d, %d)"
, GetDebugTag().get(), aX, aY); } } while (0)
;
2633
2634 // We can directly move only popups based on wl_subsurface type.
2635 MOZ_DIAGNOSTIC_ASSERT(gtk_window_get_type_hint(GTK_WINDOW(mShell)) ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gtk_window_get_type_hint(((((GtkWindow*) (void *) ((
mShell)))))) == GDK_WINDOW_TYPE_HINT_UTILITY || gtk_window_get_type_hint
(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_TOOLTIP
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(gtk_window_get_type_hint(((((GtkWindow*) (void *) ((
mShell)))))) == GDK_WINDOW_TYPE_HINT_UTILITY || gtk_window_get_type_hint
(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_TOOLTIP
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"gtk_window_get_type_hint(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_UTILITY || gtk_window_get_type_hint(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_TOOLTIP"
, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 2638); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "gtk_window_get_type_hint(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_UTILITY || gtk_window_get_type_hint(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_TOOLTIP"
")"); do { *((volatile int*)__null) = 2638; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2636 GDK_WINDOW_TYPE_HINT_UTILITY ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gtk_window_get_type_hint(((((GtkWindow*) (void *) ((
mShell)))))) == GDK_WINDOW_TYPE_HINT_UTILITY || gtk_window_get_type_hint
(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_TOOLTIP
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(gtk_window_get_type_hint(((((GtkWindow*) (void *) ((
mShell)))))) == GDK_WINDOW_TYPE_HINT_UTILITY || gtk_window_get_type_hint
(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_TOOLTIP
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"gtk_window_get_type_hint(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_UTILITY || gtk_window_get_type_hint(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_TOOLTIP"
, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 2638); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "gtk_window_get_type_hint(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_UTILITY || gtk_window_get_type_hint(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_TOOLTIP"
")"); do { *((volatile int*)__null) = 2638; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2637 gtk_window_get_type_hint(GTK_WINDOW(mShell)) ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gtk_window_get_type_hint(((((GtkWindow*) (void *) ((
mShell)))))) == GDK_WINDOW_TYPE_HINT_UTILITY || gtk_window_get_type_hint
(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_TOOLTIP
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(gtk_window_get_type_hint(((((GtkWindow*) (void *) ((
mShell)))))) == GDK_WINDOW_TYPE_HINT_UTILITY || gtk_window_get_type_hint
(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_TOOLTIP
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"gtk_window_get_type_hint(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_UTILITY || gtk_window_get_type_hint(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_TOOLTIP"
, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 2638); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "gtk_window_get_type_hint(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_UTILITY || gtk_window_get_type_hint(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_TOOLTIP"
")"); do { *((volatile int*)__null) = 2638; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2638 GDK_WINDOW_TYPE_HINT_TOOLTIP)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gtk_window_get_type_hint(((((GtkWindow*) (void *) ((
mShell)))))) == GDK_WINDOW_TYPE_HINT_UTILITY || gtk_window_get_type_hint
(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_TOOLTIP
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(gtk_window_get_type_hint(((((GtkWindow*) (void *) ((
mShell)))))) == GDK_WINDOW_TYPE_HINT_UTILITY || gtk_window_get_type_hint
(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_TOOLTIP
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"gtk_window_get_type_hint(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_UTILITY || gtk_window_get_type_hint(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_TOOLTIP"
, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 2638); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "gtk_window_get_type_hint(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_UTILITY || gtk_window_get_type_hint(((((GtkWindow*) (void *) ((mShell)))))) == GDK_WINDOW_TYPE_HINT_TOOLTIP"
")"); do { *((volatile int*)__null) = 2638; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2639
2640 gtk_window_move(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))), aX, aY);
2641
2642 // gtk_window_move() can trick us. When widget is hidden gtk_window_move()
2643 // does not move the widget but sets new widget coordinates when widget
2644 // is mapped again.
2645 //
2646 // If popup used move-to-rect before
2647 // (GdkWindow has POSITION_METHOD_MOVE_TO_RECT set), popup will use
2648 // move-to-rect again when it's mapped and we'll get bogus move-to-rect
2649 // callback.
2650 //
2651 // gdk_window_move() sets position_method to POSITION_METHOD_MOVE_RESIZE
2652 // so we'll use plain move when popup is shown.
2653 if (!gtk_widget_get_mapped(mShell)) {
2654 if (GdkWindow* window = GetToplevelGdkWindow()) {
2655 gdk_window_move(window, aX, aY);
2656 }
2657 }
2658}
2659
2660void nsWindow::WaylandPopupMoveImpl() {
2661 // Available as of GTK 3.24+
2662 static auto sGdkWindowMoveToRect = (void (*)(
2663 GdkWindow*, const GdkRectangle*, GdkGravity, GdkGravity, GdkAnchorHints,
2664 gint, gint))dlsym(RTLD_DEFAULT((void *) 0), "gdk_window_move_to_rect");
2665
2666 if (mPopupUseMoveToRect && !sGdkWindowMoveToRect) {
2667 LOG("can't use move-to-rect due missing gdk_window_move_to_rect()")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "can't use move-to-rect due missing gdk_window_move_to_rect()"
, GetDebugTag().get()); } } while (0)
;
2668 mPopupUseMoveToRect = false;
2669 }
2670
2671 GdkRectangle gtkAnchorRect;
2672 GdkPoint offset;
2673 if (mPopupUseMoveToRect) {
2674 mPopupUseMoveToRect =
2675 WaylandPopupCheckAndGetAnchor(&gtkAnchorRect, &offset);
2676 }
2677
2678 LOG("nsWindow::WaylandPopupMove")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::WaylandPopupMove", GetDebugTag
().get()); } } while (0)
;
2679 LOG(" original widget popup position [%d, %d]\n", mPopupPosition.x,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " original widget popup position [%d, %d]\n"
, GetDebugTag().get(), mPopupPosition.x, mPopupPosition.y); }
} while (0)
2680 mPopupPosition.y)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " original widget popup position [%d, %d]\n"
, GetDebugTag().get(), mPopupPosition.x, mPopupPosition.y); }
} while (0)
;
2681 LOG(" relative widget popup position [%d, %d]\n", mRelativePopupPosition.x,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " relative widget popup position [%d, %d]\n"
, GetDebugTag().get(), mRelativePopupPosition.x, mRelativePopupPosition
.y); } } while (0)
2682 mRelativePopupPosition.y)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " relative widget popup position [%d, %d]\n"
, GetDebugTag().get(), mRelativePopupPosition.x, mRelativePopupPosition
.y); } } while (0)
;
2683 LOG(" popup use move to rect %d", mPopupUseMoveToRect)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " popup use move to rect %d", GetDebugTag
().get(), mPopupUseMoveToRect); } } while (0)
;
2684
2685 WaylandPopupPrepareForMove();
2686
2687 if (!mPopupUseMoveToRect) {
2688 WaylandPopupMovePlain(mRelativePopupPosition.x, mRelativePopupPosition.y);
2689 // Layout already should be aware of our bounds, since we didn't change it
2690 // from the widget side for flipping or so.
2691 return;
2692 }
2693
2694 // Correct popup position now. It will be updated by gdk_window_move_to_rect()
2695 // anyway but we need to set it now to avoid a race condition here.
2696 WaylandPopupRemoveNegativePosition();
2697
2698 GdkWindow* gdkWindow = GetToplevelGdkWindow();
2699 if (!g_signal_handler_find(gdkWindow, G_SIGNAL_MATCH_FUNC, 0, 0, nullptr,
2700 FuncToGpointer(NativeMoveResizeCallback), this)) {
2701 g_signal_connect(gdkWindow, "moved-to-rect",g_signal_connect_data ((gdkWindow), ("moved-to-rect"), (((GCallback
) (NativeMoveResizeCallback))), (this), __null, (GConnectFlags
) 0)
2702 G_CALLBACK(NativeMoveResizeCallback), this)g_signal_connect_data ((gdkWindow), ("moved-to-rect"), (((GCallback
) (NativeMoveResizeCallback))), (this), __null, (GConnectFlags
) 0)
;
2703 }
2704 mWaitingForMoveToRectCallback = true;
2705
2706#ifdef MOZ_LOGGING1
2707 if (LOG_ENABLED()((__builtin_expect(!!(mozilla::detail::log_test(gWidgetPopupLog
, mozilla::LogLevel::Debug)), 0)) || (__builtin_expect(!!(mozilla
::detail::log_test(gWidgetLog, mozilla::LogLevel::Debug)), 0)
))
) {
2708 LOG(" Call move-to-rect")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " Call move-to-rect", GetDebugTag(
).get()); } } while (0)
;
2709 LOG(" Anchor rect [%d, %d] -> [%d x %d]", gtkAnchorRect.x, gtkAnchorRect.y,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " Anchor rect [%d, %d] -> [%d x %d]"
, GetDebugTag().get(), gtkAnchorRect.x, gtkAnchorRect.y, gtkAnchorRect
.width, gtkAnchorRect.height); } } while (0)
2710 gtkAnchorRect.width, gtkAnchorRect.height)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " Anchor rect [%d, %d] -> [%d x %d]"
, GetDebugTag().get(), gtkAnchorRect.x, gtkAnchorRect.y, gtkAnchorRect
.width, gtkAnchorRect.height); } } while (0)
;
2711 LOG(" Offset [%d, %d]", offset.x, offset.y)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " Offset [%d, %d]", GetDebugTag().
get(), offset.x, offset.y); } } while (0)
;
2712 LOG(" AnchorType")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " AnchorType", GetDebugTag().get()
); } } while (0)
;
2713 LogPopupGravity(mPopupMoveToRectParams.mAnchorRectType);
2714 LOG(" PopupAnchorType")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " PopupAnchorType", GetDebugTag().
get()); } } while (0)
;
2715 LogPopupGravity(mPopupMoveToRectParams.mPopupAnchorType);
2716 LogPopupAnchorHints(mPopupMoveToRectParams.mHints);
2717 }
2718#endif
2719
2720 sGdkWindowMoveToRect(gdkWindow, &gtkAnchorRect,
2721 mPopupMoveToRectParams.mAnchorRectType,
2722 mPopupMoveToRectParams.mPopupAnchorType,
2723 mPopupMoveToRectParams.mHints, offset.x, offset.y);
2724}
2725
2726void nsWindow::SetZIndex(int32_t aZIndex) {
2727 nsIWidget* oldPrev = GetPrevSibling();
2728
2729 nsBaseWidget::SetZIndex(aZIndex);
2730
2731 if (GetPrevSibling() == oldPrev) {
2732 return;
2733 }
2734
2735 // We skip the nsWindows that don't have mGdkWindows.
2736 // These are probably in the process of being destroyed.
2737 if (!mGdkWindow) {
2738 return;
2739 }
2740
2741 if (!GetNextSibling()) {
2742 // We're to be on top.
2743 if (mGdkWindow) {
2744 gdk_window_raise(mGdkWindow);
2745 }
2746 } else {
2747 // All the siblings before us need to be below our widget.
2748 for (nsWindow* w = this; w;
2749 w = static_cast<nsWindow*>(w->GetPrevSibling())) {
2750 if (w->mGdkWindow) {
2751 gdk_window_lower(w->mGdkWindow);
2752 }
2753 }
2754 }
2755}
2756
2757void nsWindow::SetSizeMode(nsSizeMode aMode) {
2758 LOG("nsWindow::SetSizeMode %d\n", aMode)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::SetSizeMode %d\n", GetDebugTag
().get(), aMode); } } while (0)
;
2759
2760 // Return if there's no shell or our current state is the same as the mode we
2761 // were just set to.
2762 if (!mShell) {
2763 LOG(" no shell")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " no shell", GetDebugTag().get()
); } } while (0)
;
2764 return;
2765 }
2766
2767 if (mSizeMode == aMode && mLastSizeModeRequest == aMode) {
2768 LOG(" already set")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " already set", GetDebugTag().get
()); } } while (0)
;
2769 return;
2770 }
2771
2772 // It is tempting to try to optimize calls below based only on current
2773 // mSizeMode, but that wouldn't work if there's a size-request in flight
2774 // (specially before show). See bug 1789823.
2775 const auto SizeModeMightBe = [&](nsSizeMode aModeToTest) {
2776 if (mSizeMode != mLastSizeModeRequest) {
2777 // Arbitrary size mode requests might be ongoing.
2778 return true;
2779 }
2780 return mSizeMode == aModeToTest;
2781 };
2782
2783 if (aMode != nsSizeMode_Fullscreen && aMode != nsSizeMode_Minimized) {
2784 // Fullscreen and minimized are compatible.
2785 if (SizeModeMightBe(nsSizeMode_Fullscreen)) {
2786 MakeFullScreen(false);
2787 }
2788 }
2789
2790 switch (aMode) {
2791 case nsSizeMode_Maximized:
2792 LOG(" set maximized")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " set maximized", GetDebugTag().
get()); } } while (0)
;
2793 gtk_window_maximize(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))));
2794 break;
2795 case nsSizeMode_Minimized:
2796 LOG(" set minimized")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " set minimized", GetDebugTag().
get()); } } while (0)
;
2797 gtk_window_iconify(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))));
2798 break;
2799 case nsSizeMode_Fullscreen:
2800 LOG(" set fullscreen")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " set fullscreen", GetDebugTag()
.get()); } } while (0)
;
2801 MakeFullScreen(true);
2802 break;
2803 default:
2804 MOZ_FALLTHROUGH_ASSERT("Unknown size mode")do { do { } while (false); MOZ_ReportCrash("" "MOZ_FALLTHROUGH_ASSERT: "
"Unknown size mode", "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 2804); AnnotateMozCrashReason("MOZ_CRASH(" "MOZ_FALLTHROUGH_ASSERT: "
"Unknown size mode" ")"); do { *((volatile int*)__null) = 2804
; __attribute__((nomerge)) ::abort(); } while (false); } while
(false)
;
2805 case nsSizeMode_Normal:
2806 LOG(" set normal")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " set normal", GetDebugTag().get
()); } } while (0)
;
2807 if (SizeModeMightBe(nsSizeMode_Maximized)) {
2808 gtk_window_unmaximize(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))));
2809 }
2810 if (SizeModeMightBe(nsSizeMode_Minimized)) {
2811 gtk_window_deiconify(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))));
2812 // We need this for actual deiconification on mutter.
2813 gtk_window_present(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))));
2814 }
2815 break;
2816 }
2817 mLastSizeModeRequest = aMode;
2818}
2819
2820#define kDesktopMutterSchema"org.gnome.mutter"_ns "org.gnome.mutter"_ns
2821#define kDesktopDynamicWorkspacesKey"dynamic-workspaces"_ns "dynamic-workspaces"_ns
2822
2823static bool WorkspaceManagementDisabled(GdkScreen* screen) {
2824 if (Preferences::GetBool("widget.disable-workspace-management", false)) {
2825 return true;
2826 }
2827 if (Preferences::HasUserValue("widget.workspace-management")) {
2828 return Preferences::GetBool("widget.workspace-management");
2829 }
2830
2831 if (IsGnomeDesktopEnvironment()) {
2832 // Gnome uses dynamic workspaces by default so disable workspace management
2833 // in that case.
2834 bool usesDynamicWorkspaces = true;
2835 nsCOMPtr<nsIGSettingsService> gsettings =
2836 do_GetService(NS_GSETTINGSSERVICE_CONTRACTID"@mozilla.org/gsettings-service;1");
2837 if (gsettings) {
2838 nsCOMPtr<nsIGSettingsCollection> mutterSettings;
2839 gsettings->GetCollectionForSchema(kDesktopMutterSchema"org.gnome.mutter"_ns,
2840 getter_AddRefs(mutterSettings));
2841 if (mutterSettings) {
2842 mutterSettings->GetBoolean(kDesktopDynamicWorkspacesKey"dynamic-workspaces"_ns,
2843 &usesDynamicWorkspaces);
2844 }
2845 }
2846 return usesDynamicWorkspaces;
2847 }
2848
2849 const auto& desktop = GetDesktopEnvironmentIdentifier();
2850 return desktop.EqualsLiteral("bspwm") || desktop.EqualsLiteral("i3");
2851}
2852
2853void nsWindow::GetWorkspaceID(nsAString& workspaceID) {
2854 workspaceID.Truncate();
2855
2856 if (!GdkIsX11Display() || !mShell) {
2857 return;
2858 }
2859
2860#ifdef MOZ_X111
2861 LOG("nsWindow::GetWorkspaceID()\n")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::GetWorkspaceID()\n", GetDebugTag
().get()); } } while (0)
;
2862
2863 // Get the gdk window for this widget.
2864 GdkWindow* gdk_window = GetToplevelGdkWindow();
2865 if (!gdk_window) {
2866 LOG(" missing Gdk window, quit.")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " missing Gdk window, quit.", GetDebugTag
().get()); } } while (0)
;
2867 return;
2868 }
2869
2870 if (WorkspaceManagementDisabled(gdk_window_get_screen(gdk_window))) {
2871 LOG(" WorkspaceManagementDisabled, quit.")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " WorkspaceManagementDisabled, quit."
, GetDebugTag().get()); } } while (0)
;
2872 return;
2873 }
2874
2875 GdkAtom cardinal_atom = gdk_x11_xatom_to_atom(XA_CARDINAL((Atom) 6));
2876 GdkAtom type_returned;
2877 int format_returned;
2878 int length_returned;
2879 long* wm_desktop;
2880
2881 if (!gdk_property_get(gdk_window, gdk_atom_intern("_NET_WM_DESKTOP", FALSE(0)),
2882 cardinal_atom,
2883 0, // offset
2884 INT32_MAX(2147483647), // length
2885 FALSE(0), // delete
2886 &type_returned, &format_returned, &length_returned,
2887 (guchar**)&wm_desktop)) {
2888 LOG(" gdk_property_get() failed, quit.")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " gdk_property_get() failed, quit."
, GetDebugTag().get()); } } while (0)
;
2889 return;
2890 }
2891
2892 LOG(" got workspace ID %d", (int32_t)wm_desktop[0])do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " got workspace ID %d", GetDebugTag
().get(), (int32_t)wm_desktop[0]); } } while (0)
;
2893 workspaceID.AppendInt((int32_t)wm_desktop[0]);
2894 g_free(wm_desktop);
2895#endif
2896}
2897
2898void nsWindow::MoveToWorkspace(const nsAString& workspaceIDStr) {
2899 nsresult rv = NS_OK;
2900 int32_t workspaceID = workspaceIDStr.ToInteger(&rv);
2901
2902 LOG("nsWindow::MoveToWorkspace() ID %d", workspaceID)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::MoveToWorkspace() ID %d"
, GetDebugTag().get(), workspaceID); } } while (0)
;
2903 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0))) || !workspaceID || !GdkIsX11Display() || !mShell) {
2904 LOG(" MoveToWorkspace disabled, quit")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " MoveToWorkspace disabled, quit",
GetDebugTag().get()); } } while (0)
;
2905 return;
2906 }
2907
2908#ifdef MOZ_X111
2909 // Get the gdk window for this widget.
2910 GdkWindow* gdk_window = GetToplevelGdkWindow();
2911 if (!gdk_window) {
2912 LOG(" failed to get GdkWindow, quit.")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " failed to get GdkWindow, quit.",
GetDebugTag().get()); } } while (0)
;
2913 return;
2914 }
2915
2916 // This code is inspired by some found in the 'gxtuner' project.
2917 // https://github.com/brummer10/gxtuner/blob/792d453da0f3a599408008f0f1107823939d730d/deskpager.cpp#L50
2918 XEvent xevent;
2919 Display* xdisplay = gdk_x11_get_default_xdisplay();
2920 GdkScreen* screen = gdk_window_get_screen(gdk_window);
2921 Window root_win = GDK_WINDOW_XID(gdk_screen_get_root_window(screen))(gdk_x11_window_get_xid (gdk_screen_get_root_window(screen)));
2922 GdkDisplay* display = gdk_window_get_display(gdk_window);
2923 Atom type = gdk_x11_get_xatom_by_name_for_display(display, "_NET_WM_DESKTOP");
2924
2925 xevent.type = ClientMessage33;
2926 xevent.xclient.type = ClientMessage33;
2927 xevent.xclient.serial = 0;
2928 xevent.xclient.send_event = TRUE(!(0));
2929 xevent.xclient.display = xdisplay;
2930 xevent.xclient.window = GDK_WINDOW_XID(gdk_window)(gdk_x11_window_get_xid (gdk_window));
2931 xevent.xclient.message_type = type;
2932 xevent.xclient.format = 32;
2933 xevent.xclient.data.l[0] = workspaceID;
2934 xevent.xclient.data.l[1] = X11CurrentTime0L;
2935 xevent.xclient.data.l[2] = 0;
2936 xevent.xclient.data.l[3] = 0;
2937 xevent.xclient.data.l[4] = 0;
2938
2939 XSendEvent(xdisplay, root_win, FALSE(0),
2940 SubstructureNotifyMask(1L<<19) | SubstructureRedirectMask(1L<<20), &xevent);
2941
2942 XFlush(xdisplay);
2943 LOG(" moved to workspace")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " moved to workspace", GetDebugTag
().get()); } } while (0)
;
2944#endif
2945}
2946
2947void nsWindow::SetUserTimeAndStartupTokenForActivatedWindow() {
2948 nsGTKToolkit* toolkit = nsGTKToolkit::GetToolkit();
2949 if (!toolkit || MOZ_UNLIKELY(mWindowType == WindowType::Invisible)(__builtin_expect(!!(mWindowType == WindowType::Invisible), 0
))
) {
2950 return;
2951 }
2952
2953 mWindowActivationTokenFromEnv = toolkit->GetStartupToken();
2954 if (!mWindowActivationTokenFromEnv.IsEmpty()) {
2955 if (!GdkIsWaylandDisplay()) {
2956 gtk_window_set_startup_id(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))),
2957 mWindowActivationTokenFromEnv.get());
2958 // In the case of X11, the above call is all we need. For wayland we need
2959 // to keep the token around until we take it in
2960 // TransferFocusToWaylandWindow.
2961 mWindowActivationTokenFromEnv.Truncate();
2962 }
2963 } else if (uint32_t timestamp = toolkit->GetFocusTimestamp()) {
2964 // We don't have the data we need. Fall back to an
2965 // approximation ... using the timestamp of the remote command
2966 // being received as a guess for the timestamp of the user event
2967 // that triggered it.
2968 gdk_window_focus(GetToplevelGdkWindow(), timestamp);
2969 }
2970
2971 // If we used the startup ID, that already contains the focus timestamp;
2972 // we don't want to reuse the timestamp next time we raise the window
2973 toolkit->SetFocusTimestamp(0);
2974 toolkit->SetStartupToken(""_ns);
2975}
2976
2977/* static */
2978guint32 nsWindow::GetLastUserInputTime() {
2979 // gdk_x11_display_get_user_time/gtk_get_current_event_time tracks
2980 // button and key presses, DESKTOP_STARTUP_ID used to start the app,
2981 // drop events from external drags,
2982 // WM_DELETE_WINDOW delete events, but not usually mouse motion nor
2983 // button and key releases. Therefore use the most recent of
2984 // gdk_x11_display_get_user_time and the last time that we have seen.
2985#ifdef MOZ_X111
2986 GdkDisplay* gdkDisplay = gdk_display_get_default();
2987 guint32 timestamp = GdkIsX11Display(gdkDisplay)
2988 ? gdk_x11_display_get_user_time(gdkDisplay)
2989 : gtk_get_current_event_time();
2990#else
2991 guint32 timestamp = gtk_get_current_event_time();
2992#endif
2993
2994 if (sLastUserInputTime != GDK_CURRENT_TIME0L &&
2995 TimestampIsNewerThan(sLastUserInputTime, timestamp)) {
2996 return sLastUserInputTime;
2997 }
2998
2999 return timestamp;
3000}
3001
3002#ifdef MOZ_WAYLAND1
3003void nsWindow::FocusWaylandWindow(const char* aTokenID) {
3004 MOZ_DIAGNOSTIC_ASSERT(aTokenID)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aTokenID)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aTokenID))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aTokenID", "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 3004); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "aTokenID"
")"); do { *((volatile int*)__null) = 3004; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3005
3006 LOG("nsWindow::FocusWaylandWindow(%s)", aTokenID)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::FocusWaylandWindow(%s)",
GetDebugTag().get(), aTokenID); } } while (0)
;
3007 if (IsDestroyed()) {
3008 LOG(" already destroyed, quit.")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " already destroyed, quit.", GetDebugTag
().get()); } } while (0)
;
3009 return;
3010 }
3011 wl_surface* surface =
3012 mGdkWindow ? gdk_wayland_window_get_wl_surface(mGdkWindow) : nullptr;
3013 if (!surface) {
3014 LOG(" mGdkWindow is not visible, quit.")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " mGdkWindow is not visible, quit."
, GetDebugTag().get()); } } while (0)
;
3015 return;
3016 }
3017
3018 LOG(" requesting xdg-activation, surface ID %d",do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " requesting xdg-activation, surface ID %d"
, GetDebugTag().get(), wl_proxy_get_id((struct wl_proxy*)surface
)); } } while (0)
3019 wl_proxy_get_id((struct wl_proxy*)surface))do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " requesting xdg-activation, surface ID %d"
, GetDebugTag().get(), wl_proxy_get_id((struct wl_proxy*)surface
)); } } while (0)
;
3020 xdg_activation_v1* xdg_activation = WaylandDisplayGet()->GetXdgActivation();
3021 if (!xdg_activation) {
3022 return;
3023 }
3024 xdg_activation_v1_activate(xdg_activation, aTokenID, surface);
3025}
3026
3027// Transfer focus from gFocusWindow to aWindow and use xdg_activation
3028// protocol for it.
3029void nsWindow::TransferFocusToWaylandWindow(nsWindow* aWindow) {
3030 LOGW("nsWindow::TransferFocusToWaylandWindow(%p) gFocusWindow %p", aWindow,do { const ::mozilla::LogModule* moz_real_module = gWidgetLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "nsWindow::TransferFocusToWaylandWindow(%p) gFocusWindow %p"
, aWindow, gFocusWindow); } } while (0)
3031 gFocusWindow)do { const ::mozilla::LogModule* moz_real_module = gWidgetLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "nsWindow::TransferFocusToWaylandWindow(%p) gFocusWindow %p"
, aWindow, gFocusWindow); } } while (0)
;
3032 auto promise = mozilla::widget::RequestWaylandFocusPromise();
3033 if (NS_WARN_IF(!promise)NS_warn_if_impl(!promise, "!promise", "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 3033)
) {
3034 LOGW(" quit, failed to create TransferFocusToWaylandWindow [%p]", aWindow)do { const ::mozilla::LogModule* moz_real_module = gWidgetLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " quit, failed to create TransferFocusToWaylandWindow [%p]"
, aWindow); } } while (0)
;
3035 return;
3036 }
3037 promise->Then(
3038 GetMainThreadSerialEventTarget(), __func__,
3039 /* resolve */
3040 [window = RefPtr{aWindow}](nsCString token) {
3041 window->FocusWaylandWindow(token.get());
3042 },
3043 /* reject */
3044 [window = RefPtr{aWindow}](bool state) {
3045 LOGW("TransferFocusToWaylandWindow [%p] failed", window.get())do { const ::mozilla::LogModule* moz_real_module = gWidgetLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "TransferFocusToWaylandWindow [%p] failed"
, window.get()); } } while (0)
;
3046 });
3047}
3048#endif
3049
3050// Request activation of this window or give focus to this widget.
3051// aRaise means whether we should request activation of this widget's
3052// toplevel window.
3053//
3054// nsWindow::SetFocus(Raise::Yes) - Raise and give focus to toplevel window.
3055// nsWindow::SetFocus(Raise::No) - Give focus to this window.
3056void nsWindow::SetFocus(Raise aRaise, mozilla::dom::CallerType aCallerType) {
3057 LOG("nsWindow::SetFocus Raise %d\n", aRaise == Raise::Yes)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::SetFocus Raise %d\n", GetDebugTag
().get(), aRaise == Raise::Yes); } } while (0)
;
3058
3059 // Raise the window if someone passed in true and the prefs are
3060 // set properly.
3061 GtkWidget* toplevelWidget = gtk_widget_get_toplevel(GTK_WIDGET(mContainer)((((GtkWidget*) (void *) ((mContainer))))));
3062
3063 LOG(" gFocusWindow [%p]\n", gFocusWindow)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " gFocusWindow [%p]\n", GetDebugTag
().get(), gFocusWindow); } } while (0)
;
3064 LOG(" mContainer [%p]\n", GTK_WIDGET(mContainer))do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " mContainer [%p]\n", GetDebugTag(
).get(), ((((GtkWidget*) (void *) ((mContainer)))))); } } while
(0)
;
3065 LOG(" Toplevel widget [%p]\n", toplevelWidget)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " Toplevel widget [%p]\n", GetDebugTag
().get(), toplevelWidget); } } while (0)
;
3066
3067 // Make sure that our owning widget has focus. If it doesn't try to
3068 // grab it. Note that we don't set our focus flag in this case.
3069 if (StaticPrefs::mozilla_widget_raise_on_setfocus_AtStartup() &&
3070 aRaise == Raise::Yes && toplevelWidget &&
3071 !gtk_widget_has_focus(toplevelWidget)) {
3072 if (gtk_widget_get_visible(mShell)) {
3073 LOG(" toplevel is not focused")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " toplevel is not focused", GetDebugTag
().get()); } } while (0)
;
3074 gdk_window_show_unraised(GetToplevelGdkWindow());
3075 // Unset the urgency hint if possible.
3076 SetUrgencyHint(mShell, false);
3077 }
3078 }
3079
3080 RefPtr<nsWindow> toplevelWindow = get_window_for_gtk_widget(toplevelWidget);
3081 if (!toplevelWindow) {
3082 LOG(" missing toplevel nsWindow, quit\n")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " missing toplevel nsWindow, quit\n"
, GetDebugTag().get()); } } while (0)
;
3083 return;
3084 }
3085
3086 if (aRaise == Raise::Yes) {
3087 // means request toplevel activation.
3088
3089 // This is asynchronous. If and when the window manager accepts the request,
3090 // then the focus widget will get a focus-in-event signal.
3091 if (StaticPrefs::mozilla_widget_raise_on_setfocus_AtStartup() &&
3092 toplevelWindow->mIsShown && toplevelWindow->mShell &&
3093 !gtk_window_is_active(GTK_WINDOW(toplevelWindow->mShell)((((GtkWindow*) (void *) ((toplevelWindow->mShell))))))) {
3094 LOG(" toplevel is visible but not active, requesting activation [%p]",do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " toplevel is visible but not active, requesting activation [%p]"
, GetDebugTag().get(), toplevelWindow.get()); } } while (0)
3095 toplevelWindow.get())do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " toplevel is visible but not active, requesting activation [%p]"
, GetDebugTag().get(), toplevelWindow.get()); } } while (0)
;
3096
3097 // Take the time here explicitly for the call below.
3098 const uint32_t timestamp = [&] {
3099 if (nsGTKToolkit* toolkit = nsGTKToolkit::GetToolkit()) {
3100 if (uint32_t t = toolkit->GetFocusTimestamp()) {
3101 toolkit->SetFocusTimestamp(0);
3102 return t;
3103 }
3104 }
3105 return GetLastUserInputTime();
3106 }();
3107
3108 toplevelWindow->SetUserTimeAndStartupTokenForActivatedWindow();
3109 gtk_window_present_with_time(GTK_WINDOW(toplevelWindow->mShell)((((GtkWindow*) (void *) ((toplevelWindow->mShell))))),
3110 timestamp);
3111
3112#ifdef MOZ_WAYLAND1
3113 if (GdkIsWaylandDisplay()) {
3114 auto existingToken =
3115 std::move(toplevelWindow->mWindowActivationTokenFromEnv);
3116 if (!existingToken.IsEmpty()) {
3117 LOG(" has existing activation token.")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " has existing activation token.",
GetDebugTag().get()); } } while (0)
;
3118 toplevelWindow->FocusWaylandWindow(existingToken.get());
3119 } else {
3120 LOG(" missing activation token, try to transfer from focused "do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " missing activation token, try to transfer from focused "
"window", GetDebugTag().get()); } } while (0)
3121 "window")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " missing activation token, try to transfer from focused "
"window", GetDebugTag().get()); } } while (0)
;
3122 TransferFocusToWaylandWindow(toplevelWindow);
3123 }
3124 }
3125#endif
3126 }
3127 return;
3128 }
3129
3130 // aRaise == No means that keyboard events should be dispatched from this
3131 // widget.
3132
3133 // Ensure GTK_WIDGET(mContainer) is the focused GtkWidget within its toplevel
3134 // window.
3135 //
3136 // For WindowType::Popup, this GtkWidget may not actually be the one that
3137 // receives the key events as it may be the parent window that is active.
3138 if (!gtk_widget_is_focus(GTK_WIDGET(mContainer)((((GtkWidget*) (void *) ((mContainer))))))) {
3139 // This is synchronous. It takes focus from a plugin or from a widget
3140 // in an embedder. The focus manager already knows that this window
3141 // is active so gBlockActivateEvent avoids another (unnecessary)
3142 // activate notification.
3143 gBlockActivateEvent = true;
3144 gtk_widget_grab_focus(GTK_WIDGET(mContainer)((((GtkWidget*) (void *) ((mContainer))))));
3145 gBlockActivateEvent = false;
3146 }
3147
3148 // If this is the widget that already has focus, return.
3149 if (gFocusWindow == this) {
3150 LOG(" already have focus")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " already have focus", GetDebugTag
().get()); } } while (0)
;
3151 return;
3152 }
3153
3154 // Set this window to be the focused child window
3155 gFocusWindow = this;
3156
3157 if (mIMContext) {
3158 mIMContext->OnFocusWindow(this);
3159 }
3160
3161 LOG(" widget now has focus in SetFocus()")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " widget now has focus in SetFocus()"
, GetDebugTag().get()); } } while (0)
;
3162}
3163
3164void nsWindow::ResetScreenBounds() {
3165 mGdkWindowOrigin.reset();
3166 mGdkWindowRootOrigin.reset();
3167}
3168
3169LayoutDeviceIntRect nsWindow::GetScreenBounds() {
3170 if (!mGdkWindow) {
3171 return mBounds;
3172 }
3173
3174 const LayoutDeviceIntPoint origin = [&] {
3175 GdkPoint origin;
3176
3177 if (mGdkWindowRootOrigin.isSome()) {
3178 origin = mGdkWindowRootOrigin.value();
3179 } else {
3180 gdk_window_get_root_origin(mGdkWindow, &origin.x, &origin.y);
3181 mGdkWindowRootOrigin = Some(origin);
3182 }
3183
3184 // Workaround for https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/4820
3185 // Bug 1775017 Gtk < 3.24.35 returns scaled values for
3186 // override redirected window on X11.
3187 if (gtk_check_version(3, 24, 35) != nullptr && GdkIsX11Display() &&
3188 gdk_window_get_window_type(mGdkWindow) == GDK_WINDOW_TEMP) {
3189 return LayoutDeviceIntPoint(origin.x, origin.y);
3190 }
3191 return GdkPointToDevicePixels(origin);
3192 }();
3193
3194 // mBounds.Size() is the window bounds, not the window-manager frame
3195 // bounds (bug 581863). gdk_window_get_frame_extents would give the
3196 // frame bounds, but mBounds.Size() is returned here for consistency
3197 // with Resize.
3198 const LayoutDeviceIntRect rect(origin, mBounds.Size());
3199#if MOZ_LOGGING1
3200 if (MOZ_LOG_TEST(IsPopup() ? gWidgetPopupLog : gWidgetLog,(__builtin_expect(!!(mozilla::detail::log_test(IsPopup() ? gWidgetPopupLog
: gWidgetLog, LogLevel::Verbose)), 0))
3201 LogLevel::Verbose)(__builtin_expect(!!(mozilla::detail::log_test(IsPopup() ? gWidgetPopupLog
: gWidgetLog, LogLevel::Verbose)), 0))
) {
3202 gint scale = GdkCeiledScaleFactor();
3203 if (mLastLoggedScale != scale || !(mLastLoggedBoundSize == rect)) {
3204 mLastLoggedScale = scale;
3205 mLastLoggedBoundSize = rect;
3206 LOG("GetScreenBounds %d,%d -> %d x %d, unscaled %d,%d -> %d x %d\n",do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "GetScreenBounds %d,%d -> %d x %d, unscaled %d,%d -> %d x %d\n"
, GetDebugTag().get(), rect.x, rect.y, rect.width, rect.height
, rect.x / scale, rect.y / scale, rect.width / scale, rect.height
/ scale); } } while (0)
3207 rect.x, rect.y, rect.width, rect.height, rect.x / scale,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "GetScreenBounds %d,%d -> %d x %d, unscaled %d,%d -> %d x %d\n"
, GetDebugTag().get(), rect.x, rect.y, rect.width, rect.height
, rect.x / scale, rect.y / scale, rect.width / scale, rect.height
/ scale); } } while (0)
3208 rect.y / scale, rect.width / scale, rect.height / scale)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "GetScreenBounds %d,%d -> %d x %d, unscaled %d,%d -> %d x %d\n"
, GetDebugTag().get(), rect.x, rect.y, rect.width, rect.height
, rect.x / scale, rect.y / scale, rect.width / scale, rect.height
/ scale); } } while (0)
;
3209 }
3210 }
3211#endif
3212 return rect;
3213}
3214
3215LayoutDeviceIntSize nsWindow::GetClientSize() { return mBounds.Size(); }
3216
3217LayoutDeviceIntRect nsWindow::GetClientBounds() {
3218 // GetBounds returns a rect whose top left represents the top left of the
3219 // outer bounds, but whose width/height represent the size of the inner
3220 // bounds (which is messed up).
3221 LayoutDeviceIntRect rect = GetBounds();
3222 rect.MoveBy(GetClientOffset());
3223 return rect;
3224}
3225
3226void nsWindow::RecomputeClientOffset(bool aNotify) {
3227 if (!IsTopLevelWindowType()) {
3228 return;
3229 }
3230
3231 auto oldOffset = mClientOffset;
3232
3233 mClientOffset = WidgetToScreenOffset() - mBounds.TopLeft();
3234
3235 if (aNotify && mClientOffset != oldOffset) {
3236 // Send a WindowMoved notification. This ensures that BrowserParent picks up
3237 // the new client offset and sends it to the child process if appropriate.
3238 NotifyWindowMoved(mBounds.x, mBounds.y);
3239 }
3240}
3241
3242gboolean nsWindow::OnPropertyNotifyEvent(GtkWidget* aWidget,
3243 GdkEventProperty* aEvent) {
3244 if (aEvent->atom == gdk_atom_intern("_NET_FRAME_EXTENTS", FALSE(0))) {
3245 ResetScreenBounds();
3246 RecomputeClientOffset(/* aNotify = */ true);
3247 return FALSE(0);
3248 }
3249 if (!mGdkWindow) {
3250 return FALSE(0);
3251 }
3252#ifdef MOZ_X111
3253 if (GetCurrentTimeGetter()->PropertyNotifyHandler(aWidget, aEvent)) {
3254 return TRUE(!(0));
3255 }
3256#endif
3257 return FALSE(0);
3258}
3259
3260static GdkCursor* GetCursorForImage(const nsIWidget::Cursor& aCursor,
3261 int32_t aWidgetScaleFactor) {
3262 if (!aCursor.IsCustom()) {
3263 return nullptr;
3264 }
3265 nsIntSize size = nsIWidget::CustomCursorSize(aCursor);
3266
3267 // NOTE: GTK only allows integer scale factors, so we ceil to the larger scale
3268 // factor and then tell gtk to scale it down. We ensure to scale at least to
3269 // the GDK scale factor, so that cursors aren't downsized in HiDPI on wayland,
3270 // see bug 1707533.
3271 int32_t gtkScale = std::max(
3272 aWidgetScaleFactor, int32_t(std::ceil(std::max(aCursor.mResolution.mX,
3273 aCursor.mResolution.mY))));
3274
3275 // Reject cursors greater than 128 pixels in some direction, to prevent
3276 // spoofing.
3277 // XXX ideally we should rescale. Also, we could modify the API to
3278 // allow trusted content to set larger cursors.
3279 //
3280 // TODO(emilio, bug 1445844): Unify the solution for this with other
3281 // platforms.
3282 if (size.width > 128 || size.height > 128) {
3283 return nullptr;
3284 }
3285
3286 nsIntSize rasterSize = size * gtkScale;
3287 RefPtr<GdkPixbuf> pixbuf =
3288 nsImageToPixbuf::ImageToPixbuf(aCursor.mContainer, Some(rasterSize));
3289 if (!pixbuf) {
3290 return nullptr;
3291 }
3292
3293 // Looks like all cursors need an alpha channel (tested on Gtk 2.4.4). This
3294 // is of course not documented anywhere...
3295 // So add one if there isn't one yet
3296 if (!gdk_pixbuf_get_has_alpha(pixbuf)) {
3297 RefPtr<GdkPixbuf> alphaBuf =
3298 dont_AddRef(gdk_pixbuf_add_alpha(pixbuf, FALSE(0), 0, 0, 0));
3299 pixbuf = std::move(alphaBuf);
3300 if (!pixbuf) {
3301 return nullptr;
3302 }
3303 }
3304
3305 cairo_surface_t* surface =
3306 gdk_cairo_surface_create_from_pixbuf(pixbuf, gtkScale, nullptr);
3307 if (!surface) {
3308 return nullptr;
3309 }
3310
3311 auto CleanupSurface =
3312 MakeScopeExit([&]() { cairo_surface_destroy(surface); });
3313
3314 return gdk_cursor_new_from_surface(gdk_display_get_default(), surface,
3315 aCursor.mHotspotX, aCursor.mHotspotY);
3316}
3317
3318void nsWindow::SetCursor(const Cursor& aCursor) {
3319 if (mWidgetCursorLocked || !mGdkWindow) {
3320 return;
3321 }
3322
3323 // Only change cursor if it's actually been changed
3324 if (!mUpdateCursor && mCursor == aCursor) {
3325 return;
3326 }
3327
3328 mUpdateCursor = false;
3329 mCursor = aCursor;
3330
3331 // Try to set the cursor image first, and fall back to the numeric cursor.
3332 GdkCursor* imageCursor = nullptr;
3333 if (mCustomCursorAllowed) {
3334 imageCursor = GetCursorForImage(aCursor, GdkCeiledScaleFactor());
3335 }
3336
3337 // When using a custom cursor, clear the cursor first using eCursor_none, in
3338 // order to work around https://gitlab.gnome.org/GNOME/gtk/-/issues/6242
3339 GdkCursor* nonImageCursor =
3340 get_gtk_cursor(imageCursor ? eCursor_none : aCursor.mDefaultCursor);
3341 auto CleanupCursor = mozilla::MakeScopeExit([&]() {
3342 // get_gtk_cursor returns a weak reference, which we shouldn't unref.
3343 if (imageCursor) {
3344 g_object_unref(imageCursor);
3345 }
3346 });
3347
3348 gdk_window_set_cursor(mGdkWindow, nonImageCursor);
3349 if (imageCursor) {
3350 gdk_window_set_cursor(mGdkWindow, imageCursor);
3351 }
3352}
3353
3354void nsWindow::Invalidate(const LayoutDeviceIntRect& aRect) {
3355 if (!mGdkWindow) {
3356 return;
3357 }
3358
3359 GdkRectangle rect = DevicePixelsToGdkRectRoundOut(aRect);
3360 gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE(0));
3361
3362 LOG("Invalidate (rect): %d %d %d %d\n", rect.x, rect.y, rect.width,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "Invalidate (rect): %d %d %d %d\n",
GetDebugTag().get(), rect.x, rect.y, rect.width, rect.height
); } } while (0)
3363 rect.height)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "Invalidate (rect): %d %d %d %d\n",
GetDebugTag().get(), rect.x, rect.y, rect.width, rect.height
); } } while (0)
;
3364}
3365
3366void* nsWindow::GetNativeData(uint32_t aDataType) {
3367 switch (aDataType) {
3368 case NS_NATIVE_WINDOW0:
3369 case NS_NATIVE_WIDGET3: {
3370 return mGdkWindow;
3371 }
3372
3373 case NS_NATIVE_SHELLWIDGET10:
3374 return GetToplevelWidget();
3375
3376 case NS_NATIVE_WINDOW_WEBRTC_DEVICE_ID15:
3377 if (!mGdkWindow) {
3378 return nullptr;
3379 }
3380#ifdef MOZ_X111
3381 if (GdkIsX11Display()) {
3382 return (void*)GDK_WINDOW_XID(gdk_window_get_toplevel(mGdkWindow))(gdk_x11_window_get_xid (gdk_window_get_toplevel(mGdkWindow))
)
;
3383 }
3384#endif
3385 NS_WARNING(NS_DebugBreak(NS_DEBUG_WARNING, "nsWindow::GetNativeData(): NS_NATIVE_WINDOW_WEBRTC_DEVICE_ID is not "
"handled on Wayland!", nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 3387)
3386 "nsWindow::GetNativeData(): NS_NATIVE_WINDOW_WEBRTC_DEVICE_ID is not "NS_DebugBreak(NS_DEBUG_WARNING, "nsWindow::GetNativeData(): NS_NATIVE_WINDOW_WEBRTC_DEVICE_ID is not "
"handled on Wayland!", nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 3387)
3387 "handled on Wayland!")NS_DebugBreak(NS_DEBUG_WARNING, "nsWindow::GetNativeData(): NS_NATIVE_WINDOW_WEBRTC_DEVICE_ID is not "
"handled on Wayland!", nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 3387)
;
3388 return nullptr;
3389 case NS_RAW_NATIVE_IME_CONTEXT14: {
3390 void* pseudoIMEContext = GetPseudoIMEContext();
3391 if (pseudoIMEContext) {
3392 return pseudoIMEContext;
3393 }
3394 // If IME context isn't available on this widget, we should set |this|
3395 // instead of nullptr.
3396 if (!mIMContext) {
3397 return this;
3398 }
3399 return mIMContext.get();
3400 }
3401 case NS_NATIVE_OPENGL_CONTEXT12:
3402 return nullptr;
3403 case NS_NATIVE_EGL_WINDOW106: {
3404 // On X11 we call it:
3405 // 1) If window is mapped on OnMap() by nsWindow::ResumeCompositorImpl(),
3406 // new EGLSurface/XWindow is created.
3407 // 2) If window is hidden on OnUnmap(), we replace EGLSurface/XWindow
3408 // by offline surface and release XWindow.
3409
3410 // On Wayland it:
3411 // 1) If window is mapped on OnMap(), we request frame callback
3412 // at MozContainer. If we get frame callback at MozContainer,
3413 // nsWindow::ResumeCompositorImpl() is called from it
3414 // and EGLSurface/wl_surface is created.
3415 // 2) If window is hidden on OnUnmap(), we replace EGLSurface/wl_surface
3416 // by offline surface and release XWindow.
3417
3418 // If nsWindow is already destroyed, don't try to get EGL window at all,
3419 // we're going to be deleted anyway.
3420 MutexAutoLock lock(mWindowVisibilityMutex);
3421 void* eglWindow = nullptr;
3422 if (mIsMapped && !mIsDestroyed) {
3423#ifdef MOZ_X111
3424 if (GdkIsX11Display()) {
3425 eglWindow = (void*)GDK_WINDOW_XID(mGdkWindow)(gdk_x11_window_get_xid (mGdkWindow));
3426 }
3427#endif
3428#ifdef MOZ_WAYLAND1
3429 if (GdkIsWaylandDisplay()) {
3430 eglWindow = moz_container_wayland_get_egl_window(
3431 mContainer, FractionalScaleFactor());
3432 }
3433#endif
3434 }
3435 LOG("Get NS_NATIVE_EGL_WINDOW mGdkWindow %p returned eglWindow %p",do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "Get NS_NATIVE_EGL_WINDOW mGdkWindow %p returned eglWindow %p"
, GetDebugTag().get(), mGdkWindow, eglWindow); } } while (0)
3436 mGdkWindow, eglWindow)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "Get NS_NATIVE_EGL_WINDOW mGdkWindow %p returned eglWindow %p"
, GetDebugTag().get(), mGdkWindow, eglWindow); } } while (0)
;
3437 return eglWindow;
3438 }
3439 default:
3440 NS_WARNING("nsWindow::GetNativeData called with bad value")NS_DebugBreak(NS_DEBUG_WARNING, "nsWindow::GetNativeData called with bad value"
, nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 3440)
;
3441 return nullptr;
3442 }
3443}
3444
3445nsresult nsWindow::SetTitle(const nsAString& aTitle) {
3446 if (!mShell) {
3447 return NS_OK;
3448 }
3449
3450 // convert the string into utf8 and set the title.
3451#define UTF8_FOLLOWBYTE(ch)(((ch) & 0xC0) == 0x80) (((ch) & 0xC0) == 0x80)
3452 NS_ConvertUTF16toUTF8 titleUTF8(aTitle);
3453 if (titleUTF8.Length() > NS_WINDOW_TITLE_MAX_LENGTH4095) {
3454 // Truncate overlong titles (bug 167315). Make sure we chop after a
3455 // complete sequence by making sure the next char isn't a follow-byte.
3456 uint32_t len = NS_WINDOW_TITLE_MAX_LENGTH4095;
3457 while (UTF8_FOLLOWBYTE(titleUTF8[len])(((titleUTF8[len]) & 0xC0) == 0x80)) --len;
3458 titleUTF8.Truncate(len);
3459 }
3460 gtk_window_set_title(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))), (const char*)titleUTF8.get());
3461
3462 return NS_OK;
3463}
3464
3465void nsWindow::SetIcon(const nsAString& aIconSpec) {
3466 if (!mShell) {
3467 return;
3468 }
3469
3470 nsAutoCString iconName;
3471
3472 if (aIconSpec.EqualsLiteral("default")) {
3473 nsAutoString brandName;
3474 WidgetUtils::GetBrandShortName(brandName);
3475 if (brandName.IsEmpty()) {
3476 brandName.AssignLiteral(u"Mozilla");
3477 }
3478 AppendUTF16toUTF8(brandName, iconName);
3479 ToLowerCase(iconName);
3480 } else {
3481 AppendUTF16toUTF8(aIconSpec, iconName);
3482 }
3483
3484 nsCOMPtr<nsIFile> iconFile;
3485 nsAutoCString path;
3486
3487 gint* iconSizes = gtk_icon_theme_get_icon_sizes(gtk_icon_theme_get_default(),
3488 iconName.get());
3489 bool foundIcon = (iconSizes[0] != 0);
3490 g_free(iconSizes);
3491
3492 if (!foundIcon) {
3493 // Look for icons with the following suffixes appended to the base name
3494 // The last two entries (for the old XPM format) will be ignored unless
3495 // no icons are found using other suffixes. XPM icons are deprecated.
3496
3497 const char16_t extensions[9][8] = {u".png", u"16.png", u"32.png",
3498 u"48.png", u"64.png", u"128.png",
3499 u"256.png", u".xpm", u"16.xpm"};
3500
3501 for (uint32_t i = 0; i < ArrayLength(extensions); i++) {
3502 // Don't bother looking for XPM versions if we found a PNG.
3503 if (i == ArrayLength(extensions) - 2 && foundIcon) break;
3504
3505 ResolveIconName(aIconSpec, nsDependentString(extensions[i]),
3506 getter_AddRefs(iconFile));
3507 if (iconFile) {
3508 iconFile->GetNativePath(path);
3509 GdkPixbuf* icon = gdk_pixbuf_new_from_file(path.get(), nullptr);
3510 if (icon) {
3511 gtk_icon_theme_add_builtin_icon(iconName.get(),
3512 gdk_pixbuf_get_height(icon), icon);
3513 g_object_unref(icon);
3514 foundIcon = true;
3515 }
3516 }
3517 }
3518 }
3519
3520 // leave the default icon intact if no matching icons were found
3521 if (foundIcon) {
3522 gtk_window_set_icon_name(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))), iconName.get());
3523 }
3524}
3525
3526/* TODO(bug 1655924): gdk_window_get_origin is can block waiting for the X
3527 server for a long time, we would like to use the implementation below
3528 instead. However, removing the synchronous x server queries causes a race
3529 condition to surface, causing issues such as bug 1652743 and bug 1653711.
3530
3531
3532 This code can be used instead of gdk_window_get_origin() but it cuases
3533 such issues:
3534
3535 *aX = 0;
3536 *aY = 0;
3537 if (!aWindow) {
3538 return;
3539 }
3540
3541 GdkWindow* current = aWindow;
3542 while (GdkWindow* parent = gdk_window_get_parent(current)) {
3543 if (parent == current) {
3544 break;
3545 }
3546
3547 int x = 0;
3548 int y = 0;
3549 gdk_window_get_position(current, &x, &y);
3550 *aX += x;
3551 *aY += y;
3552
3553 current = parent;
3554 }
3555*/
3556LayoutDeviceIntPoint nsWindow::WidgetToScreenOffset() {
3557 // Don't use gdk_window_get_origin() on wl_subsurface Wayland popups
3558 // https://gitlab.gnome.org/GNOME/gtk/-/issues/5287
3559 if (IsWaylandPopup() && !mPopupUseMoveToRect) {
3560 return mBounds.TopLeft();
3561 }
3562
3563 GdkPoint origin{};
3564 if (mGdkWindowOrigin.isSome()) {
3565 origin = mGdkWindowOrigin.value();
3566 } else if (mGdkWindow) {
3567 gdk_window_get_origin(mGdkWindow, &origin.x, &origin.y);
3568 mGdkWindowOrigin = Some(origin);
3569 }
3570
3571 return GdkPointToDevicePixels(origin);
3572}
3573
3574void nsWindow::CaptureRollupEvents(bool aDoCapture) {
3575 LOG("CaptureRollupEvents(%d)\n", aDoCapture)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "CaptureRollupEvents(%d)\n", GetDebugTag
().get(), aDoCapture); } } while (0)
;
3576 if (mIsDestroyed) {
3577 return;
3578 }
3579
3580 static constexpr auto kCaptureEventsMask =
3581 GdkEventMask(GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
3582 GDK_POINTER_MOTION_MASK | GDK_TOUCH_MASK);
3583
3584 static bool sSystemNeedsPointerGrab = [&] {
3585 if (GdkIsWaylandDisplay()) {
3586 return false;
3587 }
3588 // We only need to grab the pointer for X servers that move the focus with
3589 // the pointer (like twm, sawfish...). Since we roll up popups on focus out,
3590 // not grabbing the pointer triggers rollup when the mouse enters the popup
3591 // and leaves the main window, see bug 1807482.
3592 //
3593 // FVWM is also affected but less severely: the pointer can enter the
3594 // popup, but if it briefly moves out of the popup and over the main window
3595 // then we see a focus change and roll up the popup.
3596 //
3597 // We don't do it for most common desktops, if only because it causes X11
3598 // crashes like bug 1607713.
3599 const auto& desktop = GetDesktopEnvironmentIdentifier();
3600 return desktop.EqualsLiteral("twm") || desktop.EqualsLiteral("sawfish") ||
3601 StringBeginsWith(desktop, "fvwm"_ns);
3602 }();
3603
3604 const bool grabPointer = [] {
3605 switch (StaticPrefs::widget_gtk_grab_pointer()) {
3606 case 0:
3607 return false;
3608 case 1:
3609 return true;
3610 default:
3611 return sSystemNeedsPointerGrab;
3612 }
3613 }();
3614
3615 if (!grabPointer) {
3616 return;
3617 }
3618
3619 mNeedsToRetryCapturingMouse = false;
3620 if (aDoCapture) {
3621 if (mIsDragPopup || DragInProgress()) {
3622 // Don't add a grab if a drag is in progress, or if the widget is a drag
3623 // feedback popup. (panels with type="drag").
3624 return;
3625 }
3626
3627 if (!mHasMappedToplevel) {
3628 // On X, capturing an unmapped window is pointless (returns
3629 // GDK_GRAB_NOT_VIEWABLE). Avoid the X server round-trip and just retry
3630 // when we're mapped.
3631 mNeedsToRetryCapturingMouse = true;
3632 return;
3633 }
3634
3635 // gdk_pointer_grab is deprecated in favor of gdk_device_grab, but that
3636 // causes a strange bug on X11, most obviously with nested popup menus:
3637 // we somehow take the pointer position relative to the top left of the
3638 // outer menu and use it as if it were relative to the submenu. This
3639 // doesn't happen with gdk_pointer_grab even though the code is very
3640 // similar. See the video attached to bug 1750721 for a demonstration,
3641 // and see also bug 1820542 for when the same thing happened with
3642 // another attempt to use gdk_device_grab.
3643 //
3644 // (gdk_device_grab is deprecated in favor of gdk_seat_grab as of 3.20,
3645 // but at the time of this writing we still support older versions of
3646 // GTK 3.)
3647 GdkGrabStatus status =
3648 gdk_pointer_grab(GetToplevelGdkWindow(),
3649 /* owner_events = */ true, kCaptureEventsMask,
3650 /* confine_to = */ nullptr,
3651 /* cursor = */ nullptr, GetLastUserInputTime());
3652 Unused << NS_WARN_IF(status != GDK_GRAB_SUCCESS)NS_warn_if_impl(status != GDK_GRAB_SUCCESS, "status != GDK_GRAB_SUCCESS"
, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 3652)
;
3653 LOG(" > pointer grab with status %d", int(status))do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " > pointer grab with status %d"
, GetDebugTag().get(), int(status)); } } while (0)
;
3654 gtk_grab_add(GTK_WIDGET(mContainer)((((GtkWidget*) (void *) ((mContainer))))));
3655 } else {
3656 // There may not have been a drag in process when aDoCapture was set,
3657 // so make sure to remove any added grab. This is a no-op if the grab
3658 // was not added to this widget.
3659 gtk_grab_remove(GTK_WIDGET(mContainer)((((GtkWidget*) (void *) ((mContainer))))));
3660 gdk_pointer_ungrab(GetLastUserInputTime());
3661 }
3662}
3663
3664nsresult nsWindow::GetAttention(int32_t aCycleCount) {
3665 LOG("nsWindow::GetAttention")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::GetAttention", GetDebugTag
().get()); } } while (0)
;
3666
3667 GtkWidget* top_window = GetToplevelWidget();
3668 GtkWidget* top_focused_window =
3669 gFocusWindow ? gFocusWindow->GetToplevelWidget() : nullptr;
3670
3671 // Don't get attention if the window is focused anyway.
3672 if (top_window && (gtk_widget_get_visible(top_window)) &&
3673 top_window != top_focused_window) {
3674 SetUrgencyHint(top_window, true);
3675 }
3676
3677 return NS_OK;
3678}
3679
3680bool nsWindow::HasPendingInputEvent() {
3681 // This sucks, but gtk/gdk has no way to answer the question we want while
3682 // excluding paint events, and there's no X API that will let us peek
3683 // without blocking or removing. To prevent event reordering, peek
3684 // anything except expose events. Reordering expose and others should be
3685 // ok, hopefully.
3686 bool haveEvent = false;
3687#ifdef MOZ_X111
3688 XEvent ev;
3689 if (GdkIsX11Display()) {
3690 Display* display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default())(gdk_x11_display_get_xdisplay (gdk_display_get_default()));
3691 haveEvent = XCheckMaskEvent(
3692 display,
3693 KeyPressMask(1L<<0) | KeyReleaseMask(1L<<1) | ButtonPressMask(1L<<2) | ButtonReleaseMask(1L<<3) |
3694 EnterWindowMask(1L<<4) | LeaveWindowMask(1L<<5) | PointerMotionMask(1L<<6) |
3695 PointerMotionHintMask(1L<<7) | Button1MotionMask(1L<<8) | Button2MotionMask(1L<<9) |
3696 Button3MotionMask(1L<<10) | Button4MotionMask(1L<<11) | Button5MotionMask(1L<<12) |
3697 ButtonMotionMask(1L<<13) | KeymapStateMask(1L<<14) | VisibilityChangeMask(1L<<16) |
3698 StructureNotifyMask(1L<<17) | ResizeRedirectMask(1L<<18) | SubstructureNotifyMask(1L<<19) |
3699 SubstructureRedirectMask(1L<<20) | FocusChangeMask(1L<<21) | PropertyChangeMask(1L<<22) |
3700 ColormapChangeMask(1L<<23) | OwnerGrabButtonMask(1L<<24),
3701 &ev);
3702 if (haveEvent) {
3703 XPutBackEvent(display, &ev);
3704 }
3705 }
3706#endif
3707 return haveEvent;
3708}
3709
3710#ifdef cairo_copy_clip_rectangle_list
3711# error "Looks like we're including Mozilla's cairo instead of system cairo"
3712#endif
3713static bool ExtractExposeRegion(LayoutDeviceIntRegion& aRegion, cairo_t* cr) {
3714 cairo_rectangle_list_t* rects = cairo_copy_clip_rectangle_list(cr);
3715 if (rects->status != CAIRO_STATUS_SUCCESS) {
3716 NS_WARNING("Failed to obtain cairo rectangle list.")NS_DebugBreak(NS_DEBUG_WARNING, "Failed to obtain cairo rectangle list."
, nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 3716)
;
3717 return false;
3718 }
3719
3720 for (int i = 0; i < rects->num_rectangles; i++) {
3721 const cairo_rectangle_t& r = rects->rectangles[i];
3722 aRegion.Or(aRegion,
3723 LayoutDeviceIntRect::Truncate((float)r.x, (float)r.y,
3724 (float)r.width, (float)r.height));
3725 }
3726
3727 cairo_rectangle_list_destroy(rects);
3728 return true;
3729}
3730
3731#ifdef MOZ_WAYLAND1
3732void nsWindow::CreateCompositorVsyncDispatcher() {
3733 LOG_VSYNC("nsWindow::CreateCompositorVsyncDispatcher()")do { const ::mozilla::LogModule* moz_real_module = gWidgetVsync
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "nsWindow::CreateCompositorVsyncDispatcher()"
); } } while (0)
;
3734 if (!mWaylandVsyncSource) {
3735 LOG_VSYNC(do { const ::mozilla::LogModule* moz_real_module = gWidgetVsync
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " mWaylandVsyncSource is missing, create "
"nsBaseWidget::CompositorVsyncDispatcher()"); } } while (0)
3736 " mWaylandVsyncSource is missing, create "do { const ::mozilla::LogModule* moz_real_module = gWidgetVsync
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " mWaylandVsyncSource is missing, create "
"nsBaseWidget::CompositorVsyncDispatcher()"); } } while (0)
3737 "nsBaseWidget::CompositorVsyncDispatcher()")do { const ::mozilla::LogModule* moz_real_module = gWidgetVsync
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " mWaylandVsyncSource is missing, create "
"nsBaseWidget::CompositorVsyncDispatcher()"); } } while (0)
;
3738 nsBaseWidget::CreateCompositorVsyncDispatcher();
3739 return;
3740 }
3741 if (!mCompositorVsyncDispatcherLock) {
3742 mCompositorVsyncDispatcherLock =
3743 MakeUnique<Mutex>("mCompositorVsyncDispatcherLock");
3744 }
3745 MutexAutoLock lock(*mCompositorVsyncDispatcherLock);
3746 if (!mCompositorVsyncDispatcher) {
3747 LOG_VSYNC(" create CompositorVsyncDispatcher()")do { const ::mozilla::LogModule* moz_real_module = gWidgetVsync
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " create CompositorVsyncDispatcher()"
); } } while (0)
;
3748 mCompositorVsyncDispatcher =
3749 new CompositorVsyncDispatcher(mWaylandVsyncDispatcher);
3750 }
3751}
3752#endif
3753
3754void nsWindow::RequestRepaint(LayoutDeviceIntRegion& aRepaintRegion) {
3755 WindowRenderer* renderer = GetWindowRenderer();
3756 WebRenderLayerManager* layerManager = renderer->AsWebRender();
3757 KnowsCompositor* knowsCompositor = renderer->AsKnowsCompositor();
3758
3759 if (knowsCompositor && layerManager && mCompositorSession) {
3760 LOG("nsWindow::RequestRepaint()")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::RequestRepaint()", GetDebugTag
().get()); } } while (0)
;
3761
3762 if (!mConfiguredClearColor && !IsPopup()) {
3763 layerManager->WrBridge()->SendSetDefaultClearColor(LookAndFeel::Color(
3764 LookAndFeel::ColorID::Window, PreferenceSheet::ColorSchemeForChrome(),
3765 LookAndFeel::UseStandins::No));
3766 mConfiguredClearColor = true;
3767 }
3768
3769 // We need to paint to the screen even if nothing changed, since if we
3770 // don't have a compositing window manager, our pixels could be stale.
3771 layerManager->SetNeedsComposite(true);
3772 layerManager->SendInvalidRegion(aRepaintRegion.ToUnknownRegion());
3773 }
3774}
3775
3776gboolean nsWindow::OnExposeEvent(cairo_t* cr) {
3777 LOG("nsWindow::OnExposeEvent GdkWindow [%p] XID [0x%lx]", mGdkWindow,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::OnExposeEvent GdkWindow [%p] XID [0x%lx]"
, GetDebugTag().get(), mGdkWindow, GetX11Window()); } } while
(0)
3778 GetX11Window())do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::OnExposeEvent GdkWindow [%p] XID [0x%lx]"
, GetDebugTag().get(), mGdkWindow, GetX11Window()); } } while
(0)
;
3779
3780 // This might destroy us.
3781 NotifyOcclusionState(OcclusionState::VISIBLE);
3782 if (mIsDestroyed) {
3783 LOG("destroyed after NotifyOcclusionState()")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "destroyed after NotifyOcclusionState()"
, GetDebugTag().get()); } } while (0)
;
3784 return FALSE(0);
3785 }
3786
3787 // Send any pending resize events so that layout can update.
3788 // May run event loop and destroy us.
3789 MaybeDispatchResized();
3790 if (mIsDestroyed) {
3791 LOG("destroyed after MaybeDispatchResized()")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "destroyed after MaybeDispatchResized()"
, GetDebugTag().get()); } } while (0)
;
3792 return FALSE(0);
3793 }
3794
3795 // Windows that are not visible will be painted after they become visible.
3796 if (!mGdkWindow || !mHasMappedToplevel) {
3797 LOG("quit, !mGdkWindow || !mHasMappedToplevel")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "quit, !mGdkWindow || !mHasMappedToplevel"
, GetDebugTag().get()); } } while (0)
;
3798 return FALSE(0);
3799 }
3800#ifdef MOZ_WAYLAND1
3801 if (GdkIsWaylandDisplay() && !moz_container_wayland_can_draw(mContainer)) {
3802 LOG("quit, !moz_container_wayland_can_draw()")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "quit, !moz_container_wayland_can_draw()"
, GetDebugTag().get()); } } while (0)
;
3803 return FALSE(0);
3804 }
3805#endif
3806
3807 if (!GetListener()) {
3808 LOG("quit, !GetListener()")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "quit, !GetListener()", GetDebugTag
().get()); } } while (0)
;
3809 return FALSE(0);
3810 }
3811
3812 LayoutDeviceIntRegion exposeRegion;
3813 if (!ExtractExposeRegion(exposeRegion, cr)) {
3814 LOG(" no rects, quit")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " no rects, quit", GetDebugTag().get
()); } } while (0)
;
3815 return FALSE(0);
3816 }
3817
3818 gint scale = GdkCeiledScaleFactor();
3819 LayoutDeviceIntRegion region = exposeRegion;
3820 region.ScaleRoundOut(scale, scale);
3821
3822 RequestRepaint(region);
3823
3824 RefPtr<nsWindow> strongThis(this);
3825
3826 // Dispatch WillPaintWindow notification to allow scripts etc. to run
3827 // before we paint. It also spins event loop which may show/hide the window
3828 // so we may have new renderer etc.
3829 GetListener()->WillPaintWindow(this);
3830
3831 // If the window has been destroyed during the will paint notification,
3832 // there is nothing left to do.
3833 if (!mGdkWindow || mIsDestroyed) {
3834 LOG("quit, !mGdkWindow || mIsDestroyed")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "quit, !mGdkWindow || mIsDestroyed"
, GetDebugTag().get()); } } while (0)
;
3835 return TRUE(!(0));
3836 }
3837
3838 // Re-get all rendering components since the will paint notification
3839 // might have killed it.
3840 nsIWidgetListener* listener = GetListener();
3841 if (!listener) {
3842 LOG("quit, !listener")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "quit, !listener", GetDebugTag().get
()); } } while (0)
;
3843 return FALSE(0);
3844 }
3845
3846 WindowRenderer* renderer = GetWindowRenderer();
3847 WebRenderLayerManager* layerManager = renderer->AsWebRender();
3848 KnowsCompositor* knowsCompositor = renderer->AsKnowsCompositor();
3849
3850 if (knowsCompositor && layerManager && layerManager->NeedsComposite()) {
3851 LOG("needs composite, ScheduleComposite() call")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "needs composite, ScheduleComposite() call"
, GetDebugTag().get()); } } while (0)
;
3852 layerManager->ScheduleComposite(wr::RenderReasons::WIDGET);
3853 layerManager->SetNeedsComposite(false);
3854 }
3855
3856 // Our bounds may have changed after calling WillPaintWindow. Clip
3857 // to the new bounds here. The region is relative to this
3858 // window.
3859 region.And(region, LayoutDeviceIntRect(0, 0, mBounds.width, mBounds.height));
3860
3861 bool shaped = false;
3862 if (TransparencyMode::Transparent == GetTransparencyMode()) {
3863 auto* window = static_cast<nsWindow*>(GetTopLevelWidget());
3864 if (mTransparencyBitmapForTitlebar) {
3865 if (mSizeMode == nsSizeMode_Normal) {
3866 window->UpdateTitlebarTransparencyBitmap();
3867 } else {
3868 window->ClearTransparencyBitmap();
3869 }
3870 } else {
3871 if (mHasAlphaVisual) {
3872 // Remove possible shape mask from when window manger was not
3873 // previously compositing.
3874 window->ClearTransparencyBitmap();
3875 } else {
3876 shaped = true;
3877 }
3878 }
3879 }
3880
3881 if (region.IsEmpty()) {
3882 LOG("quit, region.IsEmpty()")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "quit, region.IsEmpty()", GetDebugTag
().get()); } } while (0)
;
3883 return TRUE(!(0));
3884 }
3885
3886 // If this widget uses OMTC...
3887 if (renderer->GetBackendType() == LayersBackend::LAYERS_WR) {
3888 LOG("redirect painting to OMTC rendering...")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "redirect painting to OMTC rendering..."
, GetDebugTag().get()); } } while (0)
;
3889 listener->PaintWindow(this, region);
3890
3891 // Re-get the listener since the will paint notification might have
3892 // killed it.
3893 listener = GetListener();
3894 if (!listener) {
3895 return TRUE(!(0));
3896 }
3897
3898 listener->DidPaintWindow();
3899 return TRUE(!(0));
3900 }
3901
3902 BufferMode layerBuffering = BufferMode::BUFFERED;
3903 RefPtr<DrawTarget> dt = StartRemoteDrawingInRegion(region, &layerBuffering);
3904 if (!dt || !dt->IsValid()) {
3905 return FALSE(0);
3906 }
3907 Maybe<gfxContext> ctx;
3908 IntRect boundsRect = region.GetBounds().ToUnknownRect();
3909 IntPoint offset(0, 0);
3910 if (dt->GetSize() == boundsRect.Size()) {
3911 offset = boundsRect.TopLeft();
3912 dt->SetTransform(Matrix::Translation(-offset));
3913 }
3914
3915#ifdef MOZ_X111
3916 if (shaped) {
3917 // Collapse update area to the bounding box. This is so we only have to
3918 // call UpdateTranslucentWindowAlpha once. After we have dropped
3919 // support for non-Thebes graphics, UpdateTranslucentWindowAlpha will be
3920 // our private interface so we can rework things to avoid this.
3921 dt->PushClipRect(Rect(boundsRect));
3922
3923 // The double buffering is done here to extract the shape mask.
3924 // (The shape mask won't be necessary when a visual with an alpha
3925 // channel is used on compositing window managers.)
3926 layerBuffering = BufferMode::BUFFER_NONE;
3927 RefPtr<DrawTarget> destDT =
3928 dt->CreateSimilarDrawTarget(boundsRect.Size(), SurfaceFormat::B8G8R8A8);
3929 if (!destDT || !destDT->IsValid()) {
3930 return FALSE(0);
3931 }
3932 destDT->SetTransform(Matrix::Translation(-boundsRect.TopLeft()));
3933 ctx.emplace(destDT, /* aPreserveTransform */ true);
3934 } else {
3935 gfxUtils::ClipToRegion(dt, region.ToUnknownRegion());
3936 ctx.emplace(dt, /* aPreserveTransform */ true);
3937 }
3938
3939# if 0
3940 // NOTE: Paint flashing region would be wrong for cairo, since
3941 // cairo inflates the update region, etc. So don't paint flash
3942 // for cairo.
3943# ifdef DEBUG1
3944 // XXX aEvent->region may refer to a newly-invalid area. FIXME
3945 if (0 && WANT_PAINT_FLASHING && gtk_widget_get_window(aEvent))
3946 gdk_window_flash(mGdkWindow, 1, 100, aEvent->region);
3947# endif
3948# endif
3949
3950#endif // MOZ_X11
3951
3952 bool painted = false;
3953 {
3954 if (renderer->GetBackendType() == LayersBackend::LAYERS_NONE) {
3955 if (GetTransparencyMode() == TransparencyMode::Transparent &&
3956 layerBuffering == BufferMode::BUFFER_NONE && mHasAlphaVisual) {
3957 // If our draw target is unbuffered and we use an alpha channel,
3958 // clear the image beforehand to ensure we don't get artifacts from a
3959 // reused SHM image. See bug 1258086.
3960 dt->ClearRect(Rect(boundsRect));
3961 }
3962 AutoLayerManagerSetup setupLayerManager(
3963 this, ctx.isNothing() ? nullptr : &ctx.ref(), layerBuffering);
3964 painted = listener->PaintWindow(this, region);
3965
3966 // Re-get the listener since the will paint notification might have
3967 // killed it.
3968 listener = GetListener();
3969 if (!listener) {
3970 return TRUE(!(0));
3971 }
3972 }
3973 }
3974
3975#ifdef MOZ_X111
3976 // PaintWindow can Destroy us (bug 378273), avoid doing any paint
3977 // operations below if that happened - it will lead to XError and exit().
3978 if (shaped) {
3979 if (MOZ_LIKELY(!mIsDestroyed)(__builtin_expect(!!(!mIsDestroyed), 1))) {
3980 if (painted) {
3981 RefPtr<SourceSurface> surf = ctx->GetDrawTarget()->Snapshot();
3982
3983 UpdateAlpha(surf, boundsRect);
3984
3985 dt->DrawSurface(surf, Rect(boundsRect),
3986 Rect(0, 0, boundsRect.width, boundsRect.height),
3987 DrawSurfaceOptions(SamplingFilter::POINT),
3988 DrawOptions(1.0f, CompositionOp::OP_SOURCE));
3989 }
3990 }
3991 }
3992
3993 ctx.reset();
3994 dt->PopClip();
3995
3996#endif // MOZ_X11
3997
3998 EndRemoteDrawingInRegion(dt, region);
3999
4000 listener->DidPaintWindow();
4001
4002 // Synchronously flush any new dirty areas
4003 cairo_region_t* dirtyArea = gdk_window_get_update_area(mGdkWindow);
4004
4005 if (dirtyArea) {
4006 gdk_window_invalidate_region(mGdkWindow, dirtyArea, false);
4007 cairo_region_destroy(dirtyArea);
4008 gdk_window_process_updates(mGdkWindow, false);
4009 }
4010
4011 // check the return value!
4012 return TRUE(!(0));
4013}
4014
4015void nsWindow::UpdateAlpha(SourceSurface* aSourceSurface,
4016 nsIntRect aBoundsRect) {
4017 // We need to create our own buffer to force the stride to match the
4018 // expected stride.
4019 int32_t stride =
4020 GetAlignedStride<4>(aBoundsRect.width, BytesPerPixel(SurfaceFormat::A8));
4021 if (stride == 0) {
4022 return;
4023 }
4024 int32_t bufferSize = stride * aBoundsRect.height;
4025 auto imageBuffer = MakeUniqueFallible<uint8_t[]>(bufferSize);
4026 {
4027 RefPtr<DrawTarget> drawTarget = gfxPlatform::CreateDrawTargetForData(
4028 imageBuffer.get(), aBoundsRect.Size(), stride, SurfaceFormat::A8);
4029
4030 if (drawTarget) {
4031 drawTarget->DrawSurface(aSourceSurface,
4032 Rect(0, 0, aBoundsRect.width, aBoundsRect.height),
4033 Rect(0, 0, aSourceSurface->GetSize().width,
4034 aSourceSurface->GetSize().height),
4035 DrawSurfaceOptions(SamplingFilter::POINT),
4036 DrawOptions(1.0f, CompositionOp::OP_SOURCE));
4037 }
4038 }
4039 UpdateTranslucentWindowAlphaInternal(aBoundsRect, imageBuffer.get(), stride);
4040}
4041
4042gboolean nsWindow::OnConfigureEvent(GtkWidget* aWidget,
4043 GdkEventConfigure* aEvent) {
4044 // These events are only received on toplevel windows.
4045 //
4046 // GDK ensures that the coordinates are the client window top-left wrt the
4047 // root window.
4048 //
4049 // GDK calculates the cordinates for real ConfigureNotify events on
4050 // managed windows (that would normally be relative to the parent
4051 // window).
4052 //
4053 // Synthetic ConfigureNotify events are from the window manager and
4054 // already relative to the root window. GDK creates all X windows with
4055 // border_width = 0, so synthetic events also indicate the top-left of
4056 // the client window.
4057 //
4058 // Override-redirect windows are children of the root window so parent
4059 // coordinates are root coordinates.
4060
4061#ifdef MOZ_LOGGING1
4062 int scale = mGdkWindow ? gdk_window_get_scale_factor(mGdkWindow) : -1;
4063 LOG("configure event %d,%d -> %d x %d direct mGdkWindow scale %d (scaled "do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "configure event %d,%d -> %d x %d direct mGdkWindow scale %d (scaled "
"size %d x %d)\n", GetDebugTag().get(), aEvent->x, aEvent
->y, aEvent->width, aEvent->height, scale, aEvent->
width * scale, aEvent->height * scale); } } while (0)
4064 "size %d x %d)\n",do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "configure event %d,%d -> %d x %d direct mGdkWindow scale %d (scaled "
"size %d x %d)\n", GetDebugTag().get(), aEvent->x, aEvent
->y, aEvent->width, aEvent->height, scale, aEvent->
width * scale, aEvent->height * scale); } } while (0)
4065 aEvent->x, aEvent->y, aEvent->width, aEvent->height, scale,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "configure event %d,%d -> %d x %d direct mGdkWindow scale %d (scaled "
"size %d x %d)\n", GetDebugTag().get(), aEvent->x, aEvent
->y, aEvent->width, aEvent->height, scale, aEvent->
width * scale, aEvent->height * scale); } } while (0)
4066 aEvent->width * scale, aEvent->height * scale)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "configure event %d,%d -> %d x %d direct mGdkWindow scale %d (scaled "
"size %d x %d)\n", GetDebugTag().get(), aEvent->x, aEvent
->y, aEvent->width, aEvent->height, scale, aEvent->
width * scale, aEvent->height * scale); } } while (0)
;
4067#endif
4068
4069 if (mPendingConfigures > 0) {
4070 mPendingConfigures--;
4071 }
4072
4073 ResetScreenBounds();
4074
4075 // Don't fire configure event for scale changes, we handle that
4076 // OnScaleChanged event. Skip that for toplevel windows only.
4077 if (mGdkWindow && IsTopLevelWindowType()) {
4078 if (mCeiledScaleFactor != gdk_window_get_scale_factor(mGdkWindow)) {
4079 LOG(" scale factor changed to %d,return early",do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " scale factor changed to %d,return early"
, GetDebugTag().get(), gdk_window_get_scale_factor(mGdkWindow
)); } } while (0)
4080 gdk_window_get_scale_factor(mGdkWindow))do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " scale factor changed to %d,return early"
, GetDebugTag().get(), gdk_window_get_scale_factor(mGdkWindow
)); } } while (0)
;
4081 return FALSE(0);
4082 }
4083 }
4084
4085 LayoutDeviceIntRect screenBounds = GetScreenBounds();
4086
4087 if (IsTopLevelWindowType()) {
4088 // This check avoids unwanted rollup on spurious configure events from
4089 // Cygwin/X (bug 672103).
4090 if (mBounds.x != screenBounds.x || mBounds.y != screenBounds.y) {
4091 RollupAllMenus();
4092 }
4093 }
4094
4095 NS_ASSERTION(GTK_IS_WINDOW(aWidget),do { if (!((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((aWidget)); GType __t = ((gtk_window_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "Configure event on widget that is not a GtkWindow"
, "GTK_IS_WINDOW(aWidget)", "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 4096); MOZ_PretendNoReturn(); } } while (0)
4096 "Configure event on widget that is not a GtkWindow")do { if (!((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((aWidget)); GType __t = ((gtk_window_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "Configure event on widget that is not a GtkWindow"
, "GTK_IS_WINDOW(aWidget)", "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 4096); MOZ_PretendNoReturn(); } } while (0)
;
4097 if (mGdkWindow &&
4098 gtk_window_get_window_type(GTK_WINDOW(aWidget)((((GtkWindow*) (void *) ((aWidget)))))) == GTK_WINDOW_POPUP) {
4099 // Override-redirect window
4100 //
4101 // These windows should not be moved by the window manager, and so any
4102 // change in position is a result of our direction. mBounds has
4103 // already been set in std::move() or Resize(), and that is more
4104 // up-to-date than the position in the ConfigureNotify event if the
4105 // event is from an earlier window move.
4106 //
4107 // Skipping the WindowMoved call saves context menus from an infinite
4108 // loop when nsXULPopupManager::PopupMoved moves the window to the new
4109 // position and nsMenuPopupFrame::SetPopupPosition adds
4110 // offsetForContextMenu on each iteration.
4111
4112 // Our back buffer might have been invalidated while we drew the last
4113 // frame, and its contents might be incorrect. See bug 1280653 comment 7
4114 // and comment 10. Specifically we must ensure we recomposite the frame
4115 // as soon as possible to avoid the corrupted frame being displayed.
4116 GetWindowRenderer()->FlushRendering(wr::RenderReasons::WIDGET);
4117 return FALSE(0);
4118 }
4119
4120 mBounds.MoveTo(screenBounds.TopLeft());
4121 RecomputeClientOffset(/* aNotify = */ false);
4122
4123 // XXX mozilla will invalidate the entire window after this move
4124 // complete. wtf?
4125 NotifyWindowMoved(mBounds.x, mBounds.y);
4126
4127 return FALSE(0);
4128}
4129
4130void nsWindow::OnSizeAllocate(GtkAllocation* aAllocation) {
4131 LOG("nsWindow::OnSizeAllocate %d,%d -> %d x %d\n", aAllocation->x,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::OnSizeAllocate %d,%d -> %d x %d\n"
, GetDebugTag().get(), aAllocation->x, aAllocation->y, aAllocation
->width, aAllocation->height); } } while (0)
4132 aAllocation->y, aAllocation->width, aAllocation->height)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "nsWindow::OnSizeAllocate %d,%d -> %d x %d\n"
, GetDebugTag().get(), aAllocation->x, aAllocation->y, aAllocation
->width, aAllocation->height); } } while (0)
;
4133
4134 ResetScreenBounds();
4135
4136 // Client offset are updated by _NET_FRAME_EXTENTS on X11 when system titlebar
4137 // is enabled. In either cases (Wayland or system titlebar is off on X11)
4138 // we don't get _NET_FRAME_EXTENTS X11 property notification so we derive
4139 // it from mContainer position.
4140 RecomputeClientOffset(/* aNotify = */ true);
4141
4142 mHasReceivedSizeAllocate = true;
4143
4144 LayoutDeviceIntSize size = GdkRectToDevicePixels(*aAllocation).Size();
4145
4146 // Sometimes the window manager gives us garbage sizes (way past the maximum
4147 // texture size) causing crashes if we don't enforce size constraints again
4148 // here.
4149 ConstrainSize(&size.width, &size.height);
4150
4151 if (mBounds.Size() == size) {
4152 LOG(" Already the same size")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " Already the same size", GetDebugTag
().get()); } } while (0)
;
4153 // mBounds was set at Create() or Resize().
4154 if (mNeedsDispatchSize != LayoutDeviceIntSize(-1, -1)) {
4155 LOG(" No longer needs to dispatch %dx%d", mNeedsDispatchSize.width,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " No longer needs to dispatch %dx%d"
, GetDebugTag().get(), mNeedsDispatchSize.width, mNeedsDispatchSize
.height); } } while (0)
4156 mNeedsDispatchSize.height)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " No longer needs to dispatch %dx%d"
, GetDebugTag().get(), mNeedsDispatchSize.width, mNeedsDispatchSize
.height); } } while (0)
;
4157 mNeedsDispatchSize = LayoutDeviceIntSize(-1, -1);
4158 }
4159 return;
4160 }
4161
4162 // Invalidate the new part of the window now for the pending paint to
4163 // minimize background flashes (GDK does not do this for external resizes
4164 // of toplevels.)
4165 if (mGdkWindow) {
4166 if (mBounds.width < size.width) {
4167 GdkRectangle rect = DevicePixelsToGdkRectRoundOut(LayoutDeviceIntRect(
4168 mBounds.width, 0, size.width - mBounds.width, size.height));
4169 gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE(0));
4170 }
4171 if (mBounds.height < size.height) {
4172 GdkRectangle rect = DevicePixelsToGdkRectRoundOut(LayoutDeviceIntRect(
4173 0, mBounds.height, size.width, size.height - mBounds.height));
4174 gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE(0));
4175 }
4176 }
4177
4178 // If we update mBounds here, then inner/outerHeight are out of sync until
4179 // we call WindowResized.
4180 mNeedsDispatchSize = size;
4181
4182 // Gecko permits running nested event loops during processing of events,
4183 // GtkWindow callers of gtk_widget_size_allocate expect the signal
4184 // handlers to return sometime in the near future.
4185 NS_DispatchToCurrentThread(NewRunnableMethod(
4186 "nsWindow::MaybeDispatchResized", this, &nsWindow::MaybeDispatchResized));
4187}
4188
4189void nsWindow::OnDeleteEvent() {
4190 if (mWidgetListener) mWidgetListener->RequestWindowClose(this);
4191}
4192
4193void nsWindow::OnEnterNotifyEvent(GdkEventCrossing* aEvent) {
4194 LOG("enter notify (win=%p, sub=%p): %f, %f mode %d, detail %d\n",do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "enter notify (win=%p, sub=%p): %f, %f mode %d, detail %d\n"
, GetDebugTag().get(), aEvent->window, aEvent->subwindow
, aEvent->x, aEvent->y, aEvent->mode, aEvent->detail
); } } while (0)
4195 aEvent->window, aEvent->subwindow, aEvent->x, aEvent->y, aEvent->mode,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "enter notify (win=%p, sub=%p): %f, %f mode %d, detail %d\n"
, GetDebugTag().get(), aEvent->window, aEvent->subwindow
, aEvent->x, aEvent->y, aEvent->mode, aEvent->detail
); } } while (0)
4196 aEvent->detail)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "enter notify (win=%p, sub=%p): %f, %f mode %d, detail %d\n"
, GetDebugTag().get(), aEvent->window, aEvent->subwindow
, aEvent->x, aEvent->y, aEvent->mode, aEvent->detail
); } } while (0)
;
4197 // This skips NotifyVirtual and NotifyNonlinearVirtual enter notify events
4198 // when the pointer enters a child window. If the destination window is a
4199 // Gecko window then we'll catch the corresponding event on that window,
4200 // but we won't notice when the pointer directly enters a foreign (plugin)
4201 // child window without passing over a visible portion of a Gecko window.
4202 if (aEvent->subwindow) {
4203 return;
4204 }
4205
4206 // Check before checking for ungrab as the button state may have
4207 // changed while a non-Gecko ancestor window had a pointer grab.
4208 DispatchMissedButtonReleases(aEvent);
4209
4210 WidgetMouseEvent event(true, eMouseEnterIntoWidget, this,
4211 WidgetMouseEvent::eReal);
4212
4213 event.mRefPoint = GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y);
4214 event.AssignEventTime(GetWidgetEventTime(aEvent->time));
4215
4216 LOG("OnEnterNotify")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "OnEnterNotify", GetDebugTag().get(
)); } } while (0)
;
4217
4218 DispatchInputEvent(&event);
4219}
4220
4221// Some window managers send a bogus top-level leave-notify event on every
4222// click. That confuses our event handling code in ways that can break websites,
4223// see bug 1805939 for details.
4224//
4225// Make sure to only check this on bogus environments, since for environments
4226// with CSD, gdk_device_get_window_at_position could return the window even when
4227// the pointer is in the decoration area.
4228static bool IsBogusLeaveNotifyEvent(GdkWindow* aWindow,
4229 GdkEventCrossing* aEvent) {
4230 static bool sBogusWm = [] {
4231 if (GdkIsWaylandDisplay()) {
4232 return false;
4233 }
4234 const auto& desktopEnv = GetDesktopEnvironmentIdentifier();
4235 return desktopEnv.EqualsLiteral("fluxbox") || // Bug 1805939 comment 0.
4236 desktopEnv.EqualsLiteral("blackbox") || // Bug 1805939 comment 32.
4237 desktopEnv.EqualsLiteral("lg3d") || // Bug 1820405.
4238 desktopEnv.EqualsLiteral("pekwm") || // Bug 1822911.
4239 StringBeginsWith(desktopEnv, "fvwm"_ns);
4240 }();
4241
4242 const bool shouldCheck = [] {
4243 switch (StaticPrefs::widget_gtk_ignore_bogus_leave_notify()) {
4244 case 0:
4245 return false;
4246 case 1:
4247 return true;
4248 default:
4249 return sBogusWm;
4250 }
4251 }();
4252
4253 if (!shouldCheck || !aWindow) {
4254 return false;
4255 }
4256 GdkDevice* pointer = GdkGetPointer();
4257 GdkWindow* winAtPt =
4258 gdk_device_get_window_at_position(pointer, nullptr, nullptr);
4259 if (!winAtPt) {
4260 return false;
4261 }
4262 // We're still in the same top level window, ignore this leave notify event.
4263 GdkWindow* topLevelAtPt = gdk_window_get_toplevel(winAtPt);
4264 GdkWindow* topLevelWidget = gdk_window_get_toplevel(aWindow);
4265 return topLevelAtPt == topLevelWidget;
4266}
4267
4268void nsWindow::OnLeaveNotifyEvent(GdkEventCrossing* aEvent) {
4269 LOG("leave notify (win=%p, sub=%p): %f, %f mode %d, detail %d\n",do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "leave notify (win=%p, sub=%p): %f, %f mode %d, detail %d\n"
, GetDebugTag().get(), aEvent->window, aEvent->subwindow
, aEvent->x, aEvent->y, aEvent->mode, aEvent->detail
); } } while (0)
4270 aEvent->window, aEvent->subwindow, aEvent->x, aEvent->y, aEvent->mode,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "leave notify (win=%p, sub=%p): %f, %f mode %d, detail %d\n"
, GetDebugTag().get(), aEvent->window, aEvent->subwindow
, aEvent->x, aEvent->y, aEvent->mode, aEvent->detail
); } } while (0)
4271 aEvent->detail)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "leave notify (win=%p, sub=%p): %f, %f mode %d, detail %d\n"
, GetDebugTag().get(), aEvent->window, aEvent->subwindow
, aEvent->x, aEvent->y, aEvent->mode, aEvent->detail
); } } while (0)
;
4272
4273 // This ignores NotifyVirtual and NotifyNonlinearVirtual leave notify
4274 // events when the pointer leaves a child window. If the destination
4275 // window is a Gecko window then we'll catch the corresponding event on
4276 // that window.
4277 //
4278 // XXXkt However, we will miss toplevel exits when the pointer directly
4279 // leaves a foreign (plugin) child window without passing over a visible
4280 // portion of a Gecko window.
4281 if (aEvent->subwindow) {
4282 return;
4283 }
4284
4285 // The filter out for subwindows should make sure that this is targeted to
4286 // this nsWindow.
4287 const bool leavingTopLevel = IsTopLevelWindowType();
4288 if (leavingTopLevel && IsBogusLeaveNotifyEvent(mGdkWindow, aEvent)) {
4289 return;
4290 }
4291
4292 WidgetMouseEvent event(true, eMouseExitFromWidget, this,
4293 WidgetMouseEvent::eReal);
4294
4295 event.mRefPoint = GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y);
4296 event.AssignEventTime(GetWidgetEventTime(aEvent->time));
4297 event.mExitFrom = Some(leavingTopLevel ? WidgetMouseEvent::ePlatformTopLevel
4298 : WidgetMouseEvent::ePlatformChild);
4299
4300 LOG("OnLeaveNotify")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "OnLeaveNotify", GetDebugTag().get(
)); } } while (0)
;
4301
4302 DispatchInputEvent(&event);
4303}
4304
4305Maybe<GdkWindowEdge> nsWindow::CheckResizerEdge(
4306 const LayoutDeviceIntPoint& aPoint) {
4307 const bool canResize = [&] {
4308 // Don't allow resizing maximized/fullscreen windows.
4309 if (mSizeMode != nsSizeMode_Normal) {
4310 return false;
4311 }
4312 if (mIsPIPWindow) {
4313 // Note that since we do show resizers on left/right sides on PIP windows,
4314 // we still want the resizers there, even when tiled.
4315 return true;
4316 }
4317 if (!mDrawInTitlebar) {
4318 return false;
4319 }
4320 // On KDE, allow for 1 extra pixel at the top of regular windows when
4321 // drawing to the titlebar. This matches the native titlebar behavior on
4322 // that environment. See bug 1813554.
4323 //
4324 // Don't do that on GNOME (see bug 1822764). If we wanted to do this on
4325 // GNOME we'd need an extra check for mIsTiled, since the window is "stuck"
4326 // to the top and bottom.
4327 //
4328 // Other DEs are untested.
4329 return mDrawInTitlebar && IsKdeDesktopEnvironment();
4330 }();
4331
4332 if (!canResize) {
4333 return Nothing();
4334 }
4335
4336 // If we're not in a PiP window, allow 1px resizer edge from the top edge,
4337 // and nothing else.
4338 // This is to allow resizes of tiled windows on KDE, see bug 1813554.
4339 const int resizerHeight = (mIsPIPWindow ? 15 : 1) * GdkCeiledScaleFactor();
4340 const int resizerWidth = resizerHeight * 4;
4341
4342 const int topDist = aPoint.y;
4343 const int leftDist = aPoint.x;
4344 const int rightDist = mBounds.width - aPoint.x;
4345 const int bottomDist = mBounds.height - aPoint.y;
4346
4347 // We can't emulate resize of North/West edges on Wayland as we can't shift
4348 // toplevel window.
4349 bool waylandLimitedResize = mAspectRatio != 0.0f && GdkIsWaylandDisplay();
4350
4351 if (topDist <= resizerHeight) {
4352 if (rightDist <= resizerWidth) {
4353 return Some(GDK_WINDOW_EDGE_NORTH_EAST);
4354 }
4355 if (leftDist <= resizerWidth) {
4356 return Some(GDK_WINDOW_EDGE_NORTH_WEST);
4357 }
4358 return waylandLimitedResize ? Nothing() : Some(GDK_WINDOW_EDGE_NORTH);
4359 }
4360
4361 if (!mIsPIPWindow) {
4362 return Nothing();
4363 }
4364
4365 if (bottomDist <= resizerHeight) {
4366 if (leftDist <= resizerWidth) {
4367 return Some(GDK_WINDOW_EDGE_SOUTH_WEST);
4368 }
4369 if (rightDist <= resizerWidth) {
4370 return Some(GDK_WINDOW_EDGE_SOUTH_EAST);
4371 }
4372 return Some(GDK_WINDOW_EDGE_SOUTH);
4373 }
4374
4375 if (leftDist <= resizerHeight) {
4376 if (topDist <= resizerWidth) {
4377 return Some(GDK_WINDOW_EDGE_NORTH_WEST);
4378 }
4379 if (bottomDist <= resizerWidth) {
4380 return Some(GDK_WINDOW_EDGE_SOUTH_WEST);
4381 }
4382 return waylandLimitedResize ? Nothing() : Some(GDK_WINDOW_EDGE_WEST);
4383 }
4384
4385 if (rightDist <= resizerHeight) {
4386 if (topDist <= resizerWidth) {
4387 return Some(GDK_WINDOW_EDGE_NORTH_EAST);
4388 }
4389 if (bottomDist <= resizerWidth) {
4390 return Some(GDK_WINDOW_EDGE_SOUTH_EAST);
4391 }
4392 return Some(GDK_WINDOW_EDGE_EAST);
4393 }
4394 return Nothing();
4395}
4396
4397template <typename Event>
4398static LayoutDeviceIntPoint GetRefPoint(nsWindow* aWindow, Event* aEvent) {
4399 if (aEvent->window == aWindow->GetGdkWindow()) {
4400 // we are the window that the event happened on so no need for expensive
4401 // WidgetToScreenOffset
4402 return aWindow->GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y);
4403 }
4404 // XXX we're never quite sure which GdkWindow the event came from due to our
4405 // custom bubbling in scroll_event_cb(), so use ScreenToWidget to translate
4406 // the screen root coordinates into coordinates relative to this widget.
4407 return aWindow->GdkEventCoordsToDevicePixels(aEvent->x_root, aEvent->y_root) -
4408 aWindow->WidgetToScreenOffset();
4409}
4410
4411void nsWindow::EmulateResizeDrag(GdkEventMotion* aEvent) {
4412 auto newPoint = LayoutDeviceIntPoint::Floor(aEvent->x, aEvent->y);
4413 LayoutDeviceIntPoint diff = newPoint - mLastResizePoint;
4414 mLastResizePoint = newPoint;
4415
4416 GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mBounds.Size());
4417 LayoutDeviceIntSize newSize(size.width + diff.x, size.height + diff.y);
4418
4419 if (mAspectResizer.value() == GTK_ORIENTATION_VERTICAL) {
4420 newSize.width = int(newSize.height * mAspectRatio);
4421 } else { // GTK_ORIENTATION_HORIZONTAL
4422 newSize.height = int(newSize.width / mAspectRatio);
4423 }
4424 LOG(" aspect ratio correction %d x %d aspect %f\n", newSize.width,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " aspect ratio correction %d x %d aspect %f\n"
, GetDebugTag().get(), newSize.width, newSize.height, mAspectRatio
); } } while (0)
4425 newSize.height, mAspectRatio)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " aspect ratio correction %d x %d aspect %f\n"
, GetDebugTag().get(), newSize.width, newSize.height, mAspectRatio
); } } while (0)
;
4426 gtk_window_resize(GTK_WINDOW(mShell)((((GtkWindow*) (void *) ((mShell))))), newSize.width, newSize.height);
4427}
4428
4429void nsWindow::OnMotionNotifyEvent(GdkEventMotion* aEvent) {
4430 if (!mGdkWindow) {
4431 return;
4432 }
4433
4434 // Emulate gdk_window_begin_resize_drag() for windows
4435 // with fixed aspect ratio on Wayland.
4436 if (mAspectResizer && mAspectRatio != 0.0f) {
4437 EmulateResizeDrag(aEvent);
4438 return;
4439 }
4440
4441 if (mWindowShouldStartDragging) {
4442 mWindowShouldStartDragging = false;
4443 GdkWindow* dragWindow = nullptr;
4444
4445 // find the top-level window
4446 if (mGdkWindow) {
4447 dragWindow = gdk_window_get_toplevel(mGdkWindow);
4448 MOZ_ASSERT(dragWindow, "gdk_window_get_toplevel should not return null")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(dragWindow)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(dragWindow))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("dragWindow" " (" "gdk_window_get_toplevel should not return null"
")", "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 4448); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dragWindow"
") (" "gdk_window_get_toplevel should not return null" ")");
do { *((volatile int*)__null) = 4448; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4449 }
4450
4451#ifdef MOZ_X111
4452 if (dragWindow && GdkIsX11Display()) {
4453 // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=789054
4454 // To avoid crashes disable double-click on WM without _NET_WM_MOVERESIZE.
4455 // See _should_perform_ewmh_drag() at gdkwindow-x11.c
4456 GdkScreen* screen = gdk_window_get_screen(dragWindow);
4457 GdkAtom atom = gdk_atom_intern("_NET_WM_MOVERESIZE", FALSE(0));
4458 if (!gdk_x11_screen_supports_net_wm_hint(screen, atom)) {
4459 dragWindow = nullptr;
4460 }
4461 }
4462#endif
4463
4464 if (dragWindow) {
4465 gdk_window_begin_move_drag(dragWindow, 1, aEvent->x_root, aEvent->y_root,
4466 aEvent->time);
4467 return;
4468 }
4469 }
4470
4471 mWidgetCursorLocked = false;
4472 const auto refPoint = GetRefPoint(this, aEvent);
4473 if (auto edge = CheckResizerEdge(refPoint)) {
4474 nsCursor cursor = eCursor_none;
4475 switch (*edge) {
4476 case GDK_WINDOW_EDGE_SOUTH:
4477 case GDK_WINDOW_EDGE_NORTH:
4478 cursor = eCursor_ns_resize;
4479 break;
4480 case GDK_WINDOW_EDGE_WEST:
4481 case GDK_WINDOW_EDGE_EAST:
4482 cursor = eCursor_ew_resize;
4483 break;
4484 case GDK_WINDOW_EDGE_NORTH_WEST:
4485 case GDK_WINDOW_EDGE_SOUTH_EAST:
4486 cursor = eCursor_nwse_resize;
4487 break;
4488 case GDK_WINDOW_EDGE_NORTH_EAST:
4489 case GDK_WINDOW_EDGE_SOUTH_WEST:
4490 cursor = eCursor_nesw_resize;
4491 break;
4492 }
4493 SetCursor(Cursor{cursor});
4494 // If we set resize cursor on widget level keep it locked and prevent layout
4495 // to switch it back to default (by synthetic mouse events for instance)
4496 // until resize is finished. This affects PIP windows only.
4497 if (mIsPIPWindow) {
4498 mWidgetCursorLocked = true;
4499 }
4500 return;
4501 }
4502
4503 WidgetMouseEvent event(true, eMouseMove, this, WidgetMouseEvent::eReal);
4504
4505 gdouble pressure = 0;
4506 gdk_event_get_axis((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
4507 // Sometime gdk generate 0 pressure value between normal values
4508 // We have to ignore that and use last valid value
4509 if (pressure) {
4510 mLastMotionPressure = pressure;
4511 }
4512 event.mPressure = mLastMotionPressure;
4513 event.mRefPoint = refPoint;
4514 event.AssignEventTime(GetWidgetEventTime(aEvent->time));
4515
4516 KeymapWrapper::InitInputEvent(event, aEvent->state);
4517
4518 DispatchInputEvent(&event);
4519}
4520
4521// If the automatic pointer grab on ButtonPress has deactivated before
4522// ButtonRelease, and the mouse button is released while the pointer is not
4523// over any a Gecko window, then the ButtonRelease event will not be received.
4524// (A similar situation exists when the pointer is grabbed with owner_events
4525// True as the ButtonRelease may be received on a foreign [plugin] window).
4526// Use this method to check for released buttons when the pointer returns to a
4527// Gecko window.
4528void nsWindow::DispatchMissedButtonReleases(GdkEventCrossing* aGdkEvent) {
4529 guint changed = aGdkEvent->state ^ gButtonState;
4530 // Only consider button releases.
4531 // (Ignore button presses that occurred outside Gecko.)
4532 guint released = changed & gButtonState;
4533 gButtonState = aGdkEvent->state;
4534
4535 // Loop over each button, excluding mouse wheel buttons 4 and 5 for which
4536 // GDK ignores releases.
4537 for (guint buttonMask = GDK_BUTTON1_MASK; buttonMask <= GDK_BUTTON3_MASK;
4538 buttonMask <<= 1) {
4539 if (released & buttonMask) {
4540 int16_t buttonType;
4541 switch (buttonMask) {
4542 case GDK_BUTTON1_MASK:
4543 buttonType = MouseButton::ePrimary;
4544 break;
4545 case GDK_BUTTON2_MASK:
4546 buttonType = MouseButton::eMiddle;
4547 break;
4548 default:
4549 NS_ASSERTION(buttonMask == GDK_BUTTON3_MASK,do { if (!(buttonMask == GDK_BUTTON3_MASK)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "Unexpected button mask", "buttonMask == GDK_BUTTON3_MASK",
"/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 4550); MOZ_PretendNoReturn(); } } while (0)
4550 "Unexpected button mask")do { if (!(buttonMask == GDK_BUTTON3_MASK)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "Unexpected button mask", "buttonMask == GDK_BUTTON3_MASK",
"/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 4550); MOZ_PretendNoReturn(); } } while (0)
;
4551 buttonType = MouseButton::eSecondary;
4552 }
4553
4554 LOG("Synthesized button %u release", guint(buttonType + 1))do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "Synthesized button %u release", GetDebugTag
().get(), guint(buttonType + 1)); } } while (0)
;
4555
4556 // Dispatch a synthesized button up event to tell Gecko about the
4557 // change in state. This event is marked as synthesized so that
4558 // it is not dispatched as a DOM event, because we don't know the
4559 // position, widget, modifiers, or time/order.
4560 WidgetMouseEvent synthEvent(true, eMouseUp, this,
4561 WidgetMouseEvent::eSynthesized);
4562 synthEvent.mButton = buttonType;
4563 DispatchInputEvent(&synthEvent);
4564 }
4565 }
4566}
4567
4568void nsWindow::InitButtonEvent(WidgetMouseEvent& aEvent,
4569 GdkEventButton* aGdkEvent,
4570 const LayoutDeviceIntPoint& aRefPoint) {
4571 aEvent.mRefPoint = aRefPoint;
4572
4573 guint modifierState = aGdkEvent->state;
4574 // aEvent's state includes the button state from immediately before this
4575 // event. If aEvent is a mousedown or mouseup event, we need to update
4576 // the button state.
4577 guint buttonMask = 0;
4578 switch (aGdkEvent->button) {
4579 case 1:
4580 buttonMask = GDK_BUTTON1_MASK;
4581 break;
4582 case 2:
4583 buttonMask = GDK_BUTTON2_MASK;
4584 break;
4585 case 3:
4586 buttonMask = GDK_BUTTON3_MASK;
4587 break;
4588 }
4589 if (aGdkEvent->type == GDK_BUTTON_RELEASE) {
4590 modifierState &= ~buttonMask;
4591 } else {
4592 modifierState |= buttonMask;
4593 }
4594
4595 KeymapWrapper::InitInputEvent(aEvent, modifierState);
4596
4597 aEvent.AssignEventTime(GetWidgetEventTime(aGdkEvent->time));
4598
4599 switch (aGdkEvent->type) {
4600 case GDK_2BUTTON_PRESS:
4601 aEvent.mClickCount = 2;
4602 break;
4603 case GDK_3BUTTON_PRESS:
4604 aEvent.mClickCount = 3;
4605 break;
4606 // default is one click
4607 default:
4608 aEvent.mClickCount = 1;
4609 }
4610}
4611
4612static guint ButtonMaskFromGDKButton(guint button) {
4613 return GDK_BUTTON1_MASK << (button - 1);
4614}
4615
4616void nsWindow::DispatchContextMenuEventFromMouseEvent(
4617 uint16_t domButton, GdkEventButton* aEvent,
4618 const LayoutDeviceIntPoint& aRefPoint) {
4619 if (domButton == MouseButton::eSecondary && MOZ_LIKELY(!mIsDestroyed)(__builtin_expect(!!(!mIsDestroyed), 1))) {
4620 WidgetMouseEvent contextMenuEvent(true, eContextMenu, this,
4621 WidgetMouseEvent::eReal);
4622 InitButtonEvent(contextMenuEvent, aEvent, aRefPoint);
4623 contextMenuEvent.mPressure = mLastMotionPressure;
4624 DispatchInputEvent(&contextMenuEvent);
4625 }
4626}
4627
4628void nsWindow::TryToShowNativeWindowMenu(GdkEventButton* aEvent) {
4629 if (!gdk_window_show_window_menu(GetToplevelGdkWindow(), (GdkEvent*)aEvent)) {
4630 NS_WARNING("Native context menu wasn't shown")NS_DebugBreak(NS_DEBUG_WARNING, "Native context menu wasn't shown"
, nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 4630)
;
4631 }
4632}
4633
4634bool nsWindow::DoTitlebarAction(LookAndFeel::TitlebarEvent aEvent,
4635 GdkEventButton* aButtonEvent) {
4636 LOG("DoTitlebarAction %s click",do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "DoTitlebarAction %s click", GetDebugTag
().get(), aEvent == LookAndFeel::TitlebarEvent::Double_Click ?
"double" : "middle"); } } while (0)
4637 aEvent == LookAndFeel::TitlebarEvent::Double_Click ? "double" : "middle")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "DoTitlebarAction %s click", GetDebugTag
().get(), aEvent == LookAndFeel::TitlebarEvent::Double_Click ?
"double" : "middle"); } } while (0)
;
4638 switch (LookAndFeel::GetTitlebarAction(aEvent)) {
4639 case LookAndFeel::TitlebarAction::WindowMenu:
4640 // Titlebar app menu
4641 LOG(" action menu")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " action menu", GetDebugTag().get(
)); } } while (0)
;
4642 TryToShowNativeWindowMenu(aButtonEvent);
4643 break;
4644 case LookAndFeel::TitlebarAction::WindowLower:
4645 LOG(" action lower")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " action lower", GetDebugTag().get
()); } } while (0)
;
4646 // Lower is part of gtk_surface1 protocol which we can't support
4647 // as Gtk keeps it private. So emulate it by minimize.
4648 if (GdkIsWaylandDisplay()) {
4649 SetSizeMode(nsSizeMode_Minimized);
4650 } else {
4651 gdk_window_lower(GetToplevelGdkWindow());
4652 }
4653 break;
4654 case LookAndFeel::TitlebarAction::WindowMinimize:
4655 LOG(" action minimize")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " action minimize", GetDebugTag().
get()); } } while (0)
;
4656 SetSizeMode(nsSizeMode_Minimized);
4657 break;
4658 case LookAndFeel::TitlebarAction::WindowMaximize:
4659 LOG(" action maximize")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " action maximize", GetDebugTag().
get()); } } while (0)
;
4660 SetSizeMode(nsSizeMode_Maximized);
4661 break;
4662 case LookAndFeel::TitlebarAction::WindowMaximizeToggle:
4663 LOG(" action toggle maximize")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " action toggle maximize", GetDebugTag
().get()); } } while (0)
;
4664 if (mSizeMode == nsSizeMode_Maximized) {
4665 SetSizeMode(nsSizeMode_Normal);
4666 } else if (mSizeMode == nsSizeMode_Normal) {
4667 SetSizeMode(nsSizeMode_Maximized);
4668 }
4669 break;
4670 case LookAndFeel::TitlebarAction::None:
4671 default:
4672 LOG(" action none")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " " action none", GetDebugTag().get(
)); } } while (0)
;
4673 return false;
4674 }
4675 return true;
4676}
4677
4678void nsWindow::OnButtonPressEvent(GdkEventButton* aEvent) {
4679 LOG("Button %u press\n", aEvent->button)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "Button %u press\n", GetDebugTag().
get(), aEvent->button); } } while (0)
;
4680
4681 SetLastMousePressEvent((GdkEvent*)aEvent);
4682
4683 // If you double click in GDK, it will actually generate a second
4684 // GDK_BUTTON_PRESS before sending the GDK_2BUTTON_PRESS, and this is
4685 // different than the DOM spec. GDK puts this in the queue
4686 // programatically, so it's safe to assume that if there's a
4687 // double click in the queue, it was generated so we can just drop
4688 // this click.
4689 GUniquePtr<GdkEvent> peekedEvent(gdk_event_peek());
4690 if (peekedEvent) {
4691 GdkEventType type = peekedEvent->any.type;
4692 if (type == GDK_2BUTTON_PRESS || type == GDK_3BUTTON_PRESS) {
4693 return;
4694 }
4695 }
4696
4697 nsWindow* containerWindow = GetContainerWindow();
4698 if (!gFocusWindow && containerWindow) {
4699 containerWindow->DispatchActivateEvent();
4700 }
4701
4702 const auto refPoint = GetRefPoint(this, aEvent);
4703
4704 // check to see if we should rollup
4705 if (CheckForRollup(aEvent->x_root, aEvent->y_root, false, false)) {
4706 if (aEvent->button == 3 && mDraggableRegion.Contains(refPoint)) {
4707 GUniquePtr<GdkEvent> eventCopy;
4708 if (aEvent->type != GDK_BUTTON_PRESS) {
4709 // If the user double-clicks too fast we'll get a 2BUTTON_PRESS event
4710 // instead, and that isn't handled by open_window_menu, so coerce it
4711 // into a regular press.
4712 eventCopy.reset(gdk_event_copy((GdkEvent*)aEvent));
4713 eventCopy->type = GDK_BUTTON_PRESS;
4714 }
4715 TryToShowNativeWindowMenu(eventCopy ? &eventCopy->button : aEvent);
4716 }
4717 return;
4718 }
4719
4720 // Check to see if the event is within our window's resize region
4721 if (auto edge = CheckResizerEdge(refPoint)) {
4722 // On Wayland Gtk fails to vertically/horizontally resize windows
4723 // with fixed aspect ratio. We need to emulate
4724 // gdk_window_begin_resize_drag() at OnMotionNotifyEvent().
4725 if (mAspectRatio != 0.0f && GdkIsWaylandDisplay()) {
4726 mLastResizePoint = LayoutDeviceIntPoint::Floor(aEvent->x, aEvent->y);
4727 switch (*edge) {
4728 case GDK_WINDOW_EDGE_SOUTH:
4729 mAspectResizer = Some(GTK_ORIENTATION_VERTICAL);
4730 break;
4731 case GDK_WINDOW_EDGE_EAST:
4732 mAspectResizer = Some(GTK_ORIENTATION_HORIZONTAL);
4733 break;
4734 default:
4735 mAspectResizer.reset();
4736 break;
4737 }
4738 ApplySizeConstraints();
4739 }
4740 if (!mAspectResizer) {
4741 gdk_window_begin_resize_drag(GetToplevelGdkWindow(), *edge,
4742 aEvent->button, aEvent->x_root,
4743 aEvent->y_root, aEvent->time);
4744 }
4745 return;
4746 }
4747
4748 gdouble pressure = 0;
4749 gdk_event_get_axis((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
4750 mLastMotionPressure = pressure;
4751
4752 uint16_t domButton;
4753 switch (aEvent->button) {
4754 case 1:
4755 domButton = MouseButton::ePrimary;
4756 break;
4757 case 2:
4758 domButton = MouseButton::eMiddle;
4759 break;
4760 case 3:
4761 domButton = MouseButton::eSecondary;
4762 break;
4763 // These are mapped to horizontal scroll
4764 case 6:
4765 case 7:
4766 NS_WARNING("We're not supporting legacy horizontal scroll event")NS_DebugBreak(NS_DEBUG_WARNING, "We're not supporting legacy horizontal scroll event"
, nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 4766)
;
4767 return;
4768 // Map buttons 8-9(10) to back/forward
4769 case 8:
4770 if (!Preferences::GetBool("mousebutton.4th.enabled", true)) {
4771 return;
4772 }
4773 DispatchCommandEvent(nsGkAtoms::Back);
4774 return;
4775 case 9:
4776 case 10:
4777 if (!Preferences::GetBool("mousebutton.5th.enabled", true)) {
4778 return;
4779 }
4780 DispatchCommandEvent(nsGkAtoms::Forward);
4781 return;
4782 default:
4783 return;
4784 }
4785
4786 gButtonState |= ButtonMaskFromGDKButton(aEvent->button);
4787
4788 WidgetMouseEvent event(true, eMouseDown, this, WidgetMouseEvent::eReal);
4789 event.mButton = domButton;
4790 InitButtonEvent(event, aEvent, refPoint);
4791 event.mPressure = mLastMotionPressure;
4792
4793 nsIWidget::ContentAndAPZEventStatus eventStatus = DispatchInputEvent(&event);
4794
4795 const bool defaultPrevented =
4796 eventStatus.mContentStatus == nsEventStatus_eConsumeNoDefault;
4797
4798 if (!defaultPrevented && mDraggableRegion.Contains(refPoint)) {
4799 if (domButton == MouseButton::ePrimary) {
4800 mWindowShouldStartDragging = true;
4801 } else if (domButton == MouseButton::eMiddle &&
4802 StaticPrefs::widget_gtk_titlebar_action_middle_click_enabled()) {
4803 DoTitlebarAction(nsXPLookAndFeel::TitlebarEvent::Middle_Click, aEvent);
4804 }
4805 }
4806
4807 // right menu click on linux should also pop up a context menu
4808 if (!StaticPrefs::ui_context_menus_after_mouseup() &&
4809 eventStatus.mApzStatus != nsEventStatus_eConsumeNoDefault) {
4810 DispatchContextMenuEventFromMouseEvent(domButton, aEvent, refPoint);
4811 }
4812}
4813
4814void nsWindow::OnButtonReleaseEvent(GdkEventButton* aEvent) {
4815 LOG("Button %u release\n", aEvent->button)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "Button %u release\n", GetDebugTag(
).get(), aEvent->button); } } while (0)
;
4816
4817 SetLastMousePressEvent(nullptr);
4818
4819 if (!mGdkWindow) {
4820 return;
4821 }
4822
4823 if (mAspectResizer) {
4824 mAspectResizer = Nothing();
4825 return;
4826 }
4827
4828 if (mWindowShouldStartDragging) {
4829 mWindowShouldStartDragging = false;
4830 }
4831
4832 uint16_t domButton;
4833 switch (aEvent->button) {
4834 case 1:
4835 domButton = MouseButton::ePrimary;
4836 break;
4837 case 2:
4838 domButton = MouseButton::eMiddle;
4839 break;
4840 case 3:
4841 domButton = MouseButton::eSecondary;
4842 break;
4843 default:
4844 return;
4845 }
4846
4847 gButtonState &= ~ButtonMaskFromGDKButton(aEvent->button);
4848
4849 const auto refPoint = GetRefPoint(this, aEvent);
4850
4851 WidgetMouseEvent event(true, eMouseUp, this, WidgetMouseEvent::eReal);
4852 event.mButton = domButton;
4853 InitButtonEvent(event, aEvent, refPoint);
4854 gdouble pressure = 0;
4855 gdk_event_get_axis((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
4856 event.mPressure = pressure ? (float)pressure : (float)mLastMotionPressure;
4857
4858 // The mRefPoint is manipulated in DispatchInputEvent, we're saving it
4859 // to use it for the doubleclick position check.
4860 const LayoutDeviceIntPoint pos = event.mRefPoint;
4861
4862 nsIWidget::ContentAndAPZEventStatus eventStatus = DispatchInputEvent(&event);
4863
4864 const bool defaultPrevented =
4865 eventStatus.mContentStatus == nsEventStatus_eConsumeNoDefault;
4866 // Check if mouse position in titlebar and doubleclick happened to
4867 // trigger defined action.
4868 if (!defaultPrevented && mDrawInTitlebar &&
4869 event.mButton == MouseButton::ePrimary && event.mClickCount == 2 &&
4870 mDraggableRegion.Contains(pos)) {
4871 DoTitlebarAction(nsXPLookAndFeel::TitlebarEvent::Double_Click, aEvent);
4872 }
4873 mLastMotionPressure = pressure;
4874
4875 // right menu click on linux should also pop up a context menu
4876 if (StaticPrefs::ui_context_menus_after_mouseup() &&
4877 eventStatus.mApzStatus != nsEventStatus_eConsumeNoDefault) {
4878 DispatchContextMenuEventFromMouseEvent(domButton, aEvent, refPoint);
4879 }
4880
4881 // Open window manager menu on PIP window to allow user
4882 // to place it on top / all workspaces.
4883 if (mAlwaysOnTop && aEvent->button == 3) {
4884 TryToShowNativeWindowMenu(aEvent);
4885 }
4886}
4887
4888void nsWindow::OnContainerFocusInEvent(GdkEventFocus* aEvent) {
4889 LOG("OnContainerFocusInEvent")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "OnContainerFocusInEvent", GetDebugTag
().get()); } } while (0)
;
4890
4891 // Unset the urgency hint, if possible
4892 GtkWidget* top_window = GetToplevelWidget();
4893 if (top_window && (gtk_widget_get_visible(top_window))) {
4894 SetUrgencyHint(top_window, false);
4895 }
4896
4897 // Return if being called within SetFocus because the focus manager
4898 // already knows that the window is active.
4899 if (gBlockActivateEvent) {
4900 LOG("activated notification is blocked")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "activated notification is blocked"
, GetDebugTag().get()); } } while (0)
;
4901 return;
4902 }
4903
4904 // If keyboard input will be accepted, the focus manager will call
4905 // SetFocus to set the correct window.
4906 gFocusWindow = nullptr;
4907
4908 DispatchActivateEvent();
4909
4910 if (!gFocusWindow) {
4911 // We don't really have a window for dispatching key events, but
4912 // setting a non-nullptr value here prevents OnButtonPressEvent() from
4913 // dispatching an activation notification if the widget is already
4914 // active.
4915 gFocusWindow = this;
4916 }
4917
4918 LOG("Events sent from focus in event")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "Events sent from focus in event", GetDebugTag
().get()); } } while (0)
;
4919}
4920
4921void nsWindow::OnContainerFocusOutEvent(GdkEventFocus* aEvent) {
4922 LOG("OnContainerFocusOutEvent")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "OnContainerFocusOutEvent", GetDebugTag
().get()); } } while (0)
;
4923
4924 if (IsTopLevelWindowType()) {
4925 // Rollup menus when a window is focused out unless a drag is occurring.
4926 // This check is because drags grab the keyboard and cause a focus out on
4927 // versions of GTK before 2.18.
4928 const bool shouldRollupMenus = [&] {
4929 nsCOMPtr<nsIDragService> dragService =
4930 do_GetService("@mozilla.org/widget/dragservice;1");
4931 nsCOMPtr<nsIDragSession> dragSession;
4932 dragService->GetCurrentSession(getter_AddRefs(dragSession));
4933 if (!dragSession) {
4934 return true;
4935 }
4936 // We also roll up when a drag is from a different application
4937 nsCOMPtr<nsINode> sourceNode;
4938 dragSession->GetSourceNode(getter_AddRefs(sourceNode));
4939 return !sourceNode;
4940 }();
4941
4942 if (shouldRollupMenus) {
4943 RollupAllMenus();
4944 }
4945
4946 if (RefPtr pm = nsXULPopupManager::GetInstance()) {
4947 pm->RollupTooltips();
4948 }
4949 }
4950
4951 if (gFocusWindow) {
4952 RefPtr<nsWindow> kungFuDeathGrip = gFocusWindow;
4953 if (gFocusWindow->mIMContext) {
4954 gFocusWindow->mIMContext->OnBlurWindow(gFocusWindow);
4955 }
4956 gFocusWindow = nullptr;
4957 }
4958
4959 DispatchDeactivateEvent();
4960
4961 if (IsChromeWindowTitlebar()) {
4962 // DispatchDeactivateEvent() ultimately results in a call to
4963 // BrowsingContext::SetIsActiveBrowserWindow(), which resets
4964 // the state. We call UpdateMozWindowActive() to keep it in
4965 // sync with GDK_WINDOW_STATE_FOCUSED.
4966 UpdateMozWindowActive();
4967 }
4968
4969 LOG("Done with container focus out")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "Done with container focus out", GetDebugTag
().get()); } } while (0)
;
4970}
4971
4972bool nsWindow::DispatchCommandEvent(nsAtom* aCommand) {
4973 nsEventStatus status;
4974 WidgetCommandEvent appCommandEvent(true, aCommand, this);
4975 DispatchEvent(&appCommandEvent, status);
4976 return TRUE(!(0));
4977}
4978
4979bool nsWindow::DispatchContentCommandEvent(EventMessage aMsg) {
4980 nsEventStatus status;
4981 WidgetContentCommandEvent event(true, aMsg, this);
4982 DispatchEvent(&event, status);
4983 return TRUE(!(0));
4984}
4985
4986WidgetEventTime nsWindow::GetWidgetEventTime(guint32 aEventTime) {
4987 return WidgetEventTime(GetEventTimeStamp(aEventTime));
4988}
4989
4990TimeStamp nsWindow::GetEventTimeStamp(guint32 aEventTime) {
4991 if (MOZ_UNLIKELY(!mGdkWindow)(__builtin_expect(!!(!mGdkWindow), 0))) {
4992 // nsWindow has been Destroy()ed.
4993 return TimeStamp::Now();
4994 }
4995 if (aEventTime == 0) {
4996 // Some X11 and GDK events may be received with a time of 0 to indicate
4997 // that they are synthetic events. Some input method editors do this.
4998 // In this case too, just return the current timestamp.
4999 return TimeStamp::Now();
5000 }
5001
5002 TimeStamp eventTimeStamp;
5003
5004 if (GdkIsWaylandDisplay()) {
5005 // Wayland compositors use monotonic time to set timestamps.
5006 int64_t timestampTime = g_get_monotonic_time() / 1000;
5007 guint32 refTimeTruncated = guint32(timestampTime);
5008
5009 timestampTime -= refTimeTruncated - aEventTime;
5010 int64_t tick =
5011 BaseTimeDurationPlatformUtils::TicksFromMilliseconds(timestampTime);
5012 eventTimeStamp = TimeStamp::FromSystemTime(tick);
5013 } else {
5014#ifdef MOZ_X111
5015 CurrentX11TimeGetter* getCurrentTime = GetCurrentTimeGetter();
5016 MOZ_ASSERT(getCurrentTime,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(getCurrentTime)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(getCurrentTime))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("getCurrentTime"
" (" "Null current time getter despite having a window" ")",
"/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 5017); AnnotateMozCrashReason("MOZ_ASSERT" "(" "getCurrentTime"
") (" "Null current time getter despite having a window" ")"
); do { *((volatile int*)__null) = 5017; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
5017 "Null current time getter despite having a window")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(getCurrentTime)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(getCurrentTime))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("getCurrentTime"
" (" "Null current time getter despite having a window" ")",
"/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 5017); AnnotateMozCrashReason("MOZ_ASSERT" "(" "getCurrentTime"
") (" "Null current time getter despite having a window" ")"
); do { *((volatile int*)__null) = 5017; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5018 eventTimeStamp =
5019 TimeConverter().GetTimeStampFromSystemTime(aEventTime, *getCurrentTime);
5020#endif
5021 }
5022 return eventTimeStamp;
5023}
5024
5025#ifdef MOZ_X111
5026mozilla::CurrentX11TimeGetter* nsWindow::GetCurrentTimeGetter() {
5027 MOZ_ASSERT(mGdkWindow, "Expected mGdkWindow to be set")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mGdkWindow)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mGdkWindow))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("mGdkWindow" " (" "Expected mGdkWindow to be set"
")", "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 5027); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mGdkWindow"
") (" "Expected mGdkWindow to be set" ")"); do { *((volatile
int*)__null) = 5027; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
5028 if (MOZ_UNLIKELY(!mCurrentTimeGetter)(__builtin_expect(!!(!mCurrentTimeGetter), 0))) {
5029 mCurrentTimeGetter = MakeUnique<CurrentX11TimeGetter>(mGdkWindow);
5030 }
5031 return mCurrentTimeGetter.get();
5032}
5033#endif
5034
5035gboolean nsWindow::OnKeyPressEvent(GdkEventKey* aEvent) {
5036 LOG("OnKeyPressEvent")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "OnKeyPressEvent", GetDebugTag().get
()); } } while (0)
;
5037
5038 KeymapWrapper::HandleKeyPressEvent(this, aEvent);
5039 return TRUE(!(0));
5040}
5041
5042gboolean nsWindow::OnKeyReleaseEvent(GdkEventKey* aEvent) {
5043 LOG("OnKeyReleaseEvent")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "OnKeyReleaseEvent", GetDebugTag().
get()); } } while (0)
;
5044 if (NS_WARN_IF(!KeymapWrapper::HandleKeyReleaseEvent(this, aEvent))NS_warn_if_impl(!KeymapWrapper::HandleKeyReleaseEvent(this, aEvent
), "!KeymapWrapper::HandleKeyReleaseEvent(this, aEvent)", "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 5044)
) {
5045 return FALSE(0);
5046 }
5047 return TRUE(!(0));
5048}
5049
5050void nsWindow::OnScrollEvent(GdkEventScroll* aEvent) {
5051 LOG("OnScrollEvent")do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "OnScrollEvent", GetDebugTag().get(
)); } } while (0)
;
5052
5053 // check to see if we should rollup
5054 if (CheckForRollup(aEvent->x_root, aEvent->y_root, true, false)) {
5055 return;
5056 }
5057
5058 // check for duplicate legacy scroll event, see GNOME bug 726878
5059 if (aEvent->direction != GDK_SCROLL_SMOOTH &&
5060 mLastScrollEventTime == aEvent->time) {
5061 LOG("[%d] duplicate legacy scroll event %d\n", aEvent->time,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "[%d] duplicate legacy scroll event %d\n"
, GetDebugTag().get(), aEvent->time, aEvent->direction)
; } } while (0)
5062 aEvent->direction)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "[%d] duplicate legacy scroll event %d\n"
, GetDebugTag().get(), aEvent->time, aEvent->direction)
; } } while (0)
;
5063 return;
5064 }
5065 WidgetWheelEvent wheelEvent(true, eWheel, this);
5066 wheelEvent.mDeltaMode = dom::WheelEvent_Binding::DOM_DELTA_LINE;
5067 switch (aEvent->direction) {
5068 case GDK_SCROLL_SMOOTH: {
5069 // As of GTK 3.4, all directional scroll events are provided by
5070 // the GDK_SCROLL_SMOOTH direction on XInput2 and Wayland devices.
5071 mLastScrollEventTime = aEvent->time;
5072
5073 // Special handling for touchpads to support flings
5074 // (also known as kinetic/inertial/momentum scrolling)
5075 GdkDevice* device = gdk_event_get_source_device((GdkEvent*)aEvent);
5076 GdkInputSource source = gdk_device_get_source(device);
5077 if (source == GDK_SOURCE_TOUCHSCREEN || source == GDK_SOURCE_TOUCHPAD ||
5078 mCurrentSynthesizedTouchpadPan.mTouchpadGesturePhase.isSome()) {
5079 if (StaticPrefs::apz_gtk_pangesture_enabled() &&
5080 gtk_check_version(3, 20, 0) == nullptr) {
5081 static auto sGdkEventIsScrollStopEvent =
5082 (gboolean(*)(const GdkEvent*))dlsym(
5083 RTLD_DEFAULT((void *) 0), "gdk_event_is_scroll_stop_event");
5084
5085 LOG("[%d] pan smooth event dx=%f dy=%f inprogress=%d\n", aEvent->time,do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "[%d] pan smooth event dx=%f dy=%f inprogress=%d\n"
, GetDebugTag().get(), aEvent->time, aEvent->delta_x, aEvent
->delta_y, mPanInProgress); } } while (0)
5086 aEvent->delta_x, aEvent->delta_y, mPanInProgress)do { const ::mozilla::LogModule* moz_real_module = IsPopup() ?
gWidgetPopupLog : gWidgetLog; if ((__builtin_expect(!!(mozilla
::detail::log_test(moz_real_module, mozilla::LogLevel::Debug)
), 0))) { mozilla::detail::log_print(moz_real_module, mozilla
::LogLevel::Debug, "%s: " "[%d] pan smooth event dx=%f dy=%f inprogress=%d\n"
, GetDebugTag().get(), aEvent->time, aEvent->delta_x, aEvent
->delta_y, mPanInProgress); } } while (0)
;
5087 auto eventType = PanGestureInput::PANGESTURE_PAN;
5088 if (sGdkEventIsScrollStopEvent((GdkEvent*)aEvent)) {
5089 eventType = PanGestureInput::PANGESTURE_END;
5090 mPanInProgress = false;
5091 } else if (!mPanInProgress) {
5092 eventType = PanGestureInput::PANGESTURE_START;
5093 mPanInProgress = true;
5094 } else if (mCurrentSynthesizedTouchpadPan.mTouchpadGesturePhase
5095 .isSome()) {
5096 switch (*mCurrentSynthesizedTouchpadPan.mTouchpadGesturePhase) {
5097 case PHASE_BEGIN:
5098 // we should never hit this because we'll hit the above case
5099 // before this.
5100 MOZ_ASSERT_UNREACHABLE()do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
")", "/var/lib/jenkins/workspace/firefox-scan-build/widget/gtk/nsWindow.cpp"
, 5100); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " ")"); do { *((volatile int*)__null
) = 5100; __attribute__((nomerge)) ::abort(); } while (false)
; } } while (false)
;
5101 eventT