Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/widget/gtk/nsWindow.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* vim:expandtab:shiftwidth=4:tabstop=4:
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 "mozilla/ArrayUtils.h"
11
#include "mozilla/EventForwards.h"
12
#include "mozilla/MiscEvents.h"
13
#include "mozilla/MouseEvents.h"
14
#include "mozilla/RefPtr.h"
15
#include "mozilla/TextEventDispatcher.h"
16
#include "mozilla/TextEvents.h"
17
#include "mozilla/TimeStamp.h"
18
#include "mozilla/TouchEvents.h"
19
#include "mozilla/UniquePtrExtensions.h"
20
#include "mozilla/WidgetUtils.h"
21
#include "mozilla/dom/WheelEventBinding.h"
22
#include <algorithm>
23
24
#include "GeckoProfiler.h"
25
26
#include "prlink.h"
27
#include "nsGTKToolkit.h"
28
#include "nsIRollupListener.h"
29
#include "nsINode.h"
30
31
#include "nsWidgetsCID.h"
32
#include "nsDragService.h"
33
#include "nsIWidgetListener.h"
34
#include "nsIScreenManager.h"
35
#include "SystemTimeConverter.h"
36
37
#include "nsGtkKeyUtils.h"
38
#include "nsGtkCursors.h"
39
#include "ScreenHelperGTK.h"
40
41
#include <gtk/gtk.h>
42
#include <gtk/gtkx.h>
43
44
#ifdef MOZ_WAYLAND
45
#include <gdk/gdkwayland.h>
46
#endif /* MOZ_WAYLAND */
47
48
#ifdef MOZ_X11
49
#include <gdk/gdkx.h>
50
#include <X11/Xatom.h>
51
#include <X11/extensions/XShm.h>
52
#include <X11/extensions/shape.h>
53
#include <gdk/gdkkeysyms-compat.h>
54
#endif /* MOZ_X11 */
55
56
#include <gdk/gdkkeysyms.h>
57
58
#if defined(MOZ_WAYLAND)
59
#include <gdk/gdkwayland.h>
60
#endif
61
62
#include "nsGkAtoms.h"
63
64
#ifdef MOZ_ENABLE_STARTUP_NOTIFICATION
65
#define SN_API_NOT_YET_FROZEN
66
#include <startup-notification-1.0/libsn/sn.h>
67
#endif
68
69
#include "mozilla/Assertions.h"
70
#include "mozilla/Likely.h"
71
#include "mozilla/Preferences.h"
72
#include "nsIPrefService.h"
73
#include "nsIGConfService.h"
74
#include "nsIServiceManager.h"
75
#include "nsGfxCIID.h"
76
#include "nsGtkUtils.h"
77
#include "nsIObserverService.h"
78
#include "mozilla/layers/LayersTypes.h"
79
#include "nsIIdleServiceInternal.h"
80
#include "nsIPropertyBag2.h"
81
#include "GLContext.h"
82
#include "gfx2DGlue.h"
83
84
#ifdef ACCESSIBILITY
85
#include "mozilla/a11y/Accessible.h"
86
#include "mozilla/a11y/Platform.h"
87
#include "nsAccessibilityService.h"
88
89
using namespace mozilla;
90
using namespace mozilla::widget;
91
#endif
92
93
/* For SetIcon */
94
#include "nsAppDirectoryServiceDefs.h"
95
#include "nsString.h"
96
#include "nsIFile.h"
97
98
/* SetCursor(imgIContainer*) */
99
#include <gdk/gdk.h>
100
#include <wchar.h>
101
#include "imgIContainer.h"
102
#include "nsGfxCIID.h"
103
#include "nsImageToPixbuf.h"
104
#include "nsIInterfaceRequestorUtils.h"
105
#include "ClientLayerManager.h"
106
107
#include "gfxPlatformGtk.h"
108
#include "gfxContext.h"
109
#include "gfxImageSurface.h"
110
#include "gfxUtils.h"
111
#include "Layers.h"
112
#include "GLContextProvider.h"
113
#include "mozilla/gfx/2D.h"
114
#include "mozilla/gfx/HelpersCairo.h"
115
#include "mozilla/gfx/GPUProcessManager.h"
116
#include "mozilla/layers/CompositorBridgeParent.h"
117
#include "mozilla/layers/CompositorThread.h"
118
119
#ifdef MOZ_X11
120
#include "GLContextGLX.h" // for GLContextGLX::FindVisual()
121
#include "GtkCompositorWidget.h"
122
#include "gfxXlibSurface.h"
123
#include "WindowSurfaceX11Image.h"
124
#include "WindowSurfaceX11SHM.h"
125
#include "WindowSurfaceXRender.h"
126
#endif // MOZ_X11
127
#ifdef MOZ_WAYLAND
128
#include "nsIClipboard.h"
129
#endif
130
131
#include "nsShmImage.h"
132
#include "gtkdrawing.h"
133
134
#include "NativeKeyBindings.h"
135
136
#include <dlfcn.h>
137
138
using namespace mozilla;
139
using namespace mozilla::gfx;
140
using namespace mozilla::widget;
141
using namespace mozilla::layers;
142
using mozilla::gl::GLContext;
143
using mozilla::gl::GLContextGLX;
144
145
// Don't put more than this many rects in the dirty region, just fluff
146
// out to the bounding-box if there are more
147
#define MAX_RECTS_IN_REGION 100
148
149
const gint kEvents = GDK_EXPOSURE_MASK | GDK_STRUCTURE_MASK |
150
                     GDK_VISIBILITY_NOTIFY_MASK |
151
                     GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
152
                     GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
153
#if GTK_CHECK_VERSION(3,4,0)
154
                     GDK_SMOOTH_SCROLL_MASK |
155
                     GDK_TOUCH_MASK |
156
#endif
157
                     GDK_SCROLL_MASK |
158
                     GDK_POINTER_MOTION_MASK |
159
                     GDK_PROPERTY_CHANGE_MASK;
160
161
/* utility functions */
162
static bool       is_mouse_in_window(GdkWindow* aWindow,
163
                                     gdouble aMouseX, gdouble aMouseY);
164
static nsWindow  *get_window_for_gtk_widget(GtkWidget *widget);
165
static nsWindow  *get_window_for_gdk_window(GdkWindow *window);
166
static GtkWidget *get_gtk_widget_for_gdk_window(GdkWindow *window);
167
static GdkCursor *get_gtk_cursor(nsCursor aCursor);
168
169
static GdkWindow *get_inner_gdk_window (GdkWindow *aWindow,
170
                                        gint x, gint y,
171
                                        gint *retx, gint *rety);
172
173
static int    is_parent_ungrab_enter(GdkEventCrossing *aEvent);
174
static int    is_parent_grab_leave(GdkEventCrossing *aEvent);
175
176
/* callbacks from widgets */
177
static gboolean expose_event_cb           (GtkWidget *widget,
178
                                           cairo_t *rect);
179
static gboolean configure_event_cb        (GtkWidget *widget,
180
                                           GdkEventConfigure *event);
181
static void     container_unrealize_cb    (GtkWidget *widget);
182
static void     size_allocate_cb          (GtkWidget *widget,
183
                                           GtkAllocation *allocation);
184
static gboolean delete_event_cb           (GtkWidget *widget,
185
                                           GdkEventAny *event);
186
static gboolean enter_notify_event_cb     (GtkWidget *widget,
187
                                           GdkEventCrossing *event);
188
static gboolean leave_notify_event_cb     (GtkWidget *widget,
189
                                           GdkEventCrossing *event);
190
static gboolean motion_notify_event_cb    (GtkWidget *widget,
191
                                           GdkEventMotion *event);
192
static gboolean button_press_event_cb     (GtkWidget *widget,
193
                                           GdkEventButton *event);
194
static gboolean button_release_event_cb   (GtkWidget *widget,
195
                                           GdkEventButton *event);
196
static gboolean focus_in_event_cb         (GtkWidget *widget,
197
                                           GdkEventFocus *event);
198
static gboolean focus_out_event_cb        (GtkWidget *widget,
199
                                           GdkEventFocus *event);
200
static gboolean key_press_event_cb        (GtkWidget *widget,
201
                                           GdkEventKey *event);
202
static gboolean key_release_event_cb      (GtkWidget *widget,
203
                                           GdkEventKey *event);
204
static gboolean property_notify_event_cb  (GtkWidget *widget,
205
                                           GdkEventProperty *event);
206
static gboolean scroll_event_cb           (GtkWidget *widget,
207
                                           GdkEventScroll *event);
208
static gboolean visibility_notify_event_cb(GtkWidget *widget,
209
                                           GdkEventVisibility *event);
210
static void     hierarchy_changed_cb      (GtkWidget *widget,
211
                                           GtkWidget *previous_toplevel);
212
static gboolean window_state_event_cb     (GtkWidget *widget,
213
                                           GdkEventWindowState *event);
214
static void     settings_changed_cb       (GtkSettings *settings,
215
                                           GParamSpec *pspec,
216
                                           nsWindow *data);
217
static void     check_resize_cb           (GtkContainer* container,
218
                                           gpointer user_data);
219
static void     screen_composited_changed_cb     (GdkScreen* screen,
220
                                                  gpointer user_data);
221
static void     widget_composited_changed_cb     (GtkWidget* widget,
222
                                                  gpointer user_data);
223
224
static void     scale_changed_cb          (GtkWidget* widget,
225
                                           GParamSpec* aPSpec,
226
                                           gpointer aPointer);
227
#if GTK_CHECK_VERSION(3,4,0)
228
static gboolean touch_event_cb            (GtkWidget* aWidget,
229
                                           GdkEventTouch* aEvent);
230
#endif
231
static nsWindow* GetFirstNSWindowForGDKWindow (GdkWindow *aGdkWindow);
232
233
#ifdef __cplusplus
234
extern "C" {
235
#endif /* __cplusplus */
236
#ifdef MOZ_X11
237
static GdkFilterReturn popup_take_focus_filter (GdkXEvent *gdk_xevent,
238
                                                GdkEvent *event,
239
                                                gpointer data);
240
#endif /* MOZ_X11 */
241
#ifdef __cplusplus
242
}
243
#endif /* __cplusplus */
244
245
static gboolean drag_motion_event_cb      (GtkWidget *aWidget,
246
                                           GdkDragContext *aDragContext,
247
                                           gint aX,
248
                                           gint aY,
249
                                           guint aTime,
250
                                           gpointer aData);
251
static void     drag_leave_event_cb       (GtkWidget *aWidget,
252
                                           GdkDragContext *aDragContext,
253
                                           guint aTime,
254
                                           gpointer aData);
255
static gboolean drag_drop_event_cb        (GtkWidget *aWidget,
256
                                           GdkDragContext *aDragContext,
257
                                           gint aX,
258
                                           gint aY,
259
                                           guint aTime,
260
                                           gpointer aData);
261
static void    drag_data_received_event_cb(GtkWidget *aWidget,
262
                                           GdkDragContext *aDragContext,
263
                                           gint aX,
264
                                           gint aY,
265
                                           GtkSelectionData  *aSelectionData,
266
                                           guint aInfo,
267
                                           guint32 aTime,
268
                                           gpointer aData);
269
270
/* initialization static functions */
271
static nsresult    initialize_prefs        (void);
272
273
static guint32 sLastUserInputTime = GDK_CURRENT_TIME;
274
static guint32 sRetryGrabTime;
275
276
static SystemTimeConverter<guint32>&
277
0
TimeConverter() {
278
0
    static SystemTimeConverter<guint32> sTimeConverterSingleton;
279
0
    return sTimeConverterSingleton;
280
0
}
281
282
nsWindow::CSDSupportLevel nsWindow::sCSDSupportLevel = CSD_SUPPORT_UNKNOWN;
283
284
namespace mozilla {
285
286
class CurrentX11TimeGetter
287
{
288
public:
289
    explicit CurrentX11TimeGetter(GdkWindow* aWindow)
290
        : mWindow(aWindow)
291
        , mAsyncUpdateStart()
292
0
    {
293
0
    }
294
295
    guint32 GetCurrentTime() const
296
0
    {
297
0
        return gdk_x11_get_server_time(mWindow);
298
0
    }
299
300
    void GetTimeAsyncForPossibleBackwardsSkew(const TimeStamp& aNow)
301
0
    {
302
0
        // Check for in-flight request
303
0
        if (!mAsyncUpdateStart.IsNull()) {
304
0
            return;
305
0
        }
306
0
        mAsyncUpdateStart = aNow;
307
0
308
0
        Display* xDisplay = GDK_WINDOW_XDISPLAY(mWindow);
309
0
        Window xWindow = GDK_WINDOW_XID(mWindow);
310
0
        unsigned char c = 'a';
311
0
        Atom timeStampPropAtom = TimeStampPropAtom();
312
0
        XChangeProperty(xDisplay, xWindow, timeStampPropAtom,
313
0
                        timeStampPropAtom, 8, PropModeReplace, &c, 1);
314
0
        XFlush(xDisplay);
315
0
    }
316
317
    gboolean PropertyNotifyHandler(GtkWidget* aWidget,
318
                                   GdkEventProperty* aEvent)
319
0
    {
320
0
        if (aEvent->atom !=
321
0
            gdk_x11_xatom_to_atom(TimeStampPropAtom())) {
322
0
            return FALSE;
323
0
        }
324
0
325
0
        guint32 eventTime = aEvent->time;
326
0
        TimeStamp lowerBound = mAsyncUpdateStart;
327
0
328
0
        TimeConverter().CompensateForBackwardsSkew(eventTime, lowerBound);
329
0
        mAsyncUpdateStart = TimeStamp();
330
0
        return TRUE;
331
0
    }
332
333
private:
334
0
    static Atom TimeStampPropAtom() {
335
0
        return gdk_x11_get_xatom_by_name_for_display(
336
0
            gdk_display_get_default(), "GDK_TIMESTAMP_PROP");
337
0
    }
338
339
    // This is safe because this class is stored as a member of mWindow and
340
    // won't outlive it.
341
    GdkWindow* mWindow;
342
    TimeStamp  mAsyncUpdateStart;
343
};
344
345
} // namespace mozilla
346
347
static NS_DEFINE_IID(kCDragServiceCID,  NS_DRAGSERVICE_CID);
348
349
// The window from which the focus manager asks us to dispatch key events.
350
static nsWindow         *gFocusWindow          = nullptr;
351
static bool              gBlockActivateEvent   = false;
352
static bool              gGlobalsInitialized   = false;
353
static bool              gRaiseWindows         = true;
354
355
#if GTK_CHECK_VERSION(3,4,0)
356
static uint32_t          gLastTouchID = 0;
357
#endif
358
359
0
#define NS_WINDOW_TITLE_MAX_LENGTH 4095
360
361
// If after selecting profile window, the startup fail, please refer to
362
// http://bugzilla.gnome.org/show_bug.cgi?id=88940
363
364
// needed for imgIContainer cursors
365
// GdkDisplay* was added in 2.2
366
typedef struct _GdkDisplay GdkDisplay;
367
368
0
#define kWindowPositionSlop 20
369
370
// cursor cache
371
static GdkCursor *gCursorCache[eCursorCount];
372
373
static GtkWidget *gInvisibleContainer = nullptr;
374
375
// Sometimes this actually also includes the state of the modifier keys, but
376
// only the button state bits are used.
377
static guint gButtonState;
378
379
static inline int32_t
380
GetBitmapStride(int32_t width)
381
0
{
382
0
#if defined(MOZ_X11)
383
0
  return (width+7)/8;
384
#else
385
  return cairo_format_stride_for_width(CAIRO_FORMAT_A1, width);
386
#endif
387
}
388
389
static inline bool TimestampIsNewerThan(guint32 a, guint32 b)
390
0
{
391
0
    // Timestamps are just the least significant bits of a monotonically
392
0
    // increasing function, and so the use of unsigned overflow arithmetic.
393
0
    return a - b <= G_MAXUINT32/2;
394
0
}
395
396
static void
397
UpdateLastInputEventTime(void *aGdkEvent)
398
0
{
399
0
    nsCOMPtr<nsIIdleServiceInternal> idleService =
400
0
        do_GetService("@mozilla.org/widget/idleservice;1");
401
0
    if (idleService) {
402
0
        idleService->ResetIdleTimeOut(0);
403
0
    }
404
0
405
0
    guint timestamp = gdk_event_get_time(static_cast<GdkEvent*>(aGdkEvent));
406
0
    if (timestamp == GDK_CURRENT_TIME)
407
0
        return;
408
0
409
0
    sLastUserInputTime = timestamp;
410
0
}
411
412
nsWindow::nsWindow()
413
0
{
414
0
    mIsTopLevel          = false;
415
0
    mIsDestroyed         = false;
416
0
    mListenForResizes    = false;
417
0
    mNeedsDispatchResized = false;
418
0
    mIsShown             = false;
419
0
    mNeedsShow           = false;
420
0
    mEnabled             = true;
421
0
    mCreated             = false;
422
0
#if GTK_CHECK_VERSION(3,4,0)
423
0
    mHandleTouchEvent    = false;
424
0
#endif
425
0
    mIsDragPopup         = false;
426
0
    mIsX11Display        = GDK_IS_X11_DISPLAY(gdk_display_get_default());
427
0
428
0
    mContainer           = nullptr;
429
0
    mGdkWindow           = nullptr;
430
0
    mShell               = nullptr;
431
0
    mCompositorWidgetDelegate = nullptr;
432
0
    mHasMappedToplevel   = false;
433
0
    mIsFullyObscured     = false;
434
0
    mRetryPointerGrab    = false;
435
0
    mWindowType          = eWindowType_child;
436
0
    mSizeState           = nsSizeMode_Normal;
437
0
    mLastSizeMode        = nsSizeMode_Normal;
438
0
    mSizeConstraints.mMaxSize = GetSafeWindowSize(mSizeConstraints.mMaxSize);
439
0
440
0
#ifdef MOZ_X11
441
0
    mOldFocusWindow      = 0;
442
0
443
0
    mXDisplay = nullptr;
444
0
    mXWindow  = X11None;
445
0
    mXVisual  = nullptr;
446
0
    mXDepth   = 0;
447
0
#endif /* MOZ_X11 */
448
0
449
0
    if (!gGlobalsInitialized) {
450
0
        gGlobalsInitialized = true;
451
0
452
0
        // It's OK if either of these fail, but it may not be one day.
453
0
        initialize_prefs();
454
0
455
#ifdef MOZ_WAYLAND
456
        // Wayland provides clipboard data to application on focus-in event
457
        // so we need to init our clipboard hooks before we create window
458
        // and get focus.
459
        if (!mIsX11Display) {
460
            nsCOMPtr<nsIClipboard> clipboard =
461
                do_GetService("@mozilla.org/widget/clipboard;1");
462
            NS_ASSERTION(clipboard, "Failed to init clipboard!");
463
        }
464
#endif
465
    }
466
0
467
0
    mLastMotionPressure = 0;
468
0
469
0
#ifdef ACCESSIBILITY
470
0
    mRootAccessible  = nullptr;
471
0
#endif
472
0
473
0
    mIsTransparent = false;
474
0
    mTransparencyBitmap = nullptr;
475
0
476
0
    mTransparencyBitmapWidth  = 0;
477
0
    mTransparencyBitmapHeight = 0;
478
0
479
0
#if GTK_CHECK_VERSION(3,4,0)
480
0
    mLastScrollEventTime = GDK_CURRENT_TIME;
481
0
#endif
482
0
    mPendingConfigures = 0;
483
0
    mCSDSupportLevel = CSD_SUPPORT_NONE;
484
0
    mDrawInTitlebar = false;
485
0
486
0
    mHasAlphaVisual = false;
487
0
}
488
489
nsWindow::~nsWindow()
490
0
{
491
0
    LOG(("nsWindow::~nsWindow() [%p]\n", (void *)this));
492
0
493
0
    delete[] mTransparencyBitmap;
494
0
    mTransparencyBitmap = nullptr;
495
0
496
0
    Destroy();
497
0
}
498
499
/* static */ void
500
nsWindow::ReleaseGlobals()
501
0
{
502
0
  for (auto & cursor : gCursorCache) {
503
0
    if (cursor) {
504
0
      g_object_unref(cursor);
505
0
      cursor = nullptr;
506
0
    }
507
0
  }
508
0
}
509
510
void
511
nsWindow::CommonCreate(nsIWidget *aParent, bool aListenForResizes)
512
0
{
513
0
    mParent = aParent;
514
0
    mListenForResizes = aListenForResizes;
515
0
    mCreated = true;
516
0
}
517
518
void
519
nsWindow::DispatchActivateEvent(void)
520
0
{
521
0
    NS_ASSERTION(mContainer || mIsDestroyed,
522
0
                 "DispatchActivateEvent only intended for container windows");
523
0
524
0
#ifdef ACCESSIBILITY
525
0
    DispatchActivateEventAccessible();
526
0
#endif //ACCESSIBILITY
527
0
528
0
    if (mWidgetListener)
529
0
      mWidgetListener->WindowActivated();
530
0
}
531
532
void
533
nsWindow::DispatchDeactivateEvent(void)
534
0
{
535
0
    if (mWidgetListener)
536
0
      mWidgetListener->WindowDeactivated();
537
0
538
0
#ifdef ACCESSIBILITY
539
0
    DispatchDeactivateEventAccessible();
540
0
#endif //ACCESSIBILITY
541
0
}
542
543
void
544
nsWindow::DispatchResized()
545
0
{
546
0
    mNeedsDispatchResized = false;
547
0
    if (mWidgetListener) {
548
0
        mWidgetListener->WindowResized(this, mBounds.width, mBounds.height);
549
0
    }
550
0
    if (mAttachedWidgetListener) {
551
0
        mAttachedWidgetListener->WindowResized(this,
552
0
                                               mBounds.width, mBounds.height);
553
0
    }
554
0
}
555
556
void
557
nsWindow::MaybeDispatchResized()
558
0
{
559
0
    if (mNeedsDispatchResized && !mIsDestroyed) {
560
0
        DispatchResized();
561
0
    }
562
0
}
563
564
nsIWidgetListener*
565
nsWindow::GetListener()
566
0
{
567
0
    return mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener;
568
0
}
569
570
nsresult
571
nsWindow::DispatchEvent(WidgetGUIEvent* aEvent, nsEventStatus& aStatus)
572
0
{
573
#ifdef DEBUG
574
    debug_DumpEvent(stdout, aEvent->mWidget, aEvent,
575
                    "something", 0);
576
#endif
577
    aStatus = nsEventStatus_eIgnore;
578
0
    nsIWidgetListener* listener = GetListener();
579
0
    if (listener) {
580
0
      aStatus = listener->HandleEvent(aEvent, mUseAttachedEvents);
581
0
    }
582
0
583
0
    return NS_OK;
584
0
}
585
586
void
587
nsWindow::OnDestroy(void)
588
0
{
589
0
    if (mOnDestroyCalled)
590
0
        return;
591
0
592
0
    mOnDestroyCalled = true;
593
0
594
0
    // Prevent deletion.
595
0
    nsCOMPtr<nsIWidget> kungFuDeathGrip = this;
596
0
597
0
    // release references to children, device context, toolkit + app shell
598
0
    nsBaseWidget::OnDestroy();
599
0
600
0
    // Remove association between this object and its parent and siblings.
601
0
    nsBaseWidget::Destroy();
602
0
    mParent = nullptr;
603
0
604
0
    NotifyWindowDestroyed();
605
0
}
606
607
bool
608
nsWindow::AreBoundsSane(void)
609
0
{
610
0
    if (mBounds.width > 0 && mBounds.height > 0)
611
0
        return true;
612
0
613
0
    return false;
614
0
}
615
616
static GtkWidget*
617
EnsureInvisibleContainer()
618
0
{
619
0
    if (!gInvisibleContainer) {
620
0
        // GtkWidgets need to be anchored to a GtkWindow to be realized (to
621
0
        // have a window).  Using GTK_WINDOW_POPUP rather than
622
0
        // GTK_WINDOW_TOPLEVEL in the hope that POPUP results in less
623
0
        // initialization and window manager interaction.
624
0
        GtkWidget* window = gtk_window_new(GTK_WINDOW_POPUP);
625
0
        gInvisibleContainer = moz_container_new();
626
0
        gtk_container_add(GTK_CONTAINER(window), gInvisibleContainer);
627
0
        gtk_widget_realize(gInvisibleContainer);
628
0
629
0
    }
630
0
    return gInvisibleContainer;
631
0
}
632
633
static void
634
CheckDestroyInvisibleContainer()
635
0
{
636
0
    MOZ_ASSERT(gInvisibleContainer, "oh, no");
637
0
638
0
    if (!gdk_window_peek_children(gtk_widget_get_window(gInvisibleContainer))) {
639
0
        // No children, so not in use.
640
0
        // Make sure to destroy the GtkWindow also.
641
0
        gtk_widget_destroy(gtk_widget_get_parent(gInvisibleContainer));
642
0
        gInvisibleContainer = nullptr;
643
0
    }
644
0
}
645
646
// Change the containing GtkWidget on a sub-hierarchy of GdkWindows belonging
647
// to aOldWidget and rooted at aWindow, and reparent any child GtkWidgets of
648
// the GdkWindow hierarchy to aNewWidget.
649
static void
650
SetWidgetForHierarchy(GdkWindow *aWindow,
651
                      GtkWidget *aOldWidget,
652
                      GtkWidget *aNewWidget)
653
0
{
654
0
    gpointer data;
655
0
    gdk_window_get_user_data(aWindow, &data);
656
0
657
0
    if (data != aOldWidget) {
658
0
        if (!GTK_IS_WIDGET(data))
659
0
            return;
660
0
661
0
        auto* widget = static_cast<GtkWidget*>(data);
662
0
        if (gtk_widget_get_parent(widget) != aOldWidget)
663
0
            return;
664
0
665
0
        // This window belongs to a child widget, which will no longer be a
666
0
        // child of aOldWidget.
667
0
        gtk_widget_reparent(widget, aNewWidget);
668
0
669
0
        return;
670
0
    }
671
0
672
0
    GList *children = gdk_window_get_children(aWindow);
673
0
    for(GList *list = children; list; list = list->next) {
674
0
        SetWidgetForHierarchy(GDK_WINDOW(list->data), aOldWidget, aNewWidget);
675
0
    }
676
0
    g_list_free(children);
677
0
678
0
    gdk_window_set_user_data(aWindow, aNewWidget);
679
0
}
680
681
// Walk the list of child windows and call destroy on them.
682
void
683
nsWindow::DestroyChildWindows()
684
0
{
685
0
    if (!mGdkWindow)
686
0
        return;
687
0
688
0
    while (GList *children = gdk_window_peek_children(mGdkWindow)) {
689
0
        GdkWindow *child = GDK_WINDOW(children->data);
690
0
        nsWindow *kid = get_window_for_gdk_window(child);
691
0
        if (kid) {
692
0
            kid->Destroy();
693
0
        } else {
694
0
            // This child is not an nsWindow.
695
0
            // Destroy the child GtkWidget.
696
0
            gpointer data;
697
0
            gdk_window_get_user_data(child, &data);
698
0
            if (GTK_IS_WIDGET(data)) {
699
0
                gtk_widget_destroy(static_cast<GtkWidget*>(data));
700
0
            }
701
0
        }
702
0
    }
703
0
}
704
705
void
706
nsWindow::Destroy()
707
0
{
708
0
    if (mIsDestroyed || !mCreated)
709
0
        return;
710
0
711
0
    LOG(("nsWindow::Destroy [%p]\n", (void *)this));
712
0
    mIsDestroyed = true;
713
0
    mCreated = false;
714
0
715
0
    /** Need to clean our LayerManager up while still alive */
716
0
    if (mLayerManager) {
717
0
        mLayerManager->Destroy();
718
0
    }
719
0
    mLayerManager = nullptr;
720
0
721
0
    // It is safe to call DestroyeCompositor several times (here and
722
0
    // in the parent class) since it will take effect only once.
723
0
    // The reason we call it here is because on gtk platforms we need
724
0
    // to destroy the compositor before we destroy the gdk window (which
725
0
    // destroys the the gl context attached to it).
726
0
    DestroyCompositor();
727
0
728
0
#ifdef MOZ_X11
729
0
    // Ensure any resources assigned to the window get cleaned up first
730
0
    // to avoid double-freeing.
731
0
    mSurfaceProvider.CleanupResources();
732
0
#endif
733
0
734
0
    ClearCachedResources();
735
0
736
0
    g_signal_handlers_disconnect_by_func(gtk_settings_get_default(),
737
0
                                         FuncToGpointer(settings_changed_cb),
738
0
                                         this);
739
0
740
0
    nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
741
0
    if (rollupListener) {
742
0
        nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget();
743
0
        if (static_cast<nsIWidget *>(this) == rollupWidget) {
744
0
            rollupListener->Rollup(0, false, nullptr, nullptr);
745
0
        }
746
0
    }
747
0
748
0
    // dragService will be null after shutdown of the service manager.
749
0
    RefPtr<nsDragService> dragService = nsDragService::GetInstance();
750
0
    if (dragService && this == dragService->GetMostRecentDestWindow()) {
751
0
        dragService->ScheduleLeaveEvent();
752
0
    }
753
0
754
0
    NativeShow(false);
755
0
756
0
    if (mIMContext) {
757
0
        mIMContext->OnDestroyWindow(this);
758
0
    }
759
0
760
0
    // make sure that we remove ourself as the focus window
761
0
    if (gFocusWindow == this) {
762
0
        LOGFOCUS(("automatically losing focus...\n"));
763
0
        gFocusWindow = nullptr;
764
0
    }
765
0
766
0
    GtkWidget *owningWidget = GetMozContainerWidget();
767
0
    if (mShell) {
768
0
        gtk_widget_destroy(mShell);
769
0
        mShell = nullptr;
770
0
        mContainer = nullptr;
771
0
        MOZ_ASSERT(!mGdkWindow,
772
0
                   "mGdkWindow should be NULL when mContainer is destroyed");
773
0
    }
774
0
    else if (mContainer) {
775
0
        gtk_widget_destroy(GTK_WIDGET(mContainer));
776
0
        mContainer = nullptr;
777
0
        MOZ_ASSERT(!mGdkWindow,
778
0
                   "mGdkWindow should be NULL when mContainer is destroyed");
779
0
    }
780
0
    else if (mGdkWindow) {
781
0
        // Destroy child windows to ensure that their mThebesSurfaces are
782
0
        // released and to remove references from GdkWindows back to their
783
0
        // container widget.  (OnContainerUnrealize() does this when the
784
0
        // MozContainer widget is destroyed.)
785
0
        DestroyChildWindows();
786
0
787
0
        gdk_window_set_user_data(mGdkWindow, nullptr);
788
0
        g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr);
789
0
        gdk_window_destroy(mGdkWindow);
790
0
        mGdkWindow = nullptr;
791
0
    }
792
0
793
0
    if (gInvisibleContainer && owningWidget == gInvisibleContainer) {
794
0
        CheckDestroyInvisibleContainer();
795
0
    }
796
0
797
0
#ifdef ACCESSIBILITY
798
0
     if (mRootAccessible) {
799
0
         mRootAccessible = nullptr;
800
0
     }
801
0
#endif
802
0
803
0
    // Save until last because OnDestroy() may cause us to be deleted.
804
0
    OnDestroy();
805
0
}
806
807
nsIWidget *
808
nsWindow::GetParent(void)
809
0
{
810
0
    return mParent;
811
0
}
812
813
float
814
nsWindow::GetDPI()
815
0
{
816
0
    float dpi = 96.0f;
817
0
    nsCOMPtr<nsIScreen> screen = GetWidgetScreen();
818
0
    if (screen) {
819
0
        screen->GetDpi(&dpi);
820
0
    }
821
0
    return dpi;
822
0
}
823
824
double
825
nsWindow::GetDefaultScaleInternal()
826
0
{
827
0
    return GdkScaleFactor() * gfxPlatformGtk::GetFontScaleFactor();
828
0
}
829
830
DesktopToLayoutDeviceScale
831
nsWindow::GetDesktopToDeviceScale()
832
0
{
833
#ifdef MOZ_WAYLAND
834
    GdkDisplay* gdkDisplay = gdk_display_get_default();
835
    if (GDK_IS_WAYLAND_DISPLAY(gdkDisplay)) {
836
        return DesktopToLayoutDeviceScale(GdkScaleFactor());
837
    }
838
#endif
839
840
0
    // In Gtk/X11, we manage windows using device pixels.
841
0
    return DesktopToLayoutDeviceScale(1.0);
842
0
}
843
844
void
845
nsWindow::SetParent(nsIWidget *aNewParent)
846
0
{
847
0
    if (!mGdkWindow) {
848
0
        MOZ_ASSERT_UNREACHABLE("The native window has already been destroyed");
849
0
        return;
850
0
    }
851
0
852
0
    if (mContainer) {
853
0
        // FIXME bug 1469183
854
0
        NS_ERROR("nsWindow should not have a container here");
855
0
        return;
856
0
    }
857
0
858
0
    nsCOMPtr<nsIWidget> kungFuDeathGrip = this;
859
0
    if (mParent) {
860
0
        mParent->RemoveChild(this);
861
0
    }
862
0
863
0
    mParent = aNewParent;
864
0
865
0
    GtkWidget* oldContainer = GetMozContainerWidget();
866
0
    if (!oldContainer) {
867
0
        // The GdkWindows have been destroyed so there is nothing else to
868
0
        // reparent.
869
0
        MOZ_ASSERT(gdk_window_is_destroyed(mGdkWindow),
870
0
                   "live GdkWindow with no widget");
871
0
        return;
872
0
    }
873
0
874
0
    if (aNewParent) {
875
0
        aNewParent->AddChild(this);
876
0
        ReparentNativeWidget(aNewParent);
877
0
    } else {
878
0
        // aNewParent is nullptr, but reparent to a hidden window to avoid
879
0
        // destroying the GdkWindow and its descendants.
880
0
        // An invisible container widget is needed to hold descendant
881
0
        // GtkWidgets.
882
0
        GtkWidget* newContainer = EnsureInvisibleContainer();
883
0
        GdkWindow* newParentWindow = gtk_widget_get_window(newContainer);
884
0
        ReparentNativeWidgetInternal(aNewParent, newContainer, newParentWindow,
885
0
                                     oldContainer);
886
0
    }
887
0
}
888
889
bool
890
nsWindow::WidgetTypeSupportsAcceleration()
891
0
{
892
0
  return !IsSmallPopup();
893
0
}
894
895
void
896
nsWindow::ReparentNativeWidget(nsIWidget* aNewParent)
897
0
{
898
0
    MOZ_ASSERT(aNewParent, "null widget");
899
0
    NS_ASSERTION(!mIsDestroyed, "");
900
0
    NS_ASSERTION(!static_cast<nsWindow*>(aNewParent)->mIsDestroyed, "");
901
0
902
0
    GtkWidget* oldContainer = GetMozContainerWidget();
903
0
    if (!oldContainer) {
904
0
        // The GdkWindows have been destroyed so there is nothing else to
905
0
        // reparent.
906
0
        MOZ_ASSERT(gdk_window_is_destroyed(mGdkWindow),
907
0
                   "live GdkWindow with no widget");
908
0
        return;
909
0
    }
910
0
    MOZ_ASSERT(!gdk_window_is_destroyed(mGdkWindow),
911
0
               "destroyed GdkWindow with widget");
912
0
913
0
    auto* newParent = static_cast<nsWindow*>(aNewParent);
914
0
    GdkWindow* newParentWindow = newParent->mGdkWindow;
915
0
    GtkWidget* newContainer = newParent->GetMozContainerWidget();
916
0
    GtkWindow* shell = GTK_WINDOW(mShell);
917
0
918
0
    if (shell && gtk_window_get_transient_for(shell)) {
919
0
      GtkWindow* topLevelParent =
920
0
          GTK_WINDOW(gtk_widget_get_toplevel(newContainer));
921
0
      gtk_window_set_transient_for(shell, topLevelParent);
922
0
    }
923
0
924
0
    ReparentNativeWidgetInternal(aNewParent, newContainer, newParentWindow,
925
0
                                 oldContainer);
926
0
}
927
928
void
929
nsWindow::ReparentNativeWidgetInternal(nsIWidget* aNewParent,
930
                                       GtkWidget* aNewContainer,
931
                                       GdkWindow* aNewParentWindow,
932
                                       GtkWidget* aOldContainer)
933
0
{
934
0
    if (!aNewContainer) {
935
0
        // The new parent GdkWindow has been destroyed.
936
0
        MOZ_ASSERT(!aNewParentWindow ||
937
0
                   gdk_window_is_destroyed(aNewParentWindow),
938
0
                   "live GdkWindow with no widget");
939
0
        Destroy();
940
0
    } else {
941
0
        if (aNewContainer != aOldContainer) {
942
0
            MOZ_ASSERT(!gdk_window_is_destroyed(aNewParentWindow),
943
0
                       "destroyed GdkWindow with widget");
944
0
            SetWidgetForHierarchy(mGdkWindow, aOldContainer, aNewContainer);
945
0
946
0
            if (aOldContainer == gInvisibleContainer) {
947
0
                CheckDestroyInvisibleContainer();
948
0
            }
949
0
        }
950
0
951
0
        if (!mIsTopLevel) {
952
0
            gdk_window_reparent(mGdkWindow, aNewParentWindow,
953
0
                                DevicePixelsToGdkCoordRoundDown(mBounds.x),
954
0
                                DevicePixelsToGdkCoordRoundDown(mBounds.y));
955
0
        }
956
0
    }
957
0
958
0
    auto* newParent = static_cast<nsWindow*>(aNewParent);
959
0
    bool parentHasMappedToplevel =
960
0
        newParent && newParent->mHasMappedToplevel;
961
0
    if (mHasMappedToplevel != parentHasMappedToplevel) {
962
0
        SetHasMappedToplevel(parentHasMappedToplevel);
963
0
    }
964
0
}
965
966
void
967
nsWindow::SetModal(bool aModal)
968
0
{
969
0
    LOG(("nsWindow::SetModal [%p] %d\n", (void *)this, aModal));
970
0
    if (mIsDestroyed)
971
0
        return;
972
0
    if (!mIsTopLevel || !mShell)
973
0
        return;
974
0
    gtk_window_set_modal(GTK_WINDOW(mShell), aModal ? TRUE : FALSE);
975
0
}
976
977
// nsIWidget method, which means IsShown.
978
bool
979
nsWindow::IsVisible() const
980
0
{
981
0
    return mIsShown;
982
0
}
983
984
void
985
nsWindow::RegisterTouchWindow()
986
0
{
987
0
#if GTK_CHECK_VERSION(3,4,0)
988
0
    mHandleTouchEvent = true;
989
0
    mTouches.Clear();
990
0
#endif
991
0
}
992
993
void
994
nsWindow::ConstrainPosition(bool aAllowSlop, int32_t *aX, int32_t *aY)
995
0
{
996
0
    if (!mIsTopLevel || !mShell)
997
0
      return;
998
0
999
0
    double dpiScale = GetDefaultScale().scale;
1000
0
1001
0
    // we need to use the window size in logical screen pixels
1002
0
    int32_t logWidth = std::max(NSToIntRound(mBounds.width / dpiScale), 1);
1003
0
    int32_t logHeight = std::max(NSToIntRound(mBounds.height / dpiScale), 1);
1004
0
1005
0
    /* get our playing field. use the current screen, or failing that
1006
0
      for any reason, use device caps for the default screen. */
1007
0
    nsCOMPtr<nsIScreen> screen;
1008
0
    nsCOMPtr<nsIScreenManager> screenmgr = do_GetService("@mozilla.org/gfx/screenmanager;1");
1009
0
    if (screenmgr) {
1010
0
      screenmgr->ScreenForRect(*aX, *aY, logWidth, logHeight,
1011
0
                               getter_AddRefs(screen));
1012
0
    }
1013
0
1014
0
    // We don't have any screen so leave the coordinates as is
1015
0
    if (!screen)
1016
0
      return;
1017
0
1018
0
    nsIntRect screenRect;
1019
0
    if (mSizeMode != nsSizeMode_Fullscreen) {
1020
0
      // For normalized windows, use the desktop work area.
1021
0
      screen->GetAvailRectDisplayPix(&screenRect.x, &screenRect.y,
1022
0
                                     &screenRect.width, &screenRect.height);
1023
0
    } else {
1024
0
      // For full screen windows, use the desktop.
1025
0
      screen->GetRectDisplayPix(&screenRect.x, &screenRect.y,
1026
0
                                &screenRect.width, &screenRect.height);
1027
0
    }
1028
0
1029
0
    if (aAllowSlop) {
1030
0
      if (*aX < screenRect.x - logWidth + kWindowPositionSlop)
1031
0
          *aX = screenRect.x - logWidth + kWindowPositionSlop;
1032
0
      else if (*aX >= screenRect.XMost() - kWindowPositionSlop)
1033
0
          *aX = screenRect.XMost() - kWindowPositionSlop;
1034
0
1035
0
      if (*aY < screenRect.y - logHeight + kWindowPositionSlop)
1036
0
          *aY = screenRect.y - logHeight + kWindowPositionSlop;
1037
0
      else if (*aY >= screenRect.YMost() - kWindowPositionSlop)
1038
0
          *aY = screenRect.YMost() - kWindowPositionSlop;
1039
0
    } else {
1040
0
      if (*aX < screenRect.x)
1041
0
          *aX = screenRect.x;
1042
0
      else if (*aX >= screenRect.XMost() - logWidth)
1043
0
          *aX = screenRect.XMost() - logWidth;
1044
0
1045
0
      if (*aY < screenRect.y)
1046
0
          *aY = screenRect.y;
1047
0
      else if (*aY >= screenRect.YMost() - logHeight)
1048
0
          *aY = screenRect.YMost() - logHeight;
1049
0
    }
1050
0
}
1051
1052
void nsWindow::SetSizeConstraints(const SizeConstraints& aConstraints)
1053
0
{
1054
0
    mSizeConstraints.mMinSize = GetSafeWindowSize(aConstraints.mMinSize);
1055
0
    mSizeConstraints.mMaxSize = GetSafeWindowSize(aConstraints.mMaxSize);
1056
0
1057
0
    if (mShell) {
1058
0
        GdkGeometry geometry;
1059
0
        geometry.min_width = DevicePixelsToGdkCoordRoundUp(
1060
0
                             mSizeConstraints.mMinSize.width);
1061
0
        geometry.min_height = DevicePixelsToGdkCoordRoundUp(
1062
0
                              mSizeConstraints.mMinSize.height);
1063
0
        geometry.max_width = DevicePixelsToGdkCoordRoundDown(
1064
0
                             mSizeConstraints.mMaxSize.width);
1065
0
        geometry.max_height = DevicePixelsToGdkCoordRoundDown(
1066
0
                              mSizeConstraints.mMaxSize.height);
1067
0
1068
0
        uint32_t hints = 0;
1069
0
        if (aConstraints.mMinSize != LayoutDeviceIntSize(0, 0)) {
1070
0
            hints |= GDK_HINT_MIN_SIZE;
1071
0
        }
1072
0
        if (aConstraints.mMaxSize !=
1073
0
            LayoutDeviceIntSize(NS_MAXSIZE, NS_MAXSIZE)) {
1074
0
            hints |= GDK_HINT_MAX_SIZE;
1075
0
        }
1076
0
        gtk_window_set_geometry_hints(GTK_WINDOW(mShell), nullptr,
1077
0
                                      &geometry, GdkWindowHints(hints));
1078
0
    }
1079
0
}
1080
1081
void
1082
nsWindow::Show(bool aState)
1083
0
{
1084
0
    if (aState == mIsShown)
1085
0
        return;
1086
0
1087
0
    // Clear our cached resources when the window is hidden.
1088
0
    if (mIsShown && !aState) {
1089
0
        ClearCachedResources();
1090
0
    }
1091
0
1092
0
    mIsShown = aState;
1093
0
1094
0
    LOG(("nsWindow::Show [%p] state %d\n", (void *)this, aState));
1095
0
1096
0
    if (aState) {
1097
0
        // Now that this window is shown, mHasMappedToplevel needs to be
1098
0
        // tracked on viewable descendants.
1099
0
        SetHasMappedToplevel(mHasMappedToplevel);
1100
0
    }
1101
0
1102
0
    // Ok, someone called show on a window that isn't sized to a sane
1103
0
    // value.  Mark this window as needing to have Show() called on it
1104
0
    // and return.
1105
0
    if ((aState && !AreBoundsSane()) || !mCreated) {
1106
0
        LOG(("\tbounds are insane or window hasn't been created yet\n"));
1107
0
        mNeedsShow = true;
1108
0
        return;
1109
0
    }
1110
0
1111
0
    // If someone is hiding this widget, clear any needing show flag.
1112
0
    if (!aState)
1113
0
        mNeedsShow = false;
1114
0
1115
0
#ifdef ACCESSIBILITY
1116
0
    if (aState && a11y::ShouldA11yBeEnabled())
1117
0
        CreateRootAccessible();
1118
0
#endif
1119
0
1120
0
    NativeShow(aState);
1121
0
}
1122
1123
void
1124
nsWindow::Resize(double aWidth, double aHeight, bool aRepaint)
1125
0
{
1126
0
    double scale = BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
1127
0
    int32_t width = NSToIntRound(scale * aWidth);
1128
0
    int32_t height = NSToIntRound(scale * aHeight);
1129
0
    ConstrainSize(&width, &height);
1130
0
1131
0
    // For top-level windows, aWidth and aHeight should possibly be
1132
0
    // interpreted as frame bounds, but NativeResize treats these as window
1133
0
    // bounds (Bug 581866).
1134
0
1135
0
    mBounds.SizeTo(width, height);
1136
0
1137
0
    if (!mCreated)
1138
0
        return;
1139
0
1140
0
    NativeResize();
1141
0
1142
0
    NotifyRollupGeometryChange();
1143
0
1144
0
    // send a resize notification if this is a toplevel
1145
0
    if (mIsTopLevel || mListenForResizes) {
1146
0
        DispatchResized();
1147
0
    }
1148
0
}
1149
1150
void
1151
nsWindow::Resize(double aX, double aY, double aWidth, double aHeight,
1152
                 bool aRepaint)
1153
0
{
1154
0
    double scale = BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
1155
0
    int32_t width = NSToIntRound(scale * aWidth);
1156
0
    int32_t height = NSToIntRound(scale * aHeight);
1157
0
    ConstrainSize(&width, &height);
1158
0
1159
0
    int32_t x = NSToIntRound(scale * aX);
1160
0
    int32_t y = NSToIntRound(scale * aY);
1161
0
    mBounds.x = x;
1162
0
    mBounds.y = y;
1163
0
    mBounds.SizeTo(width, height);
1164
0
1165
0
    if (!mCreated)
1166
0
        return;
1167
0
1168
0
    NativeMoveResize();
1169
0
1170
0
    NotifyRollupGeometryChange();
1171
0
1172
0
    if (mIsTopLevel || mListenForResizes) {
1173
0
        DispatchResized();
1174
0
    }
1175
0
}
1176
1177
void
1178
nsWindow::Enable(bool aState)
1179
0
{
1180
0
    mEnabled = aState;
1181
0
}
1182
1183
bool
1184
nsWindow::IsEnabled() const
1185
0
{
1186
0
    return mEnabled;
1187
0
}
1188
1189
void
1190
nsWindow::Move(double aX, double aY)
1191
0
{
1192
0
    LOG(("nsWindow::Move [%p] %f %f\n", (void *)this,
1193
0
         aX, aY));
1194
0
1195
0
    double scale = BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
1196
0
    int32_t x = NSToIntRound(aX * scale);
1197
0
    int32_t y = NSToIntRound(aY * scale);
1198
0
1199
0
    if (mWindowType == eWindowType_toplevel ||
1200
0
        mWindowType == eWindowType_dialog) {
1201
0
        SetSizeMode(nsSizeMode_Normal);
1202
0
    }
1203
0
1204
0
    // Since a popup window's x/y coordinates are in relation to to
1205
0
    // the parent, the parent might have moved so we always move a
1206
0
    // popup window.
1207
0
    if (x == mBounds.x && y == mBounds.y &&
1208
0
        mWindowType != eWindowType_popup)
1209
0
        return;
1210
0
1211
0
    // XXX Should we do some AreBoundsSane check here?
1212
0
1213
0
    mBounds.x = x;
1214
0
    mBounds.y = y;
1215
0
1216
0
    if (!mCreated)
1217
0
        return;
1218
0
1219
0
    NativeMove();
1220
0
1221
0
    NotifyRollupGeometryChange();
1222
0
}
1223
1224
1225
void
1226
nsWindow::NativeMove()
1227
0
{
1228
0
    GdkPoint point = DevicePixelsToGdkPointRoundDown(mBounds.TopLeft());
1229
0
1230
0
    if (mIsTopLevel) {
1231
0
        gtk_window_move(GTK_WINDOW(mShell), point.x, point.y);
1232
0
    }
1233
0
    else if (mGdkWindow) {
1234
0
        gdk_window_move(mGdkWindow, point.x, point.y);
1235
0
    }
1236
0
}
1237
1238
void
1239
nsWindow::SetZIndex(int32_t aZIndex)
1240
0
{
1241
0
    nsIWidget* oldPrev = GetPrevSibling();
1242
0
1243
0
    nsBaseWidget::SetZIndex(aZIndex);
1244
0
1245
0
    if (GetPrevSibling() == oldPrev) {
1246
0
        return;
1247
0
    }
1248
0
1249
0
    NS_ASSERTION(!mContainer, "Expected Mozilla child widget");
1250
0
1251
0
    // We skip the nsWindows that don't have mGdkWindows.
1252
0
    // These are probably in the process of being destroyed.
1253
0
1254
0
    if (!GetNextSibling()) {
1255
0
        // We're to be on top.
1256
0
        if (mGdkWindow)
1257
0
            gdk_window_raise(mGdkWindow);
1258
0
    } else {
1259
0
        // All the siblings before us need to be below our widget.
1260
0
        for (nsWindow* w = this; w;
1261
0
             w = static_cast<nsWindow*>(w->GetPrevSibling())) {
1262
0
            if (w->mGdkWindow)
1263
0
                gdk_window_lower(w->mGdkWindow);
1264
0
        }
1265
0
    }
1266
0
}
1267
1268
void
1269
nsWindow::SetSizeMode(nsSizeMode aMode)
1270
0
{
1271
0
    LOG(("nsWindow::SetSizeMode [%p] %d\n", (void *)this, aMode));
1272
0
1273
0
    // Save the requested state.
1274
0
    nsBaseWidget::SetSizeMode(aMode);
1275
0
1276
0
    // return if there's no shell or our current state is the same as
1277
0
    // the mode we were just set to.
1278
0
    if (!mShell || mSizeState == mSizeMode) {
1279
0
        return;
1280
0
    }
1281
0
1282
0
    switch (aMode) {
1283
0
    case nsSizeMode_Maximized:
1284
0
        gtk_window_maximize(GTK_WINDOW(mShell));
1285
0
        break;
1286
0
    case nsSizeMode_Minimized:
1287
0
        gtk_window_iconify(GTK_WINDOW(mShell));
1288
0
        break;
1289
0
    case nsSizeMode_Fullscreen:
1290
0
        MakeFullScreen(true);
1291
0
        break;
1292
0
1293
0
    default:
1294
0
        // nsSizeMode_Normal, really.
1295
0
        if (mSizeState == nsSizeMode_Minimized)
1296
0
            gtk_window_deiconify(GTK_WINDOW(mShell));
1297
0
        else if (mSizeState == nsSizeMode_Maximized)
1298
0
            gtk_window_unmaximize(GTK_WINDOW(mShell));
1299
0
        break;
1300
0
    }
1301
0
1302
0
    mSizeState = mSizeMode;
1303
0
}
1304
1305
typedef void (* SetUserTimeFunc)(GdkWindow* aWindow, guint32 aTimestamp);
1306
1307
// This will become obsolete when new GTK APIs are widely supported,
1308
// as described here: http://bugzilla.gnome.org/show_bug.cgi?id=347375
1309
static void
1310
SetUserTimeAndStartupIDForActivatedWindow(GtkWidget* aWindow)
1311
0
{
1312
0
    nsGTKToolkit* GTKToolkit = nsGTKToolkit::GetToolkit();
1313
0
    if (!GTKToolkit)
1314
0
        return;
1315
0
1316
0
    nsAutoCString desktopStartupID;
1317
0
    GTKToolkit->GetDesktopStartupID(&desktopStartupID);
1318
0
    if (desktopStartupID.IsEmpty()) {
1319
0
        // We don't have the data we need. Fall back to an
1320
0
        // approximation ... using the timestamp of the remote command
1321
0
        // being received as a guess for the timestamp of the user event
1322
0
        // that triggered it.
1323
0
        uint32_t timestamp = GTKToolkit->GetFocusTimestamp();
1324
0
        if (timestamp) {
1325
0
            gdk_window_focus(gtk_widget_get_window(aWindow), timestamp);
1326
0
            GTKToolkit->SetFocusTimestamp(0);
1327
0
        }
1328
0
        return;
1329
0
    }
1330
0
1331
#if defined(MOZ_ENABLE_STARTUP_NOTIFICATION)
1332
    // TODO - Implement for non-X11 Gtk backends (Bug 726479)
1333
    if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
1334
        GdkWindow* gdkWindow = gtk_widget_get_window(aWindow);
1335
1336
        GdkScreen* screen = gdk_window_get_screen(gdkWindow);
1337
        SnDisplay* snd =
1338
            sn_display_new(gdk_x11_display_get_xdisplay(gdk_window_get_display(gdkWindow)),
1339
                           nullptr, nullptr);
1340
        if (!snd)
1341
            return;
1342
        SnLauncheeContext* ctx =
1343
            sn_launchee_context_new(snd, gdk_screen_get_number(screen),
1344
                                    desktopStartupID.get());
1345
        if (!ctx) {
1346
            sn_display_unref(snd);
1347
            return;
1348
        }
1349
1350
        if (sn_launchee_context_get_id_has_timestamp(ctx)) {
1351
            gdk_x11_window_set_user_time(gdkWindow,
1352
                sn_launchee_context_get_timestamp(ctx));
1353
        }
1354
1355
        sn_launchee_context_setup_window(ctx, gdk_x11_window_get_xid(gdkWindow));
1356
        sn_launchee_context_complete(ctx);
1357
1358
        sn_launchee_context_unref(ctx);
1359
        sn_display_unref(snd);
1360
    }
1361
#endif
1362
1363
0
    // If we used the startup ID, that already contains the focus timestamp;
1364
0
    // we don't want to reuse the timestamp next time we raise the window
1365
0
    GTKToolkit->SetFocusTimestamp(0);
1366
0
    GTKToolkit->SetDesktopStartupID(EmptyCString());
1367
0
}
1368
1369
/* static */ guint32
1370
nsWindow::GetLastUserInputTime()
1371
0
{
1372
0
    // gdk_x11_display_get_user_time/gtk_get_current_event_time tracks
1373
0
    // button and key presses, DESKTOP_STARTUP_ID used to start the app,
1374
0
    // drop events from external drags,
1375
0
    // WM_DELETE_WINDOW delete events, but not usually mouse motion nor
1376
0
    // button and key releases.  Therefore use the most recent of
1377
0
    // gdk_x11_display_get_user_time and the last time that we have seen.
1378
0
    GdkDisplay* gdkDisplay = gdk_display_get_default();
1379
0
    guint32 timestamp = GDK_IS_X11_DISPLAY(gdkDisplay) ?
1380
0
            gdk_x11_display_get_user_time(gdkDisplay) :
1381
0
            gtk_get_current_event_time();
1382
0
1383
0
    if (sLastUserInputTime != GDK_CURRENT_TIME &&
1384
0
        TimestampIsNewerThan(sLastUserInputTime, timestamp)) {
1385
0
        return sLastUserInputTime;
1386
0
    }
1387
0
1388
0
    return timestamp;
1389
0
}
1390
1391
nsresult
1392
nsWindow::SetFocus(bool aRaise)
1393
0
{
1394
0
    // Make sure that our owning widget has focus.  If it doesn't try to
1395
0
    // grab it.  Note that we don't set our focus flag in this case.
1396
0
1397
0
    LOGFOCUS(("  SetFocus %d [%p]\n", aRaise, (void *)this));
1398
0
1399
0
    GtkWidget *owningWidget = GetMozContainerWidget();
1400
0
    if (!owningWidget)
1401
0
        return NS_ERROR_FAILURE;
1402
0
1403
0
    // Raise the window if someone passed in true and the prefs are
1404
0
    // set properly.
1405
0
    GtkWidget *toplevelWidget = gtk_widget_get_toplevel(owningWidget);
1406
0
1407
0
    if (gRaiseWindows && aRaise && toplevelWidget &&
1408
0
        !gtk_widget_has_focus(owningWidget) &&
1409
0
        !gtk_widget_has_focus(toplevelWidget)) {
1410
0
        GtkWidget* top_window = GetToplevelWidget();
1411
0
        if (top_window && (gtk_widget_get_visible(top_window)))
1412
0
        {
1413
0
            gdk_window_show_unraised(gtk_widget_get_window(top_window));
1414
0
            // Unset the urgency hint if possible.
1415
0
            SetUrgencyHint(top_window, false);
1416
0
        }
1417
0
    }
1418
0
1419
0
    RefPtr<nsWindow> owningWindow = get_window_for_gtk_widget(owningWidget);
1420
0
    if (!owningWindow)
1421
0
        return NS_ERROR_FAILURE;
1422
0
1423
0
    if (aRaise) {
1424
0
        // aRaise == true means request toplevel activation.
1425
0
1426
0
        // This is asynchronous.
1427
0
        // If and when the window manager accepts the request, then the focus
1428
0
        // widget will get a focus-in-event signal.
1429
0
        if (gRaiseWindows && owningWindow->mIsShown && owningWindow->mShell &&
1430
0
            !gtk_window_is_active(GTK_WINDOW(owningWindow->mShell))) {
1431
0
1432
0
            uint32_t timestamp = GDK_CURRENT_TIME;
1433
0
1434
0
            nsGTKToolkit* GTKToolkit = nsGTKToolkit::GetToolkit();
1435
0
            if (GTKToolkit)
1436
0
                timestamp = GTKToolkit->GetFocusTimestamp();
1437
0
1438
0
            LOGFOCUS(("  requesting toplevel activation [%p]\n", (void *)this));
1439
0
            NS_ASSERTION(owningWindow->mWindowType != eWindowType_popup
1440
0
                         || mParent,
1441
0
                         "Presenting an override-redirect window");
1442
0
            gtk_window_present_with_time(GTK_WINDOW(owningWindow->mShell), timestamp);
1443
0
1444
0
            if (GTKToolkit)
1445
0
                GTKToolkit->SetFocusTimestamp(0);
1446
0
        }
1447
0
1448
0
        return NS_OK;
1449
0
    }
1450
0
1451
0
    // aRaise == false means that keyboard events should be dispatched
1452
0
    // from this widget.
1453
0
1454
0
    // Ensure owningWidget is the focused GtkWidget within its toplevel window.
1455
0
    //
1456
0
    // For eWindowType_popup, this GtkWidget may not actually be the one that
1457
0
    // receives the key events as it may be the parent window that is active.
1458
0
    if (!gtk_widget_is_focus(owningWidget)) {
1459
0
        // This is synchronous.  It takes focus from a plugin or from a widget
1460
0
        // in an embedder.  The focus manager already knows that this window
1461
0
        // is active so gBlockActivateEvent avoids another (unnecessary)
1462
0
        // activate notification.
1463
0
        gBlockActivateEvent = true;
1464
0
        gtk_widget_grab_focus(owningWidget);
1465
0
        gBlockActivateEvent = false;
1466
0
    }
1467
0
1468
0
    // If this is the widget that already has focus, return.
1469
0
    if (gFocusWindow == this) {
1470
0
        LOGFOCUS(("  already have focus [%p]\n", (void *)this));
1471
0
        return NS_OK;
1472
0
    }
1473
0
1474
0
    // Set this window to be the focused child window
1475
0
    gFocusWindow = this;
1476
0
1477
0
    if (mIMContext) {
1478
0
        mIMContext->OnFocusWindow(this);
1479
0
    }
1480
0
1481
0
    LOGFOCUS(("  widget now has focus in SetFocus() [%p]\n",
1482
0
              (void *)this));
1483
0
1484
0
    return NS_OK;
1485
0
}
1486
1487
LayoutDeviceIntRect
1488
nsWindow::GetScreenBounds()
1489
0
{
1490
0
    LayoutDeviceIntRect rect;
1491
0
    if (mIsTopLevel && mContainer) {
1492
0
        // use the point including window decorations
1493
0
        gint x, y;
1494
0
        gdk_window_get_root_origin(gtk_widget_get_window(GTK_WIDGET(mContainer)), &x, &y);
1495
0
        rect.MoveTo(GdkPointToDevicePixels({ x, y }));
1496
0
    } else {
1497
0
        rect.MoveTo(WidgetToScreenOffset());
1498
0
    }
1499
0
    // mBounds.Size() is the window bounds, not the window-manager frame
1500
0
    // bounds (bug 581863).  gdk_window_get_frame_extents would give the
1501
0
    // frame bounds, but mBounds.Size() is returned here for consistency
1502
0
    // with Resize.
1503
0
    rect.SizeTo(mBounds.Size());
1504
0
    LOG(("GetScreenBounds %d,%d | %dx%d\n",
1505
0
         rect.x, rect.y, rect.width, rect.height));
1506
0
    return rect;
1507
0
}
1508
1509
LayoutDeviceIntSize
1510
nsWindow::GetClientSize()
1511
0
{
1512
0
  return LayoutDeviceIntSize(mBounds.width, mBounds.height);
1513
0
}
1514
1515
LayoutDeviceIntRect
1516
nsWindow::GetClientBounds()
1517
0
{
1518
0
    // GetBounds returns a rect whose top left represents the top left of the
1519
0
    // outer bounds, but whose width/height represent the size of the inner
1520
0
    // bounds (which is messed up).
1521
0
    LayoutDeviceIntRect rect = GetBounds();
1522
0
    rect.MoveBy(GetClientOffset());
1523
0
    return rect;
1524
0
}
1525
1526
void
1527
nsWindow::UpdateClientOffset()
1528
0
{
1529
0
    AUTO_PROFILER_LABEL("nsWindow::UpdateClientOffset", OTHER);
1530
0
1531
0
    if (!mIsTopLevel || !mShell || !mIsX11Display ||
1532
0
        gtk_window_get_window_type(GTK_WINDOW(mShell)) == GTK_WINDOW_POPUP) {
1533
0
        mClientOffset = nsIntPoint(0, 0);
1534
0
        return;
1535
0
    }
1536
0
1537
0
    GdkAtom cardinal_atom = gdk_x11_xatom_to_atom(XA_CARDINAL);
1538
0
1539
0
    GdkAtom type_returned;
1540
0
    int format_returned;
1541
0
    int length_returned;
1542
0
    long *frame_extents;
1543
0
1544
0
    if (!gdk_property_get(gtk_widget_get_window(mShell),
1545
0
                          gdk_atom_intern ("_NET_FRAME_EXTENTS", FALSE),
1546
0
                          cardinal_atom,
1547
0
                          0, // offset
1548
0
                          4*4, // length
1549
0
                          FALSE, // delete
1550
0
                          &type_returned,
1551
0
                          &format_returned,
1552
0
                          &length_returned,
1553
0
                          (guchar **) &frame_extents) ||
1554
0
        length_returned/sizeof(glong) != 4) {
1555
0
        mClientOffset = nsIntPoint(0, 0);
1556
0
        return;
1557
0
    }
1558
0
1559
0
    // data returned is in the order left, right, top, bottom
1560
0
    auto left = int32_t(frame_extents[0]);
1561
0
    auto top = int32_t(frame_extents[2]);
1562
0
1563
0
    g_free(frame_extents);
1564
0
1565
0
    mClientOffset = nsIntPoint(left, top);
1566
0
}
1567
1568
LayoutDeviceIntPoint
1569
nsWindow::GetClientOffset()
1570
0
{
1571
0
    return LayoutDeviceIntPoint::FromUnknownPoint(mClientOffset);
1572
0
}
1573
1574
gboolean
1575
nsWindow::OnPropertyNotifyEvent(GtkWidget* aWidget, GdkEventProperty* aEvent)
1576
1577
0
{
1578
0
  if (aEvent->atom == gdk_atom_intern("_NET_FRAME_EXTENTS", FALSE)) {
1579
0
    UpdateClientOffset();
1580
0
1581
0
    // Send a WindowMoved notification. This ensures that TabParent
1582
0
    // picks up the new client offset and sends it to the child process
1583
0
    // if appropriate.
1584
0
    NotifyWindowMoved(mBounds.x, mBounds.y);
1585
0
    return FALSE;
1586
0
  }
1587
0
1588
0
  if (GetCurrentTimeGetter()->PropertyNotifyHandler(aWidget, aEvent)) {
1589
0
    return TRUE;
1590
0
  }
1591
0
1592
0
  return FALSE;
1593
0
}
1594
1595
void
1596
nsWindow::SetCursor(nsCursor aCursor)
1597
0
{
1598
0
    // if we're not the toplevel window pass up the cursor request to
1599
0
    // the toplevel window to handle it.
1600
0
    if (!mContainer && mGdkWindow) {
1601
0
        nsWindow *window = GetContainerWindow();
1602
0
        if (!window)
1603
0
            return;
1604
0
1605
0
        window->SetCursor(aCursor);
1606
0
        return;
1607
0
    }
1608
0
1609
0
    // Only change cursor if it's actually been changed
1610
0
    if (aCursor != mCursor || mUpdateCursor) {
1611
0
        GdkCursor *newCursor = nullptr;
1612
0
        mUpdateCursor = false;
1613
0
1614
0
        newCursor = get_gtk_cursor(aCursor);
1615
0
1616
0
        if (nullptr != newCursor) {
1617
0
            mCursor = aCursor;
1618
0
1619
0
            if (!mContainer)
1620
0
                return;
1621
0
1622
0
            gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)), newCursor);
1623
0
        }
1624
0
    }
1625
0
}
1626
1627
nsresult
1628
nsWindow::SetCursor(imgIContainer* aCursor,
1629
                    uint32_t aHotspotX, uint32_t aHotspotY)
1630
0
{
1631
0
    // if we're not the toplevel window pass up the cursor request to
1632
0
    // the toplevel window to handle it.
1633
0
    if (!mContainer && mGdkWindow) {
1634
0
        nsWindow *window = GetContainerWindow();
1635
0
        if (!window)
1636
0
            return NS_ERROR_FAILURE;
1637
0
1638
0
        return window->SetCursor(aCursor, aHotspotX, aHotspotY);
1639
0
    }
1640
0
1641
0
    mCursor = eCursorInvalid;
1642
0
1643
0
    // Get the image's current frame
1644
0
    GdkPixbuf* pixbuf = nsImageToPixbuf::ImageToPixbuf(aCursor);
1645
0
    if (!pixbuf)
1646
0
        return NS_ERROR_NOT_AVAILABLE;
1647
0
1648
0
    int width = gdk_pixbuf_get_width(pixbuf);
1649
0
    int height = gdk_pixbuf_get_height(pixbuf);
1650
0
    // Reject cursors greater than 128 pixels in some direction, to prevent
1651
0
    // spoofing.
1652
0
    // XXX ideally we should rescale. Also, we could modify the API to
1653
0
    // allow trusted content to set larger cursors.
1654
0
    if (width > 128 || height > 128) {
1655
0
        g_object_unref(pixbuf);
1656
0
        return NS_ERROR_NOT_AVAILABLE;
1657
0
    }
1658
0
1659
0
    // Looks like all cursors need an alpha channel (tested on Gtk 2.4.4). This
1660
0
    // is of course not documented anywhere...
1661
0
    // So add one if there isn't one yet
1662
0
    if (!gdk_pixbuf_get_has_alpha(pixbuf)) {
1663
0
        GdkPixbuf* alphaBuf = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0);
1664
0
        g_object_unref(pixbuf);
1665
0
        if (!alphaBuf) {
1666
0
            return NS_ERROR_OUT_OF_MEMORY;
1667
0
        }
1668
0
        pixbuf = alphaBuf;
1669
0
    }
1670
0
1671
0
    GdkCursor* cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(),
1672
0
                                                   pixbuf,
1673
0
                                                   aHotspotX, aHotspotY);
1674
0
    g_object_unref(pixbuf);
1675
0
    nsresult rv = NS_ERROR_OUT_OF_MEMORY;
1676
0
    if (cursor) {
1677
0
        if (mContainer) {
1678
0
            gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)), cursor);
1679
0
            rv = NS_OK;
1680
0
        }
1681
0
        g_object_unref(cursor);
1682
0
    }
1683
0
1684
0
    return rv;
1685
0
}
1686
1687
void
1688
nsWindow::Invalidate(const LayoutDeviceIntRect& aRect)
1689
0
{
1690
0
    if (!mGdkWindow)
1691
0
        return;
1692
0
1693
0
    GdkRectangle rect = DevicePixelsToGdkRectRoundOut(aRect);
1694
0
    gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
1695
0
1696
0
    LOGDRAW(("Invalidate (rect) [%p]: %d %d %d %d\n", (void *)this,
1697
0
             rect.x, rect.y, rect.width, rect.height));
1698
0
}
1699
1700
void*
1701
nsWindow::GetNativeData(uint32_t aDataType)
1702
0
{
1703
0
    switch (aDataType) {
1704
0
    case NS_NATIVE_WINDOW:
1705
0
    case NS_NATIVE_WIDGET: {
1706
0
        if (!mGdkWindow)
1707
0
            return nullptr;
1708
0
1709
0
        return mGdkWindow;
1710
0
    }
1711
0
1712
0
    case NS_NATIVE_DISPLAY: {
1713
0
#ifdef MOZ_X11
1714
0
        GdkDisplay* gdkDisplay = gdk_display_get_default();
1715
0
        if (GDK_IS_X11_DISPLAY(gdkDisplay)) {
1716
0
            return GDK_DISPLAY_XDISPLAY(gdkDisplay);
1717
0
        }
1718
0
#endif /* MOZ_X11 */
1719
0
        // Don't bother to return native display on Wayland as it's for
1720
0
        // X11 only NPAPI plugins.
1721
0
        return nullptr;
1722
0
    }
1723
0
    case NS_NATIVE_SHELLWIDGET:
1724
0
        return GetToplevelWidget();
1725
0
1726
0
    case NS_NATIVE_SHAREABLE_WINDOW:
1727
0
        if (mIsX11Display) {
1728
0
            return (void *) GDK_WINDOW_XID(gdk_window_get_toplevel(mGdkWindow));
1729
0
        }
1730
0
        NS_WARNING("nsWindow::GetNativeData(): NS_NATIVE_SHAREABLE_WINDOW is not handled on Wayland!");
1731
0
        return nullptr;
1732
0
    case NS_RAW_NATIVE_IME_CONTEXT: {
1733
0
        void* pseudoIMEContext = GetPseudoIMEContext();
1734
0
        if (pseudoIMEContext) {
1735
0
            return pseudoIMEContext;
1736
0
        }
1737
0
        // If IME context isn't available on this widget, we should set |this|
1738
0
        // instead of nullptr.
1739
0
        if (!mIMContext) {
1740
0
            return this;
1741
0
        }
1742
0
        return mIMContext.get();
1743
0
    }
1744
0
    case NS_NATIVE_OPENGL_CONTEXT:
1745
0
      return nullptr;
1746
0
#ifdef MOZ_X11
1747
0
    case NS_NATIVE_COMPOSITOR_DISPLAY:
1748
0
        return gfxPlatformGtk::GetPlatform()->GetCompositorDisplay();
1749
0
#endif // MOZ_X11
1750
0
    case NS_NATIVE_EGL_WINDOW: {
1751
0
        if (mIsX11Display)
1752
0
            return mGdkWindow ? (void*)GDK_WINDOW_XID(mGdkWindow) : nullptr;
1753
#ifdef MOZ_WAYLAND
1754
        if (mContainer)
1755
            return moz_container_get_wl_egl_window(mContainer);
1756
#endif
1757
0
        return nullptr;
1758
0
    }
1759
0
    default:
1760
0
        NS_WARNING("nsWindow::GetNativeData called with bad value");
1761
0
        return nullptr;
1762
0
    }
1763
0
}
1764
1765
nsresult
1766
nsWindow::SetTitle(const nsAString& aTitle)
1767
0
{
1768
0
    if (!mShell)
1769
0
        return NS_OK;
1770
0
1771
0
    // convert the string into utf8 and set the title.
1772
0
#define UTF8_FOLLOWBYTE(ch) (((ch) & 0xC0) == 0x80)
1773
0
    NS_ConvertUTF16toUTF8 titleUTF8(aTitle);
1774
0
    if (titleUTF8.Length() > NS_WINDOW_TITLE_MAX_LENGTH) {
1775
0
        // Truncate overlong titles (bug 167315). Make sure we chop after a
1776
0
        // complete sequence by making sure the next char isn't a follow-byte.
1777
0
        uint32_t len = NS_WINDOW_TITLE_MAX_LENGTH;
1778
0
        while(UTF8_FOLLOWBYTE(titleUTF8[len]))
1779
0
            --len;
1780
0
        titleUTF8.Truncate(len);
1781
0
    }
1782
0
    gtk_window_set_title(GTK_WINDOW(mShell), (const char *)titleUTF8.get());
1783
0
1784
0
    return NS_OK;
1785
0
}
1786
1787
void
1788
nsWindow::SetIcon(const nsAString& aIconSpec)
1789
0
{
1790
0
    if (!mShell)
1791
0
        return;
1792
0
1793
0
    nsAutoCString iconName;
1794
0
1795
0
    if (aIconSpec.EqualsLiteral("default")) {
1796
0
        nsAutoString brandName;
1797
0
        WidgetUtils::GetBrandShortName(brandName);
1798
0
        if (brandName.IsEmpty()) {
1799
0
            brandName.AssignLiteral(u"Mozilla");
1800
0
        }
1801
0
        AppendUTF16toUTF8(brandName, iconName);
1802
0
        ToLowerCase(iconName);
1803
0
    } else {
1804
0
        AppendUTF16toUTF8(aIconSpec, iconName);
1805
0
    }
1806
0
1807
0
    nsCOMPtr<nsIFile> iconFile;
1808
0
    nsAutoCString path;
1809
0
1810
0
    gint *iconSizes =
1811
0
        gtk_icon_theme_get_icon_sizes(gtk_icon_theme_get_default(),
1812
0
                                      iconName.get());
1813
0
    bool foundIcon = (iconSizes[0] != 0);
1814
0
    g_free(iconSizes);
1815
0
1816
0
    if (!foundIcon) {
1817
0
        // Look for icons with the following suffixes appended to the base name
1818
0
        // The last two entries (for the old XPM format) will be ignored unless
1819
0
        // no icons are found using other suffixes. XPM icons are deprecated.
1820
0
1821
0
        const char16_t extensions[9][8] = { u".png", u"16.png", u"32.png",
1822
0
                                            u"48.png", u"64.png", u"128.png",
1823
0
                                            u"256.png",
1824
0
                                            u".xpm", u"16.xpm" };
1825
0
1826
0
        for (uint32_t i = 0; i < ArrayLength(extensions); i++) {
1827
0
            // Don't bother looking for XPM versions if we found a PNG.
1828
0
            if (i == ArrayLength(extensions) - 2 && foundIcon)
1829
0
                break;
1830
0
1831
0
            ResolveIconName(aIconSpec, nsDependentString(extensions[i]),
1832
0
                            getter_AddRefs(iconFile));
1833
0
            if (iconFile) {
1834
0
                iconFile->GetNativePath(path);
1835
0
                GdkPixbuf *icon = gdk_pixbuf_new_from_file(path.get(), nullptr);
1836
0
                if (icon) {
1837
0
                    gtk_icon_theme_add_builtin_icon(iconName.get(),
1838
0
                                                    gdk_pixbuf_get_height(icon),
1839
0
                                                    icon);
1840
0
                    g_object_unref(icon);
1841
0
                    foundIcon = true;
1842
0
                }
1843
0
            }
1844
0
        }
1845
0
    }
1846
0
1847
0
    // leave the default icon intact if no matching icons were found
1848
0
    if (foundIcon) {
1849
0
        gtk_window_set_icon_name(GTK_WINDOW(mShell), iconName.get());
1850
0
    }
1851
0
}
1852
1853
1854
LayoutDeviceIntPoint
1855
nsWindow::WidgetToScreenOffset()
1856
0
{
1857
0
    gint x = 0, y = 0;
1858
0
1859
0
    if (mGdkWindow) {
1860
0
        gdk_window_get_origin(mGdkWindow, &x, &y);
1861
0
    }
1862
0
1863
0
    return GdkPointToDevicePixels({ x, y });
1864
0
}
1865
1866
void
1867
nsWindow::CaptureMouse(bool aCapture)
1868
0
{
1869
0
    LOG(("CaptureMouse %p\n", (void *)this));
1870
0
1871
0
    if (!mGdkWindow)
1872
0
        return;
1873
0
1874
0
    if (!mContainer)
1875
0
        return;
1876
0
1877
0
    if (aCapture) {
1878
0
        gtk_grab_add(GTK_WIDGET(mContainer));
1879
0
        GrabPointer(GetLastUserInputTime());
1880
0
    }
1881
0
    else {
1882
0
        ReleaseGrabs();
1883
0
        gtk_grab_remove(GTK_WIDGET(mContainer));
1884
0
    }
1885
0
}
1886
1887
void
1888
nsWindow::CaptureRollupEvents(nsIRollupListener *aListener,
1889
                              bool               aDoCapture)
1890
0
{
1891
0
    if (!mGdkWindow)
1892
0
        return;
1893
0
1894
0
    if (!mContainer)
1895
0
        return;
1896
0
1897
0
    LOG(("CaptureRollupEvents %p %i\n", this, int(aDoCapture)));
1898
0
1899
0
    if (aDoCapture) {
1900
0
        gRollupListener = aListener;
1901
0
        // Don't add a grab if a drag is in progress, or if the widget is a drag
1902
0
        // feedback popup. (panels with type="drag").
1903
0
        if (!mIsDragPopup && !nsWindow::DragInProgress()) {
1904
0
            gtk_grab_add(GTK_WIDGET(mContainer));
1905
0
            GrabPointer(GetLastUserInputTime());
1906
0
        }
1907
0
    }
1908
0
    else {
1909
0
        if (!nsWindow::DragInProgress()) {
1910
0
            ReleaseGrabs();
1911
0
        }
1912
0
        // There may not have been a drag in process when aDoCapture was set,
1913
0
        // so make sure to remove any added grab.  This is a no-op if the grab
1914
0
        // was not added to this widget.
1915
0
        gtk_grab_remove(GTK_WIDGET(mContainer));
1916
0
        gRollupListener = nullptr;
1917
0
    }
1918
0
}
1919
1920
nsresult
1921
nsWindow::GetAttention(int32_t aCycleCount)
1922
0
{
1923
0
    LOG(("nsWindow::GetAttention [%p]\n", (void *)this));
1924
0
1925
0
    GtkWidget* top_window = GetToplevelWidget();
1926
0
    GtkWidget* top_focused_window =
1927
0
        gFocusWindow ? gFocusWindow->GetToplevelWidget() : nullptr;
1928
0
1929
0
    // Don't get attention if the window is focused anyway.
1930
0
    if (top_window && (gtk_widget_get_visible(top_window)) &&
1931
0
        top_window != top_focused_window) {
1932
0
        SetUrgencyHint(top_window, true);
1933
0
    }
1934
0
1935
0
    return NS_OK;
1936
0
}
1937
1938
bool
1939
nsWindow::HasPendingInputEvent()
1940
0
{
1941
0
    // This sucks, but gtk/gdk has no way to answer the question we want while
1942
0
    // excluding paint events, and there's no X API that will let us peek
1943
0
    // without blocking or removing.  To prevent event reordering, peek
1944
0
    // anything except expose events.  Reordering expose and others should be
1945
0
    // ok, hopefully.
1946
0
    bool haveEvent = false;
1947
0
#ifdef MOZ_X11
1948
0
    XEvent ev;
1949
0
    if (mIsX11Display) {
1950
0
        Display *display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
1951
0
        haveEvent =
1952
0
            XCheckMaskEvent(display,
1953
0
                            KeyPressMask | KeyReleaseMask | ButtonPressMask |
1954
0
                            ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
1955
0
                            PointerMotionMask | PointerMotionHintMask |
1956
0
                            Button1MotionMask | Button2MotionMask |
1957
0
                            Button3MotionMask | Button4MotionMask |
1958
0
                            Button5MotionMask | ButtonMotionMask | KeymapStateMask |
1959
0
                            VisibilityChangeMask | StructureNotifyMask |
1960
0
                            ResizeRedirectMask | SubstructureNotifyMask |
1961
0
                            SubstructureRedirectMask | FocusChangeMask |
1962
0
                            PropertyChangeMask | ColormapChangeMask |
1963
0
                            OwnerGrabButtonMask, &ev);
1964
0
        if (haveEvent) {
1965
0
            XPutBackEvent(display, &ev);
1966
0
        }
1967
0
    }
1968
0
#endif
1969
0
    return haveEvent;
1970
0
}
1971
1972
#if 0
1973
#ifdef DEBUG
1974
// Paint flashing code (disabled for cairo - see below)
1975
1976
#define CAPS_LOCK_IS_ON \
1977
(KeymapWrapper::AreModifiersCurrentlyActive(KeymapWrapper::CAPS_LOCK))
1978
1979
#define WANT_PAINT_FLASHING \
1980
(debug_WantPaintFlashing() && CAPS_LOCK_IS_ON)
1981
1982
#ifdef MOZ_X11
1983
static void
1984
gdk_window_flash(GdkWindow *    aGdkWindow,
1985
                 unsigned int   aTimes,
1986
                 unsigned int   aInterval,  // Milliseconds
1987
                 GdkRegion *    aRegion)
1988
{
1989
  gint         x;
1990
  gint         y;
1991
  gint         width;
1992
  gint         height;
1993
  guint        i;
1994
  GdkGC *      gc = 0;
1995
  GdkColor     white;
1996
1997
  gdk_window_get_geometry(aGdkWindow,nullptr,nullptr,&width,&height);
1998
1999
  gdk_window_get_origin (aGdkWindow,
2000
                         &x,
2001
                         &y);
2002
2003
  gc = gdk_gc_new(gdk_get_default_root_window());
2004
2005
  white.pixel = WhitePixel(gdk_display,DefaultScreen(gdk_display));
2006
2007
  gdk_gc_set_foreground(gc,&white);
2008
  gdk_gc_set_function(gc,GDK_XOR);
2009
  gdk_gc_set_subwindow(gc,GDK_INCLUDE_INFERIORS);
2010
2011
  gdk_region_offset(aRegion, x, y);
2012
  gdk_gc_set_clip_region(gc, aRegion);
2013
2014
  /*
2015
   * Need to do this twice so that the XOR effect can replace
2016
   * the original window contents.
2017
   */
2018
  for (i = 0; i < aTimes * 2; i++)
2019
  {
2020
    gdk_draw_rectangle(gdk_get_default_root_window(),
2021
                       gc,
2022
                       TRUE,
2023
                       x,
2024
                       y,
2025
                       width,
2026
                       height);
2027
2028
    gdk_flush();
2029
2030
    PR_Sleep(PR_MillisecondsToInterval(aInterval));
2031
  }
2032
2033
  gdk_gc_destroy(gc);
2034
2035
  gdk_region_offset(aRegion, -x, -y);
2036
}
2037
#endif /* MOZ_X11 */
2038
#endif // DEBUG
2039
#endif
2040
2041
# ifdef cairo_copy_clip_rectangle_list
2042
#  error "Looks like we're including Mozilla's cairo instead of system cairo"
2043
# endif
2044
static bool
2045
ExtractExposeRegion(LayoutDeviceIntRegion& aRegion, cairo_t* cr)
2046
0
{
2047
0
  cairo_rectangle_list_t* rects = cairo_copy_clip_rectangle_list(cr);
2048
0
  if (rects->status != CAIRO_STATUS_SUCCESS) {
2049
0
      NS_WARNING("Failed to obtain cairo rectangle list.");
2050
0
      return false;
2051
0
  }
2052
0
2053
0
  for (int i = 0; i < rects->num_rectangles; i++)  {
2054
0
      const cairo_rectangle_t& r = rects->rectangles[i];
2055
0
      aRegion.Or(aRegion, LayoutDeviceIntRect::Truncate(r.x, r.y, r.width, r.height));
2056
0
      LOGDRAW(("\t%f %f %f %f\n", r.x, r.y, r.width, r.height));
2057
0
  }
2058
0
2059
0
  cairo_rectangle_list_destroy(rects);
2060
0
  return true;
2061
0
}
2062
2063
gboolean
2064
nsWindow::OnExposeEvent(cairo_t *cr)
2065
0
{
2066
0
    // Send any pending resize events so that layout can update.
2067
0
    // May run event loop.
2068
0
    MaybeDispatchResized();
2069
0
2070
0
    if (mIsDestroyed) {
2071
0
        return FALSE;
2072
0
    }
2073
0
2074
0
    // Windows that are not visible will be painted after they become visible.
2075
0
    if (!mGdkWindow || mIsFullyObscured || !mHasMappedToplevel)
2076
0
        return FALSE;
2077
0
2078
#ifdef MOZ_WAYLAND
2079
    // Window does not have visible wl_surface yet.
2080
    if (!mIsX11Display && !GetWaylandSurface())
2081
        return FALSE;
2082
#endif
2083
2084
0
    nsIWidgetListener *listener = GetListener();
2085
0
    if (!listener)
2086
0
        return FALSE;
2087
0
2088
0
    LayoutDeviceIntRegion exposeRegion;
2089
0
    if (!ExtractExposeRegion(exposeRegion, cr)) {
2090
0
        return FALSE;
2091
0
    }
2092
0
2093
0
    gint scale = GdkScaleFactor();
2094
0
    LayoutDeviceIntRegion region = exposeRegion;
2095
0
    region.ScaleRoundOut(scale, scale);
2096
0
2097
0
    if (GetLayerManager()->AsKnowsCompositor() && mCompositorSession) {
2098
0
        // We need to paint to the screen even if nothing changed, since if we
2099
0
        // don't have a compositing window manager, our pixels could be stale.
2100
0
        GetLayerManager()->SetNeedsComposite(true);
2101
0
        GetLayerManager()->SendInvalidRegion(region.ToUnknownRegion());
2102
0
    }
2103
0
2104
0
    RefPtr<nsWindow> strongThis(this);
2105
0
2106
0
    // Dispatch WillPaintWindow notification to allow scripts etc. to run
2107
0
    // before we paint
2108
0
    {
2109
0
        listener->WillPaintWindow(this);
2110
0
2111
0
        // If the window has been destroyed during the will paint notification,
2112
0
        // there is nothing left to do.
2113
0
        if (!mGdkWindow)
2114
0
            return TRUE;
2115
0
2116
0
        // Re-get the listener since the will paint notification might have
2117
0
        // killed it.
2118
0
        listener = GetListener();
2119
0
        if (!listener)
2120
0
            return FALSE;
2121
0
    }
2122
0
2123
0
    if (GetLayerManager()->AsKnowsCompositor() && GetLayerManager()->NeedsComposite()) {
2124
0
      GetLayerManager()->ScheduleComposite();
2125
0
      GetLayerManager()->SetNeedsComposite(false);
2126
0
    }
2127
0
2128
0
    LOGDRAW(("sending expose event [%p] %p 0x%lx (rects follow):\n",
2129
0
             (void *)this, (void *)mGdkWindow,
2130
0
             mIsX11Display ? gdk_x11_window_get_xid(mGdkWindow) : 0));
2131
0
2132
0
    // Our bounds may have changed after calling WillPaintWindow.  Clip
2133
0
    // to the new bounds here.  The region is relative to this
2134
0
    // window.
2135
0
    region.And(region, LayoutDeviceIntRect(0, 0, mBounds.width, mBounds.height));
2136
0
2137
0
    bool shaped = false;
2138
0
    if (eTransparencyTransparent == GetTransparencyMode()) {
2139
0
        if (mHasAlphaVisual) {
2140
0
            // Remove possible shape mask from when window manger was not
2141
0
            // previously compositing.
2142
0
            static_cast<nsWindow*>(GetTopLevelWidget())->
2143
0
                ClearTransparencyBitmap();
2144
0
        } else {
2145
0
            shaped = true;
2146
0
        }
2147
0
    }
2148
0
2149
0
    if (!shaped) {
2150
0
        GList *children =
2151
0
            gdk_window_peek_children(mGdkWindow);
2152
0
        while (children) {
2153
0
            GdkWindow *gdkWin = GDK_WINDOW(children->data);
2154
0
            nsWindow *kid = get_window_for_gdk_window(gdkWin);
2155
0
            if (kid && gdk_window_is_visible(gdkWin)) {
2156
0
                AutoTArray<LayoutDeviceIntRect,1> clipRects;
2157
0
                kid->GetWindowClipRegion(&clipRects);
2158
0
                LayoutDeviceIntRect bounds = kid->GetBounds();
2159
0
                for (uint32_t i = 0; i < clipRects.Length(); ++i) {
2160
0
                    LayoutDeviceIntRect r = clipRects[i] + bounds.TopLeft();
2161
0
                    region.Sub(region, r);
2162
0
                }
2163
0
            }
2164
0
            children = children->next;
2165
0
        }
2166
0
    }
2167
0
2168
0
    if (region.IsEmpty()) {
2169
0
        return TRUE;
2170
0
    }
2171
0
2172
0
    // If this widget uses OMTC...
2173
0
    if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT ||
2174
0
        GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_WR) {
2175
0
        listener->PaintWindow(this, region);
2176
0
2177
0
        // Re-get the listener since the will paint notification might have
2178
0
        // killed it.
2179
0
        listener = GetListener();
2180
0
        if (!listener)
2181
0
            return TRUE;
2182
0
2183
0
        listener->DidPaintWindow();
2184
0
        return TRUE;
2185
0
    }
2186
0
2187
0
    BufferMode layerBuffering = BufferMode::BUFFERED;
2188
0
    RefPtr<DrawTarget> dt = StartRemoteDrawingInRegion(region, &layerBuffering);
2189
0
    if (!dt || !dt->IsValid()) {
2190
0
        return FALSE;
2191
0
    }
2192
0
    RefPtr<gfxContext> ctx;
2193
0
    IntRect boundsRect = region.GetBounds().ToUnknownRect();
2194
0
    IntPoint offset(0, 0);
2195
0
    if (dt->GetSize() == boundsRect.Size()) {
2196
0
      offset = boundsRect.TopLeft();
2197
0
      dt->SetTransform(Matrix::Translation(-offset));
2198
0
    }
2199
0
2200
0
#ifdef MOZ_X11
2201
0
    if (shaped) {
2202
0
        // Collapse update area to the bounding box. This is so we only have to
2203
0
        // call UpdateTranslucentWindowAlpha once. After we have dropped
2204
0
        // support for non-Thebes graphics, UpdateTranslucentWindowAlpha will be
2205
0
        // our private interface so we can rework things to avoid this.
2206
0
        dt->PushClipRect(Rect(boundsRect));
2207
0
2208
0
        // The double buffering is done here to extract the shape mask.
2209
0
        // (The shape mask won't be necessary when a visual with an alpha
2210
0
        // channel is used on compositing window managers.)
2211
0
        layerBuffering = BufferMode::BUFFER_NONE;
2212
0
        RefPtr<DrawTarget> destDT = dt->CreateSimilarDrawTarget(boundsRect.Size(), SurfaceFormat::B8G8R8A8);
2213
0
        if (!destDT || !destDT->IsValid()) {
2214
0
            return FALSE;
2215
0
        }
2216
0
        destDT->SetTransform(Matrix::Translation(-boundsRect.TopLeft()));
2217
0
        ctx = gfxContext::CreatePreservingTransformOrNull(destDT);
2218
0
    } else {
2219
0
        gfxUtils::ClipToRegion(dt, region.ToUnknownRegion());
2220
0
        ctx = gfxContext::CreatePreservingTransformOrNull(dt);
2221
0
    }
2222
0
    MOZ_ASSERT(ctx); // checked both dt and destDT valid draw target above
2223
0
2224
#if 0
2225
    // NOTE: Paint flashing region would be wrong for cairo, since
2226
    // cairo inflates the update region, etc.  So don't paint flash
2227
    // for cairo.
2228
#ifdef DEBUG
2229
    // XXX aEvent->region may refer to a newly-invalid area.  FIXME
2230
    if (0 && WANT_PAINT_FLASHING && gtk_widget_get_window(aEvent))
2231
        gdk_window_flash(mGdkWindow, 1, 100, aEvent->region);
2232
#endif
2233
#endif
2234
2235
0
#endif // MOZ_X11
2236
0
2237
0
    bool painted = false;
2238
0
    {
2239
0
      if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC) {
2240
0
        if (GetTransparencyMode() == eTransparencyTransparent &&
2241
0
            layerBuffering == BufferMode::BUFFER_NONE &&
2242
0
            mHasAlphaVisual) {
2243
0
          // If our draw target is unbuffered and we use an alpha channel,
2244
0
          // clear the image beforehand to ensure we don't get artifacts from a
2245
0
          // reused SHM image. See bug 1258086.
2246
0
          dt->ClearRect(Rect(boundsRect));
2247
0
        }
2248
0
        AutoLayerManagerSetup setupLayerManager(this, ctx, layerBuffering);
2249
0
        painted = listener->PaintWindow(this, region);
2250
0
2251
0
        // Re-get the listener since the will paint notification might have
2252
0
        // killed it.
2253
0
        listener = GetListener();
2254
0
        if (!listener)
2255
0
            return TRUE;
2256
0
2257
0
      }
2258
0
    }
2259
0
2260
0
#ifdef MOZ_X11
2261
0
    // PaintWindow can Destroy us (bug 378273), avoid doing any paint
2262
0
    // operations below if that happened - it will lead to XError and exit().
2263
0
    if (shaped) {
2264
0
        if (MOZ_LIKELY(!mIsDestroyed)) {
2265
0
            if (painted) {
2266
0
                RefPtr<SourceSurface> surf = ctx->GetDrawTarget()->Snapshot();
2267
0
2268
0
                UpdateAlpha(surf, boundsRect);
2269
0
2270
0
                dt->DrawSurface(surf, Rect(boundsRect), Rect(0, 0, boundsRect.width, boundsRect.height),
2271
0
                                DrawSurfaceOptions(SamplingFilter::POINT),
2272
0
                                DrawOptions(1.0f, CompositionOp::OP_SOURCE));
2273
0
            }
2274
0
        }
2275
0
    }
2276
0
2277
0
    ctx = nullptr;
2278
0
    dt->PopClip();
2279
0
2280
0
#endif // MOZ_X11
2281
0
2282
0
    EndRemoteDrawingInRegion(dt, region);
2283
0
2284
0
    listener->DidPaintWindow();
2285
0
2286
0
    // Synchronously flush any new dirty areas
2287
0
    cairo_region_t* dirtyArea = gdk_window_get_update_area(mGdkWindow);
2288
0
2289
0
    if (dirtyArea) {
2290
0
        gdk_window_invalidate_region(mGdkWindow, dirtyArea, false);
2291
0
        cairo_region_destroy(dirtyArea);
2292
0
        gdk_window_process_updates(mGdkWindow, false);
2293
0
    }
2294
0
2295
0
    // check the return value!
2296
0
    return TRUE;
2297
0
}
2298
2299
void
2300
nsWindow::UpdateAlpha(SourceSurface* aSourceSurface, nsIntRect aBoundsRect)
2301
0
{
2302
0
    // We need to create our own buffer to force the stride to match the
2303
0
    // expected stride.
2304
0
    int32_t stride = GetAlignedStride<4>(aBoundsRect.width,
2305
0
                                         BytesPerPixel(SurfaceFormat::A8));
2306
0
    if (stride == 0) {
2307
0
        return;
2308
0
    }
2309
0
    int32_t bufferSize = stride * aBoundsRect.height;
2310
0
    auto imageBuffer = MakeUniqueFallible<uint8_t[]>(bufferSize);
2311
0
    {
2312
0
        RefPtr<DrawTarget> drawTarget = gfxPlatform::CreateDrawTargetForData(
2313
0
                                              imageBuffer.get(),
2314
0
                                              aBoundsRect.Size(),
2315
0
                                              stride, SurfaceFormat::A8);
2316
0
2317
0
        if (drawTarget) {
2318
0
            drawTarget->DrawSurface(aSourceSurface, Rect(0, 0, aBoundsRect.width, aBoundsRect.height),
2319
0
                                    Rect(0, 0, aSourceSurface->GetSize().width, aSourceSurface->GetSize().height),
2320
0
                                    DrawSurfaceOptions(SamplingFilter::POINT),
2321
0
                                    DrawOptions(1.0f, CompositionOp::OP_SOURCE));
2322
0
        }
2323
0
    }
2324
0
    UpdateTranslucentWindowAlphaInternal(aBoundsRect, imageBuffer.get(), stride);
2325
0
}
2326
2327
gboolean
2328
nsWindow::OnConfigureEvent(GtkWidget *aWidget, GdkEventConfigure *aEvent)
2329
0
{
2330
0
    // These events are only received on toplevel windows.
2331
0
    //
2332
0
    // GDK ensures that the coordinates are the client window top-left wrt the
2333
0
    // root window.
2334
0
    //
2335
0
    //   GDK calculates the cordinates for real ConfigureNotify events on
2336
0
    //   managed windows (that would normally be relative to the parent
2337
0
    //   window).
2338
0
    //
2339
0
    //   Synthetic ConfigureNotify events are from the window manager and
2340
0
    //   already relative to the root window.  GDK creates all X windows with
2341
0
    //   border_width = 0, so synthetic events also indicate the top-left of
2342
0
    //   the client window.
2343
0
    //
2344
0
    //   Override-redirect windows are children of the root window so parent
2345
0
    //   coordinates are root coordinates.
2346
0
2347
0
    LOG(("configure event [%p] %d %d %d %d\n", (void *)this,
2348
0
         aEvent->x, aEvent->y, aEvent->width, aEvent->height));
2349
0
2350
0
    if (mPendingConfigures > 0) {
2351
0
        mPendingConfigures--;
2352
0
    }
2353
0
2354
0
    LayoutDeviceIntRect screenBounds = GetScreenBounds();
2355
0
2356
0
    if (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog) {
2357
0
        // This check avoids unwanted rollup on spurious configure events from
2358
0
        // Cygwin/X (bug 672103).
2359
0
        if (mBounds.x != screenBounds.x ||
2360
0
            mBounds.y != screenBounds.y) {
2361
0
            CheckForRollup(0, 0, false, true);
2362
0
        }
2363
0
    }
2364
0
2365
0
    // This event indicates that the window position may have changed.
2366
0
    // mBounds.Size() is updated in OnSizeAllocate().
2367
0
2368
0
    NS_ASSERTION(GTK_IS_WINDOW(aWidget),
2369
0
                 "Configure event on widget that is not a GtkWindow");
2370
0
    if (gtk_window_get_window_type(GTK_WINDOW(aWidget)) == GTK_WINDOW_POPUP) {
2371
0
        // Override-redirect window
2372
0
        //
2373
0
        // These windows should not be moved by the window manager, and so any
2374
0
        // change in position is a result of our direction.  mBounds has
2375
0
        // already been set in std::move() or Resize(), and that is more
2376
0
        // up-to-date than the position in the ConfigureNotify event if the
2377
0
        // event is from an earlier window move.
2378
0
        //
2379
0
        // Skipping the WindowMoved call saves context menus from an infinite
2380
0
        // loop when nsXULPopupManager::PopupMoved moves the window to the new
2381
0
        // position and nsMenuPopupFrame::SetPopupPosition adds
2382
0
        // offsetForContextMenu on each iteration.
2383
0
        return FALSE;
2384
0
    }
2385
0
2386
0
    mBounds.MoveTo(screenBounds.TopLeft());
2387
0
2388
0
    // XXX mozilla will invalidate the entire window after this move
2389
0
    // complete.  wtf?
2390
0
    NotifyWindowMoved(mBounds.x, mBounds.y);
2391
0
2392
0
    return FALSE;
2393
0
}
2394
2395
void
2396
nsWindow::OnContainerUnrealize()
2397
0
{
2398
0
    // The GdkWindows are about to be destroyed (but not deleted), so remove
2399
0
    // their references back to their container widget while the GdkWindow
2400
0
    // hierarchy is still available.
2401
0
2402
0
    if (mGdkWindow) {
2403
0
        DestroyChildWindows();
2404
0
2405
0
        g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr);
2406
0
        mGdkWindow = nullptr;
2407
0
    }
2408
0
}
2409
2410
void
2411
nsWindow::OnSizeAllocate(GtkAllocation *aAllocation)
2412
0
{
2413
0
    LOG(("size_allocate [%p] %d %d %d %d\n",
2414
0
         (void *)this, aAllocation->x, aAllocation->y,
2415
0
         aAllocation->width, aAllocation->height));
2416
0
2417
0
    LayoutDeviceIntSize size = GdkRectToDevicePixels(*aAllocation).Size();
2418
0
2419
0
    if (mBounds.Size() == size)
2420
0
        return;
2421
0
2422
0
    // Invalidate the new part of the window now for the pending paint to
2423
0
    // minimize background flashes (GDK does not do this for external resizes
2424
0
    // of toplevels.)
2425
0
    if (mBounds.width < size.width) {
2426
0
        GdkRectangle rect = DevicePixelsToGdkRectRoundOut(
2427
0
            LayoutDeviceIntRect(mBounds.width, 0,
2428
0
                                size.width - mBounds.width, size.height));
2429
0
        gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
2430
0
    }
2431
0
    if (mBounds.height < size.height) {
2432
0
        GdkRectangle rect = DevicePixelsToGdkRectRoundOut(
2433
0
            LayoutDeviceIntRect(0, mBounds.height,
2434
0
                                size.width, size.height - mBounds.height));
2435
0
        gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
2436
0
    }
2437
0
2438
0
    mBounds.SizeTo(size);
2439
0
2440
0
#ifdef MOZ_X11
2441
0
    // Notify the GtkCompositorWidget of a ClientSizeChange
2442
0
    if (mCompositorWidgetDelegate) {
2443
0
      mCompositorWidgetDelegate->NotifyClientSizeChanged(GetClientSize());
2444
0
    }
2445
0
#endif
2446
0
2447
0
    // Gecko permits running nested event loops during processing of events,
2448
0
    // GtkWindow callers of gtk_widget_size_allocate expect the signal
2449
0
    // handlers to return sometime in the near future.
2450
0
    mNeedsDispatchResized = true;
2451
0
    NS_DispatchToCurrentThread(NewRunnableMethod(
2452
0
      "nsWindow::MaybeDispatchResized", this, &nsWindow::MaybeDispatchResized));
2453
0
}
2454
2455
void
2456
nsWindow::OnDeleteEvent()
2457
0
{
2458
0
    if (mWidgetListener)
2459
0
        mWidgetListener->RequestWindowClose(this);
2460
0
}
2461
2462
void
2463
nsWindow::OnEnterNotifyEvent(GdkEventCrossing *aEvent)
2464
0
{
2465
0
    // This skips NotifyVirtual and NotifyNonlinearVirtual enter notify events
2466
0
    // when the pointer enters a child window.  If the destination window is a
2467
0
    // Gecko window then we'll catch the corresponding event on that window,
2468
0
    // but we won't notice when the pointer directly enters a foreign (plugin)
2469
0
    // child window without passing over a visible portion of a Gecko window.
2470
0
    if (aEvent->subwindow != nullptr)
2471
0
        return;
2472
0
2473
0
    // Check before is_parent_ungrab_enter() as the button state may have
2474
0
    // changed while a non-Gecko ancestor window had a pointer grab.
2475
0
    DispatchMissedButtonReleases(aEvent);
2476
0
2477
0
    if (is_parent_ungrab_enter(aEvent))
2478
0
        return;
2479
0
2480
0
    WidgetMouseEvent event(true, eMouseEnterIntoWidget, this,
2481
0
                           WidgetMouseEvent::eReal);
2482
0
2483
0
    event.mRefPoint = GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y);
2484
0
    event.AssignEventTime(GetWidgetEventTime(aEvent->time));
2485
0
2486
0
    LOG(("OnEnterNotify: %p\n", (void *)this));
2487
0
2488
0
    DispatchInputEvent(&event);
2489
0
}
2490
2491
// XXX Is this the right test for embedding cases?
2492
static bool
2493
is_top_level_mouse_exit(GdkWindow* aWindow, GdkEventCrossing *aEvent)
2494
0
{
2495
0
    auto x = gint(aEvent->x_root);
2496
0
    auto y = gint(aEvent->y_root);
2497
0
    GdkDisplay* display = gdk_window_get_display(aWindow);
2498
0
    GdkWindow* winAtPt = gdk_display_get_window_at_pointer(display, &x, &y);
2499
0
    if (!winAtPt)
2500
0
        return true;
2501
0
    GdkWindow* topLevelAtPt = gdk_window_get_toplevel(winAtPt);
2502
0
    GdkWindow* topLevelWidget = gdk_window_get_toplevel(aWindow);
2503
0
    return topLevelAtPt != topLevelWidget;
2504
0
}
2505
2506
void
2507
nsWindow::OnLeaveNotifyEvent(GdkEventCrossing *aEvent)
2508
0
{
2509
0
    // This ignores NotifyVirtual and NotifyNonlinearVirtual leave notify
2510
0
    // events when the pointer leaves a child window.  If the destination
2511
0
    // window is a Gecko window then we'll catch the corresponding event on
2512
0
    // that window.
2513
0
    //
2514
0
    // XXXkt However, we will miss toplevel exits when the pointer directly
2515
0
    // leaves a foreign (plugin) child window without passing over a visible
2516
0
    // portion of a Gecko window.
2517
0
    if (aEvent->subwindow != nullptr)
2518
0
        return;
2519
0
2520
0
    WidgetMouseEvent event(true, eMouseExitFromWidget, this,
2521
0
                           WidgetMouseEvent::eReal);
2522
0
2523
0
    event.mRefPoint = GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y);
2524
0
    event.AssignEventTime(GetWidgetEventTime(aEvent->time));
2525
0
2526
0
    event.mExitFrom = is_top_level_mouse_exit(mGdkWindow, aEvent)
2527
0
        ? WidgetMouseEvent::eTopLevel : WidgetMouseEvent::eChild;
2528
0
2529
0
    LOG(("OnLeaveNotify: %p\n", (void *)this));
2530
0
2531
0
    DispatchInputEvent(&event);
2532
0
}
2533
2534
template <typename Event> static LayoutDeviceIntPoint
2535
GetRefPoint(nsWindow* aWindow, Event* aEvent)
2536
0
{
2537
0
    if (aEvent->window == aWindow->GetGdkWindow()) {
2538
0
        // we are the window that the event happened on so no need for expensive WidgetToScreenOffset
2539
0
        return aWindow->GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y);
2540
0
    }
2541
0
    // XXX we're never quite sure which GdkWindow the event came from due to our custom bubbling
2542
0
    // in scroll_event_cb(), so use ScreenToWidget to translate the screen root coordinates into
2543
0
    // coordinates relative to this widget.
2544
0
    return aWindow->GdkEventCoordsToDevicePixels(
2545
0
        aEvent->x_root, aEvent->y_root) - aWindow->WidgetToScreenOffset();
2546
0
}
Unexecuted instantiation: nsWindow.cpp:mozilla::gfx::IntPointTyped<mozilla::LayoutDevicePixel> GetRefPoint<_GdkEventMotion>(nsWindow*, _GdkEventMotion*)
Unexecuted instantiation: nsWindow.cpp:mozilla::gfx::IntPointTyped<mozilla::LayoutDevicePixel> GetRefPoint<_GdkEventButton>(nsWindow*, _GdkEventButton*)
Unexecuted instantiation: nsWindow.cpp:mozilla::gfx::IntPointTyped<mozilla::LayoutDevicePixel> GetRefPoint<_GdkEventScroll>(nsWindow*, _GdkEventScroll*)
Unexecuted instantiation: nsWindow.cpp:mozilla::gfx::IntPointTyped<mozilla::LayoutDevicePixel> GetRefPoint<_GdkEventTouch>(nsWindow*, _GdkEventTouch*)
2547
2548
void
2549
nsWindow::OnMotionNotifyEvent(GdkEventMotion *aEvent)
2550
0
{
2551
0
    // see if we can compress this event
2552
0
    // XXXldb Why skip every other motion event when we have multiple,
2553
0
    // but not more than that?
2554
0
    bool synthEvent = false;
2555
0
#ifdef MOZ_X11
2556
0
    XEvent xevent;
2557
0
2558
0
    if (mIsX11Display) {
2559
0
        while (XPending (GDK_WINDOW_XDISPLAY(aEvent->window))) {
2560
0
            XEvent peeked;
2561
0
            XPeekEvent (GDK_WINDOW_XDISPLAY(aEvent->window), &peeked);
2562
0
            if (peeked.xany.window != gdk_x11_window_get_xid(aEvent->window)
2563
0
                || peeked.type != MotionNotify)
2564
0
                break;
2565
0
2566
0
            synthEvent = true;
2567
0
            XNextEvent (GDK_WINDOW_XDISPLAY(aEvent->window), &xevent);
2568
0
        }
2569
0
    }
2570
0
#endif /* MOZ_X11 */
2571
0
2572
0
    WidgetMouseEvent event(true, eMouseMove, this, WidgetMouseEvent::eReal);
2573
0
2574
0
    gdouble pressure = 0;
2575
0
    gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
2576
0
    // Sometime gdk generate 0 pressure value between normal values
2577
0
    // We have to ignore that and use last valid value
2578
0
    if (pressure)
2579
0
      mLastMotionPressure = pressure;
2580
0
    event.pressure = mLastMotionPressure;
2581
0
2582
0
    guint modifierState;
2583
0
    if (synthEvent) {
2584
0
#ifdef MOZ_X11
2585
0
        event.mRefPoint.x = nscoord(xevent.xmotion.x);
2586
0
        event.mRefPoint.y = nscoord(xevent.xmotion.y);
2587
0
2588
0
        modifierState = xevent.xmotion.state;
2589
0
2590
0
        event.AssignEventTime(GetWidgetEventTime(xevent.xmotion.time));
2591
#else
2592
        event.mRefPoint = GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y);
2593
2594
        modifierState = aEvent->state;
2595
2596
        event.AssignEventTime(GetWidgetEventTime(aEvent->time));
2597
#endif /* MOZ_X11 */
2598
0
    } else {
2599
0
        event.mRefPoint = GetRefPoint(this, aEvent);
2600
0
2601
0
        modifierState = aEvent->state;
2602
0
2603
0
        event.AssignEventTime(GetWidgetEventTime(aEvent->time));
2604
0
    }
2605
0
2606
0
    KeymapWrapper::InitInputEvent(event, modifierState);
2607
0
2608
0
    DispatchInputEvent(&event);
2609
0
}
2610
2611
// If the automatic pointer grab on ButtonPress has deactivated before
2612
// ButtonRelease, and the mouse button is released while the pointer is not
2613
// over any a Gecko window, then the ButtonRelease event will not be received.
2614
// (A similar situation exists when the pointer is grabbed with owner_events
2615
// True as the ButtonRelease may be received on a foreign [plugin] window).
2616
// Use this method to check for released buttons when the pointer returns to a
2617
// Gecko window.
2618
void
2619
nsWindow::DispatchMissedButtonReleases(GdkEventCrossing *aGdkEvent)
2620
0
{
2621
0
    guint changed = aGdkEvent->state ^ gButtonState;
2622
0
    // Only consider button releases.
2623
0
    // (Ignore button presses that occurred outside Gecko.)
2624
0
    guint released = changed & gButtonState;
2625
0
    gButtonState = aGdkEvent->state;
2626
0
2627
0
    // Loop over each button, excluding mouse wheel buttons 4 and 5 for which
2628
0
    // GDK ignores releases.
2629
0
    for (guint buttonMask = GDK_BUTTON1_MASK;
2630
0
         buttonMask <= GDK_BUTTON3_MASK;
2631
0
         buttonMask <<= 1) {
2632
0
2633
0
        if (released & buttonMask) {
2634
0
            int16_t buttonType;
2635
0
            switch (buttonMask) {
2636
0
            case GDK_BUTTON1_MASK:
2637
0
                buttonType = WidgetMouseEvent::eLeftButton;
2638
0
                break;
2639
0
            case GDK_BUTTON2_MASK:
2640
0
                buttonType = WidgetMouseEvent::eMiddleButton;
2641
0
                break;
2642
0
            default:
2643
0
                NS_ASSERTION(buttonMask == GDK_BUTTON3_MASK,
2644
0
                             "Unexpected button mask");
2645
0
                buttonType = WidgetMouseEvent::eRightButton;
2646
0
            }
2647
0
2648
0
            LOG(("Synthesized button %u release on %p\n",
2649
0
                 guint(buttonType + 1), (void *)this));
2650
0
2651
0
            // Dispatch a synthesized button up event to tell Gecko about the
2652
0
            // change in state.  This event is marked as synthesized so that
2653
0
            // it is not dispatched as a DOM event, because we don't know the
2654
0
            // position, widget, modifiers, or time/order.
2655
0
            WidgetMouseEvent synthEvent(true, eMouseUp, this,
2656
0
                                        WidgetMouseEvent::eSynthesized);
2657
0
            synthEvent.button = buttonType;
2658
0
            DispatchInputEvent(&synthEvent);
2659
0
        }
2660
0
    }
2661
0
}
2662
2663
void
2664
nsWindow::InitButtonEvent(WidgetMouseEvent& aEvent,
2665
                          GdkEventButton* aGdkEvent)
2666
0
{
2667
0
    aEvent.mRefPoint = GetRefPoint(this, aGdkEvent);
2668
0
2669
0
    guint modifierState = aGdkEvent->state;
2670
0
    // aEvent's state includes the button state from immediately before this
2671
0
    // event.  If aEvent is a mousedown or mouseup event, we need to update
2672
0
    // the button state.
2673
0
    guint buttonMask = 0;
2674
0
    switch (aGdkEvent->button) {
2675
0
        case 1:
2676
0
            buttonMask = GDK_BUTTON1_MASK;
2677
0
            break;
2678
0
        case 2:
2679
0
            buttonMask = GDK_BUTTON2_MASK;
2680
0
            break;
2681
0
        case 3:
2682
0
            buttonMask = GDK_BUTTON3_MASK;
2683
0
            break;
2684
0
    }
2685
0
    if (aGdkEvent->type == GDK_BUTTON_RELEASE) {
2686
0
        modifierState &= ~buttonMask;
2687
0
    } else {
2688
0
        modifierState |= buttonMask;
2689
0
    }
2690
0
2691
0
    KeymapWrapper::InitInputEvent(aEvent, modifierState);
2692
0
2693
0
    aEvent.AssignEventTime(GetWidgetEventTime(aGdkEvent->time));
2694
0
2695
0
    switch (aGdkEvent->type) {
2696
0
    case GDK_2BUTTON_PRESS:
2697
0
        aEvent.mClickCount = 2;
2698
0
        break;
2699
0
    case GDK_3BUTTON_PRESS:
2700
0
        aEvent.mClickCount = 3;
2701
0
        break;
2702
0
        // default is one click
2703
0
    default:
2704
0
        aEvent.mClickCount = 1;
2705
0
    }
2706
0
}
2707
2708
static guint ButtonMaskFromGDKButton(guint button)
2709
0
{
2710
0
    return GDK_BUTTON1_MASK << (button - 1);
2711
0
}
2712
2713
void
2714
nsWindow::DispatchContextMenuEventFromMouseEvent(uint16_t domButton,
2715
                                                 GdkEventButton *aEvent)
2716
0
{
2717
0
    if (domButton == WidgetMouseEvent::eRightButton && MOZ_LIKELY(!mIsDestroyed)) {
2718
0
        WidgetMouseEvent contextMenuEvent(true, eContextMenu, this,
2719
0
                                          WidgetMouseEvent::eReal);
2720
0
        InitButtonEvent(contextMenuEvent, aEvent);
2721
0
        contextMenuEvent.pressure = mLastMotionPressure;
2722
0
        DispatchInputEvent(&contextMenuEvent);
2723
0
    }
2724
0
}
2725
2726
void
2727
nsWindow::OnButtonPressEvent(GdkEventButton *aEvent)
2728
0
{
2729
0
    LOG(("Button %u press on %p\n", aEvent->button, (void *)this));
2730
0
2731
0
    // If you double click in GDK, it will actually generate a second
2732
0
    // GDK_BUTTON_PRESS before sending the GDK_2BUTTON_PRESS, and this is
2733
0
    // different than the DOM spec.  GDK puts this in the queue
2734
0
    // programatically, so it's safe to assume that if there's a
2735
0
    // double click in the queue, it was generated so we can just drop
2736
0
    // this click.
2737
0
    GdkEvent *peekedEvent = gdk_event_peek();
2738
0
    if (peekedEvent) {
2739
0
        GdkEventType type = peekedEvent->any.type;
2740
0
        gdk_event_free(peekedEvent);
2741
0
        if (type == GDK_2BUTTON_PRESS || type == GDK_3BUTTON_PRESS)
2742
0
            return;
2743
0
    }
2744
0
2745
0
    nsWindow *containerWindow = GetContainerWindow();
2746
0
    if (!gFocusWindow && containerWindow) {
2747
0
        containerWindow->DispatchActivateEvent();
2748
0
    }
2749
0
2750
0
    // check to see if we should rollup
2751
0
    if (CheckForRollup(aEvent->x_root, aEvent->y_root, false, false))
2752
0
        return;
2753
0
2754
0
    gdouble pressure = 0;
2755
0
    gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
2756
0
    mLastMotionPressure = pressure;
2757
0
2758
0
    uint16_t domButton;
2759
0
    switch (aEvent->button) {
2760
0
    case 1:
2761
0
        domButton = WidgetMouseEvent::eLeftButton;
2762
0
        break;
2763
0
    case 2:
2764
0
        domButton = WidgetMouseEvent::eMiddleButton;
2765
0
        break;
2766
0
    case 3:
2767
0
        domButton = WidgetMouseEvent::eRightButton;
2768
0
        break;
2769
0
    // These are mapped to horizontal scroll
2770
0
    case 6:
2771
0
    case 7:
2772
0
        NS_WARNING("We're not supporting legacy horizontal scroll event");
2773
0
        return;
2774
0
    // Map buttons 8-9 to back/forward
2775
0
    case 8:
2776
0
        DispatchCommandEvent(nsGkAtoms::Back);
2777
0
        return;
2778
0
    case 9:
2779
0
        DispatchCommandEvent(nsGkAtoms::Forward);
2780
0
        return;
2781
0
    default:
2782
0
        return;
2783
0
    }
2784
0
2785
0
    gButtonState |= ButtonMaskFromGDKButton(aEvent->button);
2786
0
2787
0
    WidgetMouseEvent event(true, eMouseDown, this, WidgetMouseEvent::eReal);
2788
0
    event.button = domButton;
2789
0
    InitButtonEvent(event, aEvent);
2790
0
    event.pressure = mLastMotionPressure;
2791
0
2792
0
    DispatchInputEvent(&event);
2793
0
2794
0
    // right menu click on linux should also pop up a context menu
2795
0
    if (!nsBaseWidget::ShowContextMenuAfterMouseUp()) {
2796
0
        DispatchContextMenuEventFromMouseEvent(domButton, aEvent);
2797
0
    }
2798
0
}
2799
2800
void
2801
nsWindow::OnButtonReleaseEvent(GdkEventButton *aEvent)
2802
0
{
2803
0
    LOG(("Button %u release on %p\n", aEvent->button, (void *)this));
2804
0
2805
0
    uint16_t domButton;
2806
0
    switch (aEvent->button) {
2807
0
    case 1:
2808
0
        domButton = WidgetMouseEvent::eLeftButton;
2809
0
        break;
2810
0
    case 2:
2811
0
        domButton = WidgetMouseEvent::eMiddleButton;
2812
0
        break;
2813
0
    case 3:
2814
0
        domButton = WidgetMouseEvent::eRightButton;
2815
0
        break;
2816
0
    default:
2817
0
        return;
2818
0
    }
2819
0
2820
0
    gButtonState &= ~ButtonMaskFromGDKButton(aEvent->button);
2821
0
2822
0
    WidgetMouseEvent event(true, eMouseUp, this,
2823
0
                           WidgetMouseEvent::eReal);
2824
0
    event.button = domButton;
2825
0
    InitButtonEvent(event, aEvent);
2826
0
    gdouble pressure = 0;
2827
0
    gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
2828
0
    event.pressure = pressure ? pressure : mLastMotionPressure;
2829
0
2830
0
    // The mRefPoint is manipulated in DispatchInputEvent, we're saving it
2831
0
    // to use it for the doubleclick position check.
2832
0
    LayoutDeviceIntPoint pos = event.mRefPoint;
2833
0
2834
0
    nsEventStatus eventStatus = DispatchInputEvent(&event);
2835
0
2836
0
    bool defaultPrevented = (eventStatus == nsEventStatus_eConsumeNoDefault);
2837
0
    // Check if mouse position in titlebar and doubleclick happened to
2838
0
    // trigger restore/maximize.
2839
0
    if (!defaultPrevented
2840
0
             && mDrawInTitlebar
2841
0
             && event.button == WidgetMouseEvent::eLeftButton
2842
0
             && event.mClickCount == 2
2843
0
             && mDraggableRegion.Contains(pos.x, pos.y)) {
2844
0
2845
0
        if (mSizeState == nsSizeMode_Maximized) {
2846
0
            SetSizeMode(nsSizeMode_Normal);
2847
0
        } else {
2848
0
            SetSizeMode(nsSizeMode_Maximized);
2849
0
        }
2850
0
    }
2851
0
    mLastMotionPressure = pressure;
2852
0
2853
0
    // right menu click on linux should also pop up a context menu
2854
0
    if (nsBaseWidget::ShowContextMenuAfterMouseUp()) {
2855
0
        DispatchContextMenuEventFromMouseEvent(domButton, aEvent);
2856
0
    }
2857
0
}
2858
2859
void
2860
nsWindow::OnContainerFocusInEvent(GdkEventFocus *aEvent)
2861
0
{
2862
0
    LOGFOCUS(("OnContainerFocusInEvent [%p]\n", (void *)this));
2863
0
2864
0
    // Unset the urgency hint, if possible
2865
0
    GtkWidget* top_window = GetToplevelWidget();
2866
0
    if (top_window && (gtk_widget_get_visible(top_window)))
2867
0
        SetUrgencyHint(top_window, false);
2868
0
2869
0
    // Return if being called within SetFocus because the focus manager
2870
0
    // already knows that the window is active.
2871
0
    if (gBlockActivateEvent) {
2872
0
        LOGFOCUS(("activated notification is blocked [%p]\n", (void *)this));
2873
0
        return;
2874
0
    }
2875
0
2876
0
    // If keyboard input will be accepted, the focus manager will call
2877
0
    // SetFocus to set the correct window.
2878
0
    gFocusWindow = nullptr;
2879
0
2880
0
    DispatchActivateEvent();
2881
0
2882
0
    if (!gFocusWindow) {
2883
0
        // We don't really have a window for dispatching key events, but
2884
0
        // setting a non-nullptr value here prevents OnButtonPressEvent() from
2885
0
        // dispatching an activation notification if the widget is already
2886
0
        // active.
2887
0
        gFocusWindow = this;
2888
0
    }
2889
0
2890
0
    LOGFOCUS(("Events sent from focus in event [%p]\n", (void *)this));
2891
0
}
2892
2893
void
2894
nsWindow::OnContainerFocusOutEvent(GdkEventFocus *aEvent)
2895
0
{
2896
0
    LOGFOCUS(("OnContainerFocusOutEvent [%p]\n", (void *)this));
2897
0
2898
0
    if (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog) {
2899
0
        nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
2900
0
        nsCOMPtr<nsIDragSession> dragSession;
2901
0
        dragService->GetCurrentSession(getter_AddRefs(dragSession));
2902
0
2903
0
        // Rollup popups when a window is focused out unless a drag is occurring.
2904
0
        // This check is because drags grab the keyboard and cause a focus out on
2905
0
        // versions of GTK before 2.18.
2906
0
        bool shouldRollup = !dragSession;
2907
0
        if (!shouldRollup) {
2908
0
            // we also roll up when a drag is from a different application
2909
0
            nsCOMPtr<nsINode> sourceNode;
2910
0
            dragSession->GetSourceNode(getter_AddRefs(sourceNode));
2911
0
            shouldRollup = (sourceNode == nullptr);
2912
0
        }
2913
0
2914
0
        if (shouldRollup) {
2915
0
            CheckForRollup(0, 0, false, true);
2916
0
        }
2917
0
    }
2918
0
2919
0
    if (gFocusWindow) {
2920
0
        RefPtr<nsWindow> kungFuDeathGrip = gFocusWindow;
2921
0
        if (gFocusWindow->mIMContext) {
2922
0
            gFocusWindow->mIMContext->OnBlurWindow(gFocusWindow);
2923
0
        }
2924
0
        gFocusWindow = nullptr;
2925
0
    }
2926
0
2927
0
    DispatchDeactivateEvent();
2928
0
2929
0
    LOGFOCUS(("Done with container focus out [%p]\n", (void *)this));
2930
0
}
2931
2932
bool
2933
nsWindow::DispatchCommandEvent(nsAtom* aCommand)
2934
0
{
2935
0
    nsEventStatus status;
2936
0
    WidgetCommandEvent appCommandEvent(true, aCommand, this);
2937
0
    DispatchEvent(&appCommandEvent, status);
2938
0
    return TRUE;
2939
0
}
2940
2941
bool
2942
nsWindow::DispatchContentCommandEvent(EventMessage aMsg)
2943
0
{
2944
0
  nsEventStatus status;
2945
0
  WidgetContentCommandEvent event(true, aMsg, this);
2946
0
  DispatchEvent(&event, status);
2947
0
  return TRUE;
2948
0
}
2949
2950
static bool
2951
IsCtrlAltTab(GdkEventKey *aEvent)
2952
0
{
2953
0
    return aEvent->keyval == GDK_Tab &&
2954
0
        KeymapWrapper::AreModifiersActive(
2955
0
            KeymapWrapper::CTRL | KeymapWrapper::ALT, aEvent->state);
2956
0
}
2957
2958
bool
2959
nsWindow::DispatchKeyDownOrKeyUpEvent(GdkEventKey* aEvent,
2960
                                      bool aIsProcessedByIME,
2961
                                      bool* aIsCancelled)
2962
0
{
2963
0
    MOZ_ASSERT(aIsCancelled, "aIsCancelled must not be nullptr");
2964
0
2965
0
    *aIsCancelled = false;
2966
0
2967
0
    if (aEvent->type == GDK_KEY_PRESS && IsCtrlAltTab(aEvent)) {
2968
0
        return false;
2969
0
    }
2970
0
2971
0
    EventMessage message =
2972
0
        aEvent->type == GDK_KEY_PRESS ? eKeyDown : eKeyUp;
2973
0
    WidgetKeyboardEvent keyEvent(true, message, this);
2974
0
    KeymapWrapper::InitKeyEvent(keyEvent, aEvent, aIsProcessedByIME);
2975
0
    return DispatchKeyDownOrKeyUpEvent(keyEvent, aIsCancelled);
2976
0
}
2977
bool
2978
nsWindow::DispatchKeyDownOrKeyUpEvent(WidgetKeyboardEvent& aKeyboardEvent,
2979
                                      bool* aIsCancelled)
2980
0
{
2981
0
    MOZ_ASSERT(aIsCancelled, "aIsCancelled must not be nullptr");
2982
0
2983
0
    *aIsCancelled = false;
2984
0
2985
0
    RefPtr<TextEventDispatcher> dispatcher = GetTextEventDispatcher();
2986
0
    nsresult rv = dispatcher->BeginNativeInputTransaction();
2987
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
2988
0
        return FALSE;
2989
0
    }
2990
0
2991
0
    nsEventStatus status = nsEventStatus_eIgnore;
2992
0
    bool dispatched =
2993
0
        dispatcher->DispatchKeyboardEvent(aKeyboardEvent.mMessage,
2994
0
                                          aKeyboardEvent, status, nullptr);
2995
0
    *aIsCancelled = (status == nsEventStatus_eConsumeNoDefault);
2996
0
    return dispatched;
2997
0
}
2998
2999
WidgetEventTime
3000
nsWindow::GetWidgetEventTime(guint32 aEventTime)
3001
0
{
3002
0
  return WidgetEventTime(aEventTime, GetEventTimeStamp(aEventTime));
3003
0
}
3004
3005
TimeStamp
3006
nsWindow::GetEventTimeStamp(guint32 aEventTime)
3007
0
{
3008
0
    if (MOZ_UNLIKELY(!mGdkWindow)) {
3009
0
        // nsWindow has been Destroy()ed.
3010
0
        return TimeStamp::Now();
3011
0
    }
3012
0
    if (aEventTime == 0) {
3013
0
        // Some X11 and GDK events may be received with a time of 0 to indicate
3014
0
        // that they are synthetic events. Some input method editors do this.
3015
0
        // In this case too, just return the current timestamp.
3016
0
        return TimeStamp::Now();
3017
0
    }
3018
0
3019
0
    TimeStamp eventTimeStamp;
3020
0
3021
0
    if (!mIsX11Display) {
3022
0
        // Wayland compositors use monotonic time to set timestamps.
3023
0
        int64_t timestampTime = g_get_monotonic_time() / 1000;
3024
0
        guint32 refTimeTruncated = guint32(timestampTime);
3025
0
3026
0
        timestampTime -= refTimeTruncated - aEventTime;
3027
0
        int64_t tick =
3028
0
            BaseTimeDurationPlatformUtils::TicksFromMilliseconds(timestampTime);
3029
0
        eventTimeStamp = TimeStamp::FromSystemTime(tick);
3030
0
    } else {
3031
0
        CurrentX11TimeGetter* getCurrentTime = GetCurrentTimeGetter();
3032
0
        MOZ_ASSERT(getCurrentTime,
3033
0
                   "Null current time getter despite having a window");
3034
0
        eventTimeStamp = TimeConverter().GetTimeStampFromSystemTime(aEventTime,
3035
0
                                                              *getCurrentTime);
3036
0
    }
3037
0
    return eventTimeStamp;
3038
0
}
3039
3040
mozilla::CurrentX11TimeGetter*
3041
0
nsWindow::GetCurrentTimeGetter() {
3042
0
    MOZ_ASSERT(mGdkWindow, "Expected mGdkWindow to be set");
3043
0
    if (MOZ_UNLIKELY(!mCurrentTimeGetter)) {
3044
0
        mCurrentTimeGetter = MakeUnique<CurrentX11TimeGetter>(mGdkWindow);
3045
0
    }
3046
0
    return mCurrentTimeGetter.get();
3047
0
}
3048
3049
gboolean
3050
nsWindow::OnKeyPressEvent(GdkEventKey *aEvent)
3051
0
{
3052
0
    LOGFOCUS(("OnKeyPressEvent [%p]\n", (void *)this));
3053
0
3054
0
    // if we are in the middle of composing text, XIM gets to see it
3055
0
    // before mozilla does.
3056
0
    // FYI: Don't dispatch keydown event before notifying IME of the event
3057
0
    //      because IME may send a key event synchronously and consume the
3058
0
    //      original event.
3059
0
    bool IMEWasEnabled = false;
3060
0
    if (mIMContext) {
3061
0
        IMEWasEnabled = mIMContext->IsEnabled();
3062
0
        if (mIMContext->OnKeyEvent(this, aEvent)) {
3063
0
            return TRUE;
3064
0
        }
3065
0
    }
3066
0
3067
0
    // work around for annoying things.
3068
0
    if (IsCtrlAltTab(aEvent)) {
3069
0
        return TRUE;
3070
0
    }
3071
0
3072
0
    nsCOMPtr<nsIWidget> kungFuDeathGrip = this;
3073
0
3074
0
    // Dispatch keydown event always.  At auto repeating, we should send
3075
0
    // KEYDOWN -> KEYPRESS -> KEYDOWN -> KEYPRESS ... -> KEYUP
3076
0
    // However, old distributions (e.g., Ubuntu 9.10) sent native key
3077
0
    // release event, so, on such platform, the DOM events will be:
3078
0
    // KEYDOWN -> KEYPRESS -> KEYUP -> KEYDOWN -> KEYPRESS -> KEYUP...
3079
0
3080
0
    bool isKeyDownCancelled = false;
3081
0
    if (DispatchKeyDownOrKeyUpEvent(aEvent, false, &isKeyDownCancelled) &&
3082
0
        (MOZ_UNLIKELY(mIsDestroyed) || isKeyDownCancelled)) {
3083
0
        return TRUE;
3084
0
    }
3085
0
3086
0
    // If a keydown event handler causes to enable IME, i.e., it moves
3087
0
    // focus from IME unusable content to IME usable editor, we should
3088
0
    // send the native key event to IME for the first input on the editor.
3089
0
    if (!IMEWasEnabled && mIMContext && mIMContext->IsEnabled()) {
3090
0
        // Notice our keydown event was already dispatched.  This prevents
3091
0
        // unnecessary DOM keydown event in the editor.
3092
0
        if (mIMContext->OnKeyEvent(this, aEvent, true)) {
3093
0
            return TRUE;
3094
0
        }
3095
0
    }
3096
0
3097
0
    // Look for specialized app-command keys
3098
0
    switch (aEvent->keyval) {
3099
0
        case GDK_Back:
3100
0
            return DispatchCommandEvent(nsGkAtoms::Back);
3101
0
        case GDK_Forward:
3102
0
            return DispatchCommandEvent(nsGkAtoms::Forward);
3103
0
        case GDK_Refresh:
3104
0
            return DispatchCommandEvent(nsGkAtoms::Reload);
3105
0
        case GDK_Stop:
3106
0
            return DispatchCommandEvent(nsGkAtoms::Stop);
3107
0
        case GDK_Search:
3108
0
            return DispatchCommandEvent(nsGkAtoms::Search);
3109
0
        case GDK_Favorites:
3110
0
            return DispatchCommandEvent(nsGkAtoms::Bookmarks);
3111
0
        case GDK_HomePage:
3112
0
            return DispatchCommandEvent(nsGkAtoms::Home);
3113
0
        case GDK_Copy:
3114
0
        case GDK_F16:  // F16, F20, F18, F14 are old keysyms for Copy Cut Paste Undo
3115
0
            return DispatchContentCommandEvent(eContentCommandCopy);
3116
0
        case GDK_Cut:
3117
0
        case GDK_F20:
3118
0
            return DispatchContentCommandEvent(eContentCommandCut);
3119
0
        case GDK_Paste:
3120
0
        case GDK_F18:
3121
0
            return DispatchContentCommandEvent(eContentCommandPaste);
3122
0
        case GDK_Redo:
3123
0
            return DispatchContentCommandEvent(eContentCommandRedo);
3124
0
        case GDK_Undo:
3125
0
        case GDK_F14:
3126
0
            return DispatchContentCommandEvent(eContentCommandUndo);
3127
0
    }
3128
0
3129
0
    WidgetKeyboardEvent keypressEvent(true, eKeyPress, this);
3130
0
    KeymapWrapper::InitKeyEvent(keypressEvent, aEvent, false);
3131
0
3132
0
    // before we dispatch a key, check if it's the context menu key.
3133
0
    // If so, send a context menu key event instead.
3134
0
    if (MaybeDispatchContextMenuEvent(aEvent)) {
3135
0
        return TRUE;
3136
0
    }
3137
0
3138
0
    RefPtr<TextEventDispatcher> dispatcher = GetTextEventDispatcher();
3139
0
    nsresult rv = dispatcher->BeginNativeInputTransaction();
3140
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
3141
0
        return TRUE;
3142
0
    }
3143
0
3144
0
    // If the character code is in the BMP, send the key press event.
3145
0
    // Otherwise, send a compositionchange event with the equivalent UTF-16
3146
0
    // string.
3147
0
    // TODO: Investigate other browser's behavior in this case because
3148
0
    //       this hack is odd for UI Events.
3149
0
    nsEventStatus status = nsEventStatus_eIgnore;
3150
0
    if (keypressEvent.mKeyNameIndex != KEY_NAME_INDEX_USE_STRING ||
3151
0
        keypressEvent.mKeyValue.Length() == 1) {
3152
0
        dispatcher->MaybeDispatchKeypressEvents(keypressEvent,
3153
0
                                                status, aEvent);
3154
0
    } else {
3155
0
        WidgetEventTime eventTime = GetWidgetEventTime(aEvent->time);
3156
0
        dispatcher->CommitComposition(status, &keypressEvent.mKeyValue,
3157
0
                                      &eventTime);
3158
0
    }
3159
0
3160
0
    return TRUE;
3161
0
}
3162
3163
bool
3164
nsWindow::MaybeDispatchContextMenuEvent(const GdkEventKey* aEvent)
3165
0
{
3166
0
    KeyNameIndex keyNameIndex = KeymapWrapper::ComputeDOMKeyNameIndex(aEvent);
3167
0
3168
0
    // Shift+F10 and ContextMenu should cause eContextMenu event.
3169
0
    if (keyNameIndex != KEY_NAME_INDEX_F10 &&
3170
0
        keyNameIndex != KEY_NAME_INDEX_ContextMenu) {
3171
0
      return false;
3172
0
    }
3173
0
3174
0
    WidgetMouseEvent contextMenuEvent(true, eContextMenu, this,
3175
0
                                      WidgetMouseEvent::eReal,
3176
0
                                      WidgetMouseEvent::eContextMenuKey);
3177
0
3178
0
    contextMenuEvent.mRefPoint = LayoutDeviceIntPoint(0, 0);
3179
0
    contextMenuEvent.AssignEventTime(GetWidgetEventTime(aEvent->time));
3180
0
    contextMenuEvent.mClickCount = 1;
3181
0
    KeymapWrapper::InitInputEvent(contextMenuEvent, aEvent->state);
3182
0
3183
0
    if (contextMenuEvent.IsControl() || contextMenuEvent.IsMeta() ||
3184
0
        contextMenuEvent.IsAlt()) {
3185
0
        return false;
3186
0
    }
3187
0
3188
0
    // If the key is ContextMenu, then an eContextMenu mouse event is
3189
0
    // dispatched regardless of the state of the Shift modifier.  When it is
3190
0
    // pressed without the Shift modifier, a web page can prevent the default
3191
0
    // context menu action.  When pressed with the Shift modifier, the web page
3192
0
    // cannot prevent the default context menu action.
3193
0
    // (PresShell::HandleEventInternal() sets mOnlyChromeDispatch to true.)
3194
0
3195
0
    // If the key is F10, it needs Shift state because Shift+F10 is well-known
3196
0
    // shortcut key on Linux.  However, eContextMenu with Shift state is
3197
0
    // special.  It won't fire "contextmenu" event in the web content for
3198
0
    // blocking web page to prevent its default.  Therefore, this combination
3199
0
    // should work same as ContextMenu key.
3200
0
    // XXX Should we allow to block web page to prevent its default with
3201
0
    //     Ctrl+Shift+F10 or Alt+Shift+F10 instead?
3202
0
    if (keyNameIndex == KEY_NAME_INDEX_F10) {
3203
0
        if (!contextMenuEvent.IsShift()) {
3204
0
            return false;
3205
0
        }
3206
0
        contextMenuEvent.mModifiers &= ~MODIFIER_SHIFT;
3207
0
    }
3208
0
3209
0
    DispatchInputEvent(&contextMenuEvent);
3210
0
    return true;
3211
0
}
3212
3213
gboolean
3214
nsWindow::OnKeyReleaseEvent(GdkEventKey* aEvent)
3215
0
{
3216
0
    LOGFOCUS(("OnKeyReleaseEvent [%p]\n", (void *)this));
3217
0
3218
0
    if (mIMContext && mIMContext->OnKeyEvent(this, aEvent)) {
3219
0
        return TRUE;
3220
0
    }
3221
0
3222
0
    bool isCancelled = false;
3223
0
    if (NS_WARN_IF(!DispatchKeyDownOrKeyUpEvent(aEvent, false, &isCancelled))) {
3224
0
        return FALSE;
3225
0
    }
3226
0
3227
0
    return TRUE;
3228
0
}
3229
3230
void
3231
nsWindow::OnScrollEvent(GdkEventScroll *aEvent)
3232
0
{
3233
0
    // check to see if we should rollup
3234
0
    if (CheckForRollup(aEvent->x_root, aEvent->y_root, true, false))
3235
0
        return;
3236
0
#if GTK_CHECK_VERSION(3,4,0)
3237
0
    // check for duplicate legacy scroll event, see GNOME bug 726878
3238
0
    if (aEvent->direction != GDK_SCROLL_SMOOTH &&
3239
0
        mLastScrollEventTime == aEvent->time)
3240
0
        return;
3241
0
#endif
3242
0
    WidgetWheelEvent wheelEvent(true, eWheel, this);
3243
0
    wheelEvent.mDeltaMode = dom::WheelEvent_Binding::DOM_DELTA_LINE;
3244
0
    switch (aEvent->direction) {
3245
0
#if GTK_CHECK_VERSION(3,4,0)
3246
0
    case GDK_SCROLL_SMOOTH:
3247
0
    {
3248
0
        // As of GTK 3.4, all directional scroll events are provided by
3249
0
        // the GDK_SCROLL_SMOOTH direction on XInput2 devices.
3250
0
        mLastScrollEventTime = aEvent->time;
3251
0
        // TODO - use a more appropriate scrolling unit than lines.
3252
0
        // Multiply event deltas by 3 to emulate legacy behaviour.
3253
0
        wheelEvent.mDeltaX = aEvent->delta_x * 3;
3254
0
        wheelEvent.mDeltaY = aEvent->delta_y * 3;
3255
0
        wheelEvent.mIsNoLineOrPageDelta = true;
3256
0
        // This next step manually unsets smooth scrolling for touch devices
3257
0
        // that trigger GDK_SCROLL_SMOOTH. We use the slave device, which
3258
0
        // represents the actual input.
3259
0
        GdkDevice *device = gdk_event_get_source_device((GdkEvent*)aEvent);
3260
0
        GdkInputSource source = gdk_device_get_source(device);
3261
0
        if (source == GDK_SOURCE_TOUCHSCREEN ||
3262
0
            source == GDK_SOURCE_TOUCHPAD) {
3263
0
            wheelEvent.mScrollType = WidgetWheelEvent::SCROLL_ASYNCHRONOUSELY;
3264
0
        }
3265
0
        break;
3266
0
    }
3267
0
#endif
3268
0
    case GDK_SCROLL_UP:
3269
0
        wheelEvent.mDeltaY = wheelEvent.mLineOrPageDeltaY = -3;
3270
0
        break;
3271
0
    case GDK_SCROLL_DOWN:
3272
0
        wheelEvent.mDeltaY = wheelEvent.mLineOrPageDeltaY = 3;
3273
0
        break;
3274
0
    case GDK_SCROLL_LEFT:
3275
0
        wheelEvent.mDeltaX = wheelEvent.mLineOrPageDeltaX = -1;
3276
0
        break;
3277
0
    case GDK_SCROLL_RIGHT:
3278
0
        wheelEvent.mDeltaX = wheelEvent.mLineOrPageDeltaX = 1;
3279
0
        break;
3280
0
    }
3281
0
3282
0
    wheelEvent.mRefPoint = GetRefPoint(this, aEvent);
3283
0
3284
0
    KeymapWrapper::InitInputEvent(wheelEvent, aEvent->state);
3285
0
3286
0
    wheelEvent.AssignEventTime(GetWidgetEventTime(aEvent->time));
3287
0
3288
0
    DispatchInputEvent(&wheelEvent);
3289
0
}
3290
3291
void
3292
nsWindow::OnVisibilityNotifyEvent(GdkEventVisibility *aEvent)
3293
0
{
3294
0
    LOGDRAW(("Visibility event %i on [%p] %p\n",
3295
0
             aEvent->state, this, aEvent->window));
3296
0
3297
0
    if (!mGdkWindow)
3298
0
        return;
3299
0
3300
0
    switch (aEvent->state) {
3301
0
    case GDK_VISIBILITY_UNOBSCURED:
3302
0
    case GDK_VISIBILITY_PARTIAL:
3303
0
        if (mIsFullyObscured && mHasMappedToplevel) {
3304
0
            // GDK_EXPOSE events have been ignored, so make sure GDK
3305
0
            // doesn't think that the window has already been painted.
3306
0
            gdk_window_invalidate_rect(mGdkWindow, nullptr, FALSE);
3307
0
        }
3308
0
3309
0
        mIsFullyObscured = false;
3310
0
3311
0
        // if we have to retry the grab, retry it.
3312
0
        EnsureGrabs();
3313
0
        break;
3314
0
    default: // includes GDK_VISIBILITY_FULLY_OBSCURED
3315
0
        mIsFullyObscured = true;
3316
0
        break;
3317
0
    }
3318
0
}
3319
3320
void
3321
nsWindow::OnWindowStateEvent(GtkWidget *aWidget, GdkEventWindowState *aEvent)
3322
0
{
3323
0
    LOG(("nsWindow::OnWindowStateEvent [%p] changed %d new_window_state %d\n",
3324
0
         (void *)this, aEvent->changed_mask, aEvent->new_window_state));
3325
0
3326
0
    if (IS_MOZ_CONTAINER(aWidget)) {
3327
0
        // This event is notifying the container widget of changes to the
3328
0
        // toplevel window.  Just detect changes affecting whether windows are
3329
0
        // viewable.
3330
0
        //
3331
0
        // (A visibility notify event is sent to each window that becomes
3332
0
        // viewable when the toplevel is mapped, but we can't rely on that for
3333
0
        // setting mHasMappedToplevel because these toplevel window state
3334
0
        // events are asynchronous.  The windows in the hierarchy now may not
3335
0
        // be the same windows as when the toplevel was mapped, so they may
3336
0
        // not get VisibilityNotify events.)
3337
0
        bool mapped =
3338
0
            !(aEvent->new_window_state &
3339
0
              (GDK_WINDOW_STATE_ICONIFIED|GDK_WINDOW_STATE_WITHDRAWN));
3340
0
        if (mHasMappedToplevel != mapped) {
3341
0
            SetHasMappedToplevel(mapped);
3342
0
        }
3343
0
        return;
3344
0
    }
3345
0
    // else the widget is a shell widget.
3346
0
3347
0
    // The block below is a bit evil.
3348
0
    //
3349
0
    // When a window is resized before it is shown, gtk_window_resize() delays
3350
0
    // resizes until the window is shown.  If gtk_window_state_event() sees a
3351
0
    // GDK_WINDOW_STATE_MAXIMIZED change [1] before the window is shown, then
3352
0
    // gtk_window_compute_configure_request_size() ignores the values from the
3353
0
    // resize [2].  See bug 1449166 for an example of how this could happen.
3354
0
    //
3355
0
    // [1] https://gitlab.gnome.org/GNOME/gtk/blob/3.22.30/gtk/gtkwindow.c#L7967
3356
0
    // [2] https://gitlab.gnome.org/GNOME/gtk/blob/3.22.30/gtk/gtkwindow.c#L9377
3357
0
    //
3358
0
    // In order to provide a sensible size for the window when the user exits
3359
0
    // maximized state, we hide the GDK_WINDOW_STATE_MAXIMIZED change from
3360
0
    // gtk_window_state_event() so as to trick GTK into using the values from
3361
0
    // gtk_window_resize() in its configure request.
3362
0
    //
3363
0
    // We instead notify gtk_window_state_event() of the maximized state change
3364
0
    // once the window is shown.
3365
0
    if (!mIsShown) {
3366
0
        aEvent->changed_mask = static_cast<GdkWindowState>
3367
0
            (aEvent->changed_mask & ~GDK_WINDOW_STATE_MAXIMIZED);
3368
0
    } else if (aEvent->changed_mask & GDK_WINDOW_STATE_WITHDRAWN &&
3369
0
               aEvent->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) {
3370
0
        aEvent->changed_mask = static_cast<GdkWindowState>
3371
0
            (aEvent->changed_mask | GDK_WINDOW_STATE_MAXIMIZED);
3372
0
    }
3373
0
3374
0
    // We don't care about anything but changes in the maximized/icon/fullscreen
3375
0
    // states
3376
0
    if ((aEvent->changed_mask
3377
0
         & (GDK_WINDOW_STATE_ICONIFIED |
3378
0
            GDK_WINDOW_STATE_MAXIMIZED |
3379
0
            GDK_WINDOW_STATE_FULLSCREEN)) == 0) {
3380
0
        return;
3381
0
    }
3382
0
3383
0
    if (aEvent->new_window_state & GDK_WINDOW_STATE_ICONIFIED) {
3384
0
        LOG(("\tIconified\n"));
3385
0
        mSizeState = nsSizeMode_Minimized;
3386
0
#ifdef ACCESSIBILITY
3387
0
        DispatchMinimizeEventAccessible();
3388
0
#endif //ACCESSIBILITY
3389
0
    }
3390
0
    else if (aEvent->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) {
3391
0
        LOG(("\tFullscreen\n"));
3392
0
        mSizeState = nsSizeMode_Fullscreen;
3393
0
    }
3394
0
    else if (aEvent->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) {
3395
0
        LOG(("\tMaximized\n"));
3396
0
        mSizeState = nsSizeMode_Maximized;
3397
0
#ifdef ACCESSIBILITY
3398
0
        DispatchMaximizeEventAccessible();
3399
0
#endif //ACCESSIBILITY
3400
0
    }
3401
0
    else {
3402
0
        LOG(("\tNormal\n"));
3403
0
        mSizeState = nsSizeMode_Normal;
3404
0
#ifdef ACCESSIBILITY
3405
0
        DispatchRestoreEventAccessible();
3406
0
#endif //ACCESSIBILITY
3407
0
    }
3408
0
3409
0
    if (mWidgetListener) {
3410
0
      mWidgetListener->SizeModeChanged(mSizeState);
3411
0
      if (aEvent->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
3412
0
        mWidgetListener->FullscreenChanged(
3413
0
          aEvent->new_window_state & GDK_WINDOW_STATE_FULLSCREEN);
3414
0
      }
3415
0
    }
3416
0
3417
0
    if (mDrawInTitlebar && mCSDSupportLevel == CSD_SUPPORT_CLIENT) {
3418
0
        UpdateClientOffsetForCSDWindow();
3419
0
    }
3420
0
}
3421
3422
void
3423
nsWindow::ThemeChanged()
3424
0
{
3425
0
    NotifyThemeChanged();
3426
0
3427
0
    if (!mGdkWindow || MOZ_UNLIKELY(mIsDestroyed))
3428
0
        return;
3429
0
3430
0
    // Dispatch theme change notification to all child windows
3431
0
    GList *children =
3432
0
        gdk_window_peek_children(mGdkWindow);
3433
0
    while (children) {
3434
0
        GdkWindow *gdkWin = GDK_WINDOW(children->data);
3435
0
3436
0
        auto *win = (nsWindow*) g_object_get_data(G_OBJECT(gdkWin),
3437
0
                                                      "nsWindow");
3438
0
3439
0
        if (win && win != this) { // guard against infinite recursion
3440
0
            RefPtr<nsWindow> kungFuDeathGrip = win;
3441
0
            win->ThemeChanged();
3442
0
        }
3443
0
3444
0
        children = children->next;
3445
0
    }
3446
0
3447
0
    IMContextWrapper::OnThemeChanged();
3448
0
}
3449
3450
void
3451
nsWindow::OnDPIChanged()
3452
0
{
3453
0
  if (mWidgetListener) {
3454
0
    nsIPresShell* presShell = mWidgetListener->GetPresShell();
3455
0
    if (presShell) {
3456
0
      presShell->BackingScaleFactorChanged();
3457
0
      // Update menu's font size etc
3458
0
      presShell->ThemeChanged();
3459
0
    }
3460
0
    mWidgetListener->UIResolutionChanged();
3461
0
  }
3462
0
}
3463
3464
void
3465
nsWindow::OnCheckResize()
3466
0
{
3467
0
    mPendingConfigures++;
3468
0
}
3469
3470
void
3471
nsWindow::OnCompositedChanged()
3472
0
{
3473
0
  if (mWidgetListener) {
3474
0
    nsIPresShell* presShell = mWidgetListener->GetPresShell();
3475
0
    if (presShell) {
3476
0
      // Update CSD after the change in alpha visibility
3477
0
      presShell->ThemeChanged();
3478
0
    }
3479
0
  }
3480
0
}
3481
3482
void
3483
nsWindow::DispatchDragEvent(EventMessage aMsg, const LayoutDeviceIntPoint& aRefPoint,
3484
                            guint aTime)
3485
0
{
3486
0
    WidgetDragEvent event(true, aMsg, this);
3487
0
3488
0
    InitDragEvent(event);
3489
0
3490
0
    event.mRefPoint = aRefPoint;
3491
0
    event.AssignEventTime(GetWidgetEventTime(aTime));
3492
0
3493
0
    DispatchInputEvent(&event);
3494
0
}
3495
3496
void
3497
nsWindow::OnDragDataReceivedEvent(GtkWidget *aWidget,
3498
                                  GdkDragContext *aDragContext,
3499
                                  gint aX,
3500
                                  gint aY,
3501
                                  GtkSelectionData  *aSelectionData,
3502
                                  guint aInfo,
3503
                                  guint aTime,
3504
                                  gpointer aData)
3505
0
{
3506
0
    LOGDRAG(("nsWindow::OnDragDataReceived(%p)\n", (void*)this));
3507
0
3508
0
    RefPtr<nsDragService> dragService = nsDragService::GetInstance();
3509
0
    dragService->
3510
0
        TargetDataReceived(aWidget, aDragContext, aX, aY,
3511
0
                           aSelectionData, aInfo, aTime);
3512
0
}
3513
3514
nsWindow*
3515
nsWindow::GetTransientForWindowIfPopup()
3516
0
{
3517
0
    if (mWindowType != eWindowType_popup) {
3518
0
        return nullptr;
3519
0
    }
3520
0
    GtkWindow* toplevel = gtk_window_get_transient_for(GTK_WINDOW(mShell));
3521
0
    if (toplevel) {
3522
0
        return get_window_for_gtk_widget(GTK_WIDGET(toplevel));
3523
0
    }
3524
0
    return nullptr;
3525
0
}
3526
3527
bool
3528
nsWindow::IsHandlingTouchSequence(GdkEventSequence* aSequence)
3529
0
{
3530
0
    return mHandleTouchEvent && mTouches.Contains(aSequence);
3531
0
}
3532
3533
#if GTK_CHECK_VERSION(3,4,0)
3534
gboolean
3535
nsWindow::OnTouchEvent(GdkEventTouch* aEvent)
3536
0
{
3537
0
    if (!mHandleTouchEvent) {
3538
0
        // If a popup window was spawned (e.g. as the result of a long-press)
3539
0
        // and touch events got diverted to that window within a touch sequence,
3540
0
        // ensure the touch event gets sent to the original window instead. We
3541
0
        // keep the checks here very conservative so that we only redirect
3542
0
        // events in this specific scenario.
3543
0
        nsWindow* targetWindow = GetTransientForWindowIfPopup();
3544
0
        if (targetWindow &&
3545
0
            targetWindow->IsHandlingTouchSequence(aEvent->sequence)) {
3546
0
            return targetWindow->OnTouchEvent(aEvent);
3547
0
        }
3548
0
3549
0
        return FALSE;
3550
0
    }
3551
0
3552
0
    EventMessage msg;
3553
0
    switch (aEvent->type) {
3554
0
    case GDK_TOUCH_BEGIN:
3555
0
        msg = eTouchStart;
3556
0
        break;
3557
0
    case GDK_TOUCH_UPDATE:
3558
0
        msg = eTouchMove;
3559
0
        break;
3560
0
    case GDK_TOUCH_END:
3561
0
        msg = eTouchEnd;
3562
0
        break;
3563
0
    case GDK_TOUCH_CANCEL:
3564
0
        msg = eTouchCancel;
3565
0
        break;
3566
0
    default:
3567
0
        return FALSE;
3568
0
    }
3569
0
3570
0
    LayoutDeviceIntPoint touchPoint = GetRefPoint(this, aEvent);
3571
0
3572
0
    int32_t id;
3573
0
    RefPtr<dom::Touch> touch;
3574
0
    if (mTouches.Remove(aEvent->sequence, getter_AddRefs(touch))) {
3575
0
        id = touch->mIdentifier;
3576
0
    } else {
3577
0
        id = ++gLastTouchID & 0x7FFFFFFF;
3578
0
    }
3579
0
3580
0
    touch = new dom::Touch(id, touchPoint, LayoutDeviceIntPoint(1, 1),
3581
0
                           0.0f, 0.0f);
3582
0
3583
0
    WidgetTouchEvent event(true, msg, this);
3584
0
    KeymapWrapper::InitInputEvent(event, aEvent->state);
3585
0
    event.mTime = aEvent->time;
3586
0
3587
0
    if (aEvent->type == GDK_TOUCH_BEGIN || aEvent->type == GDK_TOUCH_UPDATE) {
3588
0
        mTouches.Put(aEvent->sequence, touch.forget());
3589
0
        // add all touch points to event object
3590
0
        for (auto iter = mTouches.Iter(); !iter.Done(); iter.Next()) {
3591
0
            event.mTouches.AppendElement(new dom::Touch(*iter.UserData()));
3592
0
        }
3593
0
    } else if (aEvent->type == GDK_TOUCH_END ||
3594
0
               aEvent->type == GDK_TOUCH_CANCEL) {
3595
0
        *event.mTouches.AppendElement() = touch.forget();
3596
0
    }
3597
0
3598
0
    DispatchInputEvent(&event);
3599
0
    return TRUE;
3600
0
}
3601
#endif
3602
3603
static GdkWindow *
3604
CreateGdkWindow(GdkWindow *parent, GtkWidget *widget)
3605
0
{
3606
0
    GdkWindowAttr attributes;
3607
0
    gint          attributes_mask = GDK_WA_VISUAL;
3608
0
3609
0
    attributes.event_mask = kEvents;
3610
0
3611
0
    attributes.width = 1;
3612
0
    attributes.height = 1;
3613
0
    attributes.wclass = GDK_INPUT_OUTPUT;
3614
0
    attributes.visual = gtk_widget_get_visual(widget);
3615
0
    attributes.window_type = GDK_WINDOW_CHILD;
3616
0
3617
0
    GdkWindow *window = gdk_window_new(parent, &attributes, attributes_mask);
3618
0
    gdk_window_set_user_data(window, widget);
3619
0
3620
0
    return window;
3621
0
}
3622
3623
nsresult
3624
nsWindow::Create(nsIWidget* aParent,
3625
                 nsNativeWidget aNativeParent,
3626
                 const LayoutDeviceIntRect& aRect,
3627
                 nsWidgetInitData* aInitData)
3628
0
{
3629
0
    // only set the base parent if we're going to be a dialog or a
3630
0
    // toplevel
3631
0
    nsIWidget *baseParent = aInitData &&
3632
0
        (aInitData->mWindowType == eWindowType_dialog ||
3633
0
         aInitData->mWindowType == eWindowType_toplevel ||
3634
0
         aInitData->mWindowType == eWindowType_invisible) ?
3635
0
        nullptr : aParent;
3636
0
3637
0
#ifdef ACCESSIBILITY
3638
0
    // Send a DBus message to check whether a11y is enabled
3639
0
    a11y::PreInit();
3640
0
#endif
3641
0
3642
0
    // Ensure that the toolkit is created.
3643
0
    nsGTKToolkit::GetToolkit();
3644
0
3645
0
    // initialize all the common bits of this class
3646
0
    BaseCreate(baseParent, aInitData);
3647
0
3648
0
    // Do we need to listen for resizes?
3649
0
    bool listenForResizes = false;;
3650
0
    if (aNativeParent || (aInitData && aInitData->mListenForResizes))
3651
0
        listenForResizes = true;
3652
0
3653
0
    // and do our common creation
3654
0
    CommonCreate(aParent, listenForResizes);
3655
0
3656
0
    // save our bounds
3657
0
    mBounds = aRect;
3658
0
    ConstrainSize(&mBounds.width, &mBounds.height);
3659
0
3660
0
    // figure out our parent window
3661
0
    GtkWidget      *parentMozContainer = nullptr;
3662
0
    GtkContainer   *parentGtkContainer = nullptr;
3663
0
    GdkWindow      *parentGdkWindow = nullptr;
3664
0
    GtkWindow      *topLevelParent = nullptr;
3665
0
    nsWindow       *parentnsWindow = nullptr;
3666
0
    GtkWidget      *eventWidget = nullptr;
3667
0
    bool            drawToContainer = false;
3668
0
    bool            needsAlphaVisual = (mWindowType == eWindowType_popup &&
3669
0
                                       aInitData->mSupportTranslucency);
3670
0
3671
0
    // Some Gtk+ themes use non-rectangular toplevel windows. To fully support
3672
0
    // such themes we need to make toplevel window transparent with ARGB visual.
3673
0
    // It may cause performanance issue so make it configurable
3674
0
    // and enable it by default for selected window managers.
3675
0
    if (mWindowType == eWindowType_toplevel) {
3676
0
        needsAlphaVisual = TopLevelWindowUseARGBVisual();
3677
0
    }
3678
0
3679
0
    if (aParent) {
3680
0
        parentnsWindow = static_cast<nsWindow*>(aParent);
3681
0
        parentGdkWindow = parentnsWindow->mGdkWindow;
3682
0
    } else if (aNativeParent && GDK_IS_WINDOW(aNativeParent)) {
3683
0
        parentGdkWindow = GDK_WINDOW(aNativeParent);
3684
0
        parentnsWindow = get_window_for_gdk_window(parentGdkWindow);
3685
0
        if (!parentnsWindow)
3686
0
            return NS_ERROR_FAILURE;
3687
0
3688
0
    } else if (aNativeParent && GTK_IS_CONTAINER(aNativeParent)) {
3689
0
        parentGtkContainer = GTK_CONTAINER(aNativeParent);
3690
0
    }
3691
0
3692
0
    if (parentGdkWindow) {
3693
0
        // get the widget for the window - it should be a moz container
3694
0
        parentMozContainer = parentnsWindow->GetMozContainerWidget();
3695
0
        if (!parentMozContainer)
3696
0
            return NS_ERROR_FAILURE;
3697
0
3698
0
        // get the toplevel window just in case someone needs to use it
3699
0
        // for setting transients or whatever.
3700
0
        topLevelParent =
3701
0
            GTK_WINDOW(gtk_widget_get_toplevel(parentMozContainer));
3702
0
    }
3703
0
3704
0
    // ok, create our windows
3705
0
    switch (mWindowType) {
3706
0
    case eWindowType_dialog:
3707
0
    case eWindowType_popup:
3708
0
    case eWindowType_toplevel:
3709
0
    case eWindowType_invisible: {
3710
0
        mIsTopLevel = true;
3711
0
3712
0
        // Popups that are not noautohide are only temporary. The are used
3713
0
        // for menus and the like and disappear when another window is used.
3714
0
        // For most popups, use the standard GtkWindowType GTK_WINDOW_POPUP,
3715
0
        // which will use a Window with the override-redirect attribute
3716
0
        // (for temporary windows).
3717
0
        // For long-lived windows, their stacking order is managed by the
3718
0
        // window manager, as indicated by GTK_WINDOW_TOPLEVEL.
3719
0
        // For Wayland we have to always use GTK_WINDOW_POPUP to control
3720
0
        // popup window position.
3721
0
        GtkWindowType type = GTK_WINDOW_TOPLEVEL;
3722
0
        if (mWindowType == eWindowType_popup) {
3723
0
            type = (mIsX11Display && aInitData->mNoAutoHide) ?
3724
0
                GTK_WINDOW_TOPLEVEL : GTK_WINDOW_POPUP;
3725
0
        }
3726
0
        mShell = gtk_window_new(type);
3727
0
3728
0
#ifdef MOZ_X11
3729
0
        // Ensure gfxPlatform is initialized, since that is what initializes
3730
0
        // gfxVars, used below.
3731
0
        Unused << gfxPlatform::GetPlatform();
3732
0
3733
0
        bool useWebRender = gfx::gfxVars::UseWebRender() &&
3734
0
            AllowWebRenderForThisWindow();
3735
0
3736
0
        // If using WebRender on X11, we need to select a visual with a depth buffer,
3737
0
        // as well as an alpha channel if transparency is requested. This must be done
3738
0
        // before the widget is realized.
3739
0
        if (mIsX11Display && useWebRender) {
3740
0
            auto display =
3741
0
                GDK_DISPLAY_XDISPLAY(gtk_widget_get_display(mShell));
3742
0
            auto screen = gtk_widget_get_screen(mShell);
3743
0
            int screenNumber = GDK_SCREEN_XNUMBER(screen);
3744
0
            int visualId = 0;
3745
0
            if (GLContextGLX::FindVisual(display, screenNumber,
3746
0
                                         useWebRender, needsAlphaVisual,
3747
0
                                         &visualId)) {
3748
0
                // If we're using CSD, rendering will go through mContainer, but
3749
0
                // it will inherit this visual as it is a child of mShell.
3750
0
                gtk_widget_set_visual(mShell,
3751
0
                                      gdk_x11_screen_lookup_visual(screen,
3752
0
                                                                   visualId));
3753
0
                mHasAlphaVisual = needsAlphaVisual;
3754
0
            } else {
3755
0
                NS_WARNING("We're missing X11 Visual for WebRender!");
3756
0
            }
3757
0
        } else
3758
0
#endif // MOZ_X11
3759
0
        {
3760
0
            if (needsAlphaVisual) {
3761
0
                GdkScreen *screen = gtk_widget_get_screen(mShell);
3762
0
                if (gdk_screen_is_composited(screen)) {
3763
0
                    GdkVisual *visual = gdk_screen_get_rgba_visual(screen);
3764
0
                    if (visual) {
3765
0
                        gtk_widget_set_visual(mShell, visual);
3766
0
                        mHasAlphaVisual = true;
3767
0
                    }
3768
0
                }
3769
0
            }
3770
0
        }
3771
0
3772
0
        // We have a toplevel window with transparency. Mark it as transparent
3773
0
        // now as nsWindow::SetTransparencyMode() can't be called after
3774
0
        // nsWindow is created (Bug 1344839).
3775
0
        if (mWindowType == eWindowType_toplevel && mHasAlphaVisual) {
3776
0
            mIsTransparent = true;
3777
0
        }
3778
0
3779
0
        // We only move a general managed toplevel window if someone has
3780
0
        // actually placed the window somewhere.  If no placement has taken
3781
0
        // place, we just let the window manager Do The Right Thing.
3782
0
        NativeResize();
3783
0
3784
0
        if (mWindowType == eWindowType_dialog) {
3785
0
            SetDefaultIcon();
3786
0
            gtk_window_set_wmclass(GTK_WINDOW(mShell), "Dialog",
3787
0
                                   gdk_get_program_class());
3788
0
            gtk_window_set_type_hint(GTK_WINDOW(mShell),
3789
0
                                     GDK_WINDOW_TYPE_HINT_DIALOG);
3790
0
            gtk_window_set_transient_for(GTK_WINDOW(mShell),
3791
0
                                         topLevelParent);
3792
0
        }
3793
0
        else if (mWindowType == eWindowType_popup) {
3794
0
            // With popup windows, we want to control their position, so don't
3795
0
            // wait for the window manager to place them (which wouldn't
3796
0
            // happen with override-redirect windows anyway).
3797
0
            NativeMove();
3798
0
3799
0
            gtk_window_set_wmclass(GTK_WINDOW(mShell), "Popup",
3800
0
                                   gdk_get_program_class());
3801
0
3802
0
            if (aInitData->mNoAutoHide) {
3803
0
                // ... but the window manager does not decorate this window,
3804
0
                // nor provide a separate taskbar icon.
3805
0
                if (mBorderStyle == eBorderStyle_default) {
3806
0
                  gtk_window_set_decorated(GTK_WINDOW(mShell), FALSE);
3807
0
                }
3808
0
                else {
3809
0
                  bool decorate = mBorderStyle & eBorderStyle_title;
3810
0
                  gtk_window_set_decorated(GTK_WINDOW(mShell), decorate);
3811
0
                  if (decorate) {
3812
0
                    gtk_window_set_deletable(GTK_WINDOW(mShell), mBorderStyle & eBorderStyle_close);
3813
0
                  }
3814
0
                }
3815
0
                gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mShell), TRUE);
3816
0
                // Element focus is managed by the parent window so the
3817
0
                // WM_HINTS input field is set to False to tell the window
3818
0
                // manager not to set input focus to this window ...
3819
0
                gtk_window_set_accept_focus(GTK_WINDOW(mShell), FALSE);
3820
0
#ifdef MOZ_X11
3821
0
                // ... but when the window manager offers focus through
3822
0
                // WM_TAKE_FOCUS, focus is requested on the parent window.
3823
0
                if (mIsX11Display) {
3824
0
                    gtk_widget_realize(mShell);
3825
0
                    gdk_window_add_filter(gtk_widget_get_window(mShell),
3826
0
                                          popup_take_focus_filter, nullptr);
3827
0
                }
3828
0
#endif
3829
0
            }
3830
0
3831
0
            GdkWindowTypeHint gtkTypeHint;
3832
0
            if (aInitData->mIsDragPopup) {
3833
0
                gtkTypeHint = GDK_WINDOW_TYPE_HINT_DND;
3834
0
                mIsDragPopup = true;
3835
0
            }
3836
0
            else {
3837
0
                switch (aInitData->mPopupHint) {
3838
0
                    case ePopupTypeMenu:
3839
0
                        // Use GDK_WINDOW_TYPE_HINT_UTILITY on Wayland which
3840
0
                        // guides Gtk to create the popup as subsurface
3841
0
                        // instead of xdg_shell popup (see Bug 1423598).
3842
0
                        gtkTypeHint = mIsX11Display ? GDK_WINDOW_TYPE_HINT_POPUP_MENU :
3843
0
                                                      GDK_WINDOW_TYPE_HINT_UTILITY;
3844
0
                        break;
3845
0
                    case ePopupTypeTooltip:
3846
0
                        gtkTypeHint = GDK_WINDOW_TYPE_HINT_TOOLTIP;
3847
0
                        break;
3848
0
                    default:
3849
0
                        gtkTypeHint = GDK_WINDOW_TYPE_HINT_UTILITY;
3850
0
                        break;
3851
0
                }
3852
0
            }
3853
0
            gtk_window_set_type_hint(GTK_WINDOW(mShell), gtkTypeHint);
3854
0
3855
0
            if (topLevelParent) {
3856
0
                gtk_window_set_transient_for(GTK_WINDOW(mShell),
3857
0
                                            topLevelParent);
3858
0
            }
3859
0
        }
3860
0
        else { // must be eWindowType_toplevel
3861
0
            SetDefaultIcon();
3862
0
            gtk_window_set_wmclass(GTK_WINDOW(mShell), "Toplevel",
3863
0
                                   gdk_get_program_class());
3864
0
3865
0
            // each toplevel window gets its own window group
3866
0
            GtkWindowGroup *group = gtk_window_group_new();
3867
0
            gtk_window_group_add_window(group, GTK_WINDOW(mShell));
3868
0
            g_object_unref(group);
3869
0
3870
0
            // We enable titlebar rendering for toplevel windows only.
3871
0
            mCSDSupportLevel = GetSystemCSDSupportLevel();
3872
0
        }
3873
0
3874
0
        // Create a container to hold child windows and child GtkWidgets.
3875
0
        GtkWidget *container = moz_container_new();
3876
0
        mContainer = MOZ_CONTAINER(container);
3877
0
3878
0
        // "csd" style is set when widget is realized so we need to call
3879
0
        // it explicitly now.
3880
0
        gtk_widget_realize(mShell);
3881
0
3882
0
        /* There are several cases here:
3883
0
         *
3884
0
         * 1) We're running on Gtk+ without client side decorations.
3885
0
         *    Content is rendered to mShell window and we listen
3886
0
         *    to the Gtk+ events on mShell
3887
0
         * 2) We're running on Gtk+ and client side decorations
3888
0
         *    are drawn by Gtk+ to mShell. Content is rendered to mContainer
3889
0
         *    and we listen to the Gtk+ events on mContainer.
3890
0
         * 3) We're running on Wayland. All gecko content is rendered
3891
0
         *    to mContainer and we listen to the Gtk+ events on mContainer.
3892
0
         */
3893
0
        GtkStyleContext* style = gtk_widget_get_style_context(mShell);
3894
0
        drawToContainer =
3895
0
            !mIsX11Display ||
3896
0
            (mCSDSupportLevel == CSD_SUPPORT_CLIENT) ||
3897
0
            gtk_style_context_has_class(style, "csd");
3898
0
        eventWidget = (drawToContainer) ? container : mShell;
3899
0
3900
0
        gtk_widget_add_events(eventWidget, kEvents);
3901
0
        if (drawToContainer)
3902
0
            gtk_widget_add_events(mShell, GDK_PROPERTY_CHANGE_MASK);
3903
0
3904
0
        // Prevent GtkWindow from painting a background to avoid flickering.
3905
0
        gtk_widget_set_app_paintable(eventWidget, TRUE);
3906
0
3907
0
        // If we draw to mContainer window then configure it now because
3908
0
        // gtk_container_add() realizes the child widget.
3909
0
        gtk_widget_set_has_window(container, drawToContainer);
3910
0
3911
0
        gtk_container_add(GTK_CONTAINER(mShell), container);
3912
0
        gtk_widget_realize(container);
3913
0
3914
0
        // make sure this is the focus widget in the container
3915
0
        gtk_widget_show(container);
3916
0
        gtk_widget_grab_focus(container);
3917
0
3918
0
        // the drawing window
3919
0
        mGdkWindow = gtk_widget_get_window(eventWidget);
3920
0
3921
0
        if (mWindowType == eWindowType_popup) {
3922
0
            // gdk does not automatically set the cursor for "temporary"
3923
0
            // windows, which are what gtk uses for popups.
3924
0
3925
0
            mCursor = eCursor_wait; // force SetCursor to actually set the
3926
0
                                    // cursor, even though our internal state
3927
0
                                    // indicates that we already have the
3928
0
                                    // standard cursor.
3929
0
            SetCursor(eCursor_standard);
3930
0
3931
0
            if (aInitData->mNoAutoHide) {
3932
0
                gint wmd = ConvertBorderStyles(mBorderStyle);
3933
0
                if (wmd != -1)
3934
0
                  gdk_window_set_decorations(mGdkWindow, (GdkWMDecoration) wmd);
3935
0
            }
3936
0
3937
0
            if (!mIsX11Display) {
3938
0
                gtk_widget_set_app_paintable(mShell, TRUE);
3939
0
            }
3940
0
3941
0
            // If the popup ignores mouse events, set an empty input shape.
3942
0
            if (aInitData->mMouseTransparent) {
3943
0
              cairo_rectangle_int_t rect = { 0, 0, 0, 0 };
3944
0
              cairo_region_t *region = cairo_region_create_rectangle(&rect);
3945
0
3946
0
              gdk_window_input_shape_combine_region(mGdkWindow, region, 0, 0);
3947
0
              cairo_region_destroy(region);
3948
0
            }
3949
0
        }
3950
0
3951
0
#ifdef MOZ_X11
3952
0
        // Set window manager hint to keep fullscreen windows composited.
3953
0
        //
3954
0
        // If the window were to get unredirected, there could be visible
3955
0
        // tearing because Gecko does not align its framebuffer updates with
3956
0
        // vblank.
3957
0
        SetCompositorHint(GTK_WIDGET_COMPOSIDED_ENABLED);
3958
0
#endif
3959
0
    }
3960
0
        break;
3961
0
3962
0
    case eWindowType_plugin:
3963
0
    case eWindowType_plugin_ipc_chrome:
3964
0
    case eWindowType_plugin_ipc_content:
3965
0
        MOZ_ASSERT_UNREACHABLE("Unexpected eWindowType_plugin*");
3966
0
        return NS_ERROR_FAILURE;
3967
0
3968
0
    case eWindowType_child: {
3969
0
        if (parentMozContainer) {
3970
0
            mGdkWindow = CreateGdkWindow(parentGdkWindow, parentMozContainer);
3971
0
            mHasMappedToplevel = parentnsWindow->mHasMappedToplevel;
3972
0
        }
3973
0
        else if (parentGtkContainer) {
3974
0
            // This MozContainer has its own window for drawing and receives
3975
0
            // events because there is no mShell widget (corresponding to this
3976
0
            // nsWindow).
3977
0
            GtkWidget *container = moz_container_new();
3978
0
            mContainer = MOZ_CONTAINER(container);
3979
0
            eventWidget = container;
3980
0
            gtk_widget_add_events(eventWidget, kEvents);
3981
0
            gtk_container_add(parentGtkContainer, container);
3982
0
            gtk_widget_realize(container);
3983
0
3984
0
            mGdkWindow = gtk_widget_get_window(container);
3985
0
        }
3986
0
        else {
3987
0
            NS_WARNING("Warning: tried to create a new child widget with no parent!");
3988
0
            return NS_ERROR_FAILURE;
3989
0
        }
3990
0
    }
3991
0
        break;
3992
0
    default:
3993
0
        break;
3994
0
    }
3995
0
3996
0
    // label the drawing window with this object so we can find our way home
3997
0
    g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", this);
3998
0
    if (drawToContainer) {
3999
0
        // Also label mShell toplevel window,
4000
0
        // property_notify_event_cb callback also needs to find its way home
4001
0
        g_object_set_data(G_OBJECT(gtk_widget_get_window(mShell)),
4002
0
                          "nsWindow", this);
4003
0
    }
4004
0
4005
0
    if (mContainer)
4006
0
        g_object_set_data(G_OBJECT(mContainer), "nsWindow", this);
4007
0
4008
0
    if (mShell)
4009
0
        g_object_set_data(G_OBJECT(mShell), "nsWindow", this);
4010
0
4011
0
    // attach listeners for events
4012
0
    if (mShell) {
4013
0
        g_signal_connect(mShell, "configure_event",
4014
0
                         G_CALLBACK(configure_event_cb), nullptr);
4015
0
        g_signal_connect(mShell, "delete_event",
4016
0
                         G_CALLBACK(delete_event_cb), nullptr);
4017
0
        g_signal_connect(mShell, "window_state_event",
4018
0
                         G_CALLBACK(window_state_event_cb), nullptr);
4019
0
        g_signal_connect(mShell, "check-resize",
4020
0
                         G_CALLBACK(check_resize_cb), nullptr);
4021
0
        g_signal_connect(mShell, "composited-changed",
4022
0
                         G_CALLBACK(widget_composited_changed_cb), nullptr);
4023
0
        g_signal_connect(mShell, "property-notify-event",
4024
0
                         G_CALLBACK(property_notify_event_cb), nullptr);
4025
0
4026
0
        GdkScreen *screen = gtk_widget_get_screen(mShell);
4027
0
        if (!g_signal_handler_find(screen, G_SIGNAL_MATCH_FUNC,
4028
0
                                   0, 0, nullptr,
4029
0
                                   FuncToGpointer(screen_composited_changed_cb), 0)) {
4030
0
            g_signal_connect(screen, "composited-changed",
4031
0
                             G_CALLBACK(screen_composited_changed_cb), nullptr);
4032
0
        }
4033
0
4034
0
        GtkSettings* default_settings = gtk_settings_get_default();
4035
0
        g_signal_connect_after(default_settings,
4036
0
                               "notify::gtk-theme-name",
4037
0
                               G_CALLBACK(settings_changed_cb), this);
4038
0
        g_signal_connect_after(default_settings,
4039
0
                               "notify::gtk-font-name",
4040
0
                               G_CALLBACK(settings_changed_cb), this);
4041
0
        g_signal_connect_after(default_settings,
4042
0
                               "notify::gtk-enable-animations",
4043
0
                               G_CALLBACK(settings_changed_cb), this);
4044
0
    }
4045
0
4046
0
    if (mContainer) {
4047
0
        // Widget signals
4048
0
        g_signal_connect(mContainer, "unrealize",
4049
0
                         G_CALLBACK(container_unrealize_cb), nullptr);
4050
0
        g_signal_connect_after(mContainer, "size_allocate",
4051
0
                               G_CALLBACK(size_allocate_cb), nullptr);
4052
0
        g_signal_connect(mContainer, "hierarchy-changed",
4053
0
                         G_CALLBACK(hierarchy_changed_cb), nullptr);
4054
0
        g_signal_connect(mContainer, "notify::scale-factor",
4055
0
                         G_CALLBACK(scale_changed_cb), nullptr);
4056
0
        // Initialize mHasMappedToplevel.
4057
0
        hierarchy_changed_cb(GTK_WIDGET(mContainer), nullptr);
4058
0
        // Expose, focus, key, and drag events are sent even to GTK_NO_WINDOW
4059
0
        // widgets.
4060
0
        g_signal_connect(G_OBJECT(mContainer), "draw",
4061
0
                         G_CALLBACK(expose_event_cb), nullptr);
4062
0
        g_signal_connect(mContainer, "focus_in_event",
4063
0
                         G_CALLBACK(focus_in_event_cb), nullptr);
4064
0
        g_signal_connect(mContainer, "focus_out_event",
4065
0
                         G_CALLBACK(focus_out_event_cb), nullptr);
4066
0
        g_signal_connect(mContainer, "key_press_event",
4067
0
                         G_CALLBACK(key_press_event_cb), nullptr);
4068
0
        g_signal_connect(mContainer, "key_release_event",
4069
0
                         G_CALLBACK(key_release_event_cb), nullptr);
4070
0
4071
0
        gtk_drag_dest_set((GtkWidget *)mContainer,
4072
0
                          (GtkDestDefaults)0,
4073
0
                          nullptr,
4074
0
                          0,
4075
0
                          (GdkDragAction)0);
4076
0
4077
0
        g_signal_connect(mContainer, "drag_motion",
4078
0
                         G_CALLBACK(drag_motion_event_cb), nullptr);
4079
0
        g_signal_connect(mContainer, "drag_leave",
4080
0
                         G_CALLBACK(drag_leave_event_cb), nullptr);
4081
0
        g_signal_connect(mContainer, "drag_drop",
4082
0
                         G_CALLBACK(drag_drop_event_cb), nullptr);
4083
0
        g_signal_connect(mContainer, "drag_data_received",
4084
0
                         G_CALLBACK(drag_data_received_event_cb), nullptr);
4085
0
4086
0
        GtkWidget *widgets[] = { GTK_WIDGET(mContainer),
4087
0
                                 !drawToContainer ? mShell : nullptr };
4088
0
        for (size_t i = 0; i < ArrayLength(widgets) && widgets[i]; ++i) {
4089
0
            // Visibility events are sent to the owning widget of the relevant
4090
0
            // window but do not propagate to parent widgets so connect on
4091
0
            // mShell (if it exists) as well as mContainer.
4092
0
            g_signal_connect(widgets[i], "visibility-notify-event",
4093
0
                             G_CALLBACK(visibility_notify_event_cb), nullptr);
4094
0
            // Similarly double buffering is controlled by the window's owning
4095
0
            // widget.  Disable double buffering for painting directly to the
4096
0
            // X Window.
4097
0
            gtk_widget_set_double_buffered(widgets[i], FALSE);
4098
0
        }
4099
0
4100
0
        // We create input contexts for all containers, except for
4101
0
        // toplevel popup windows
4102
0
        if (mWindowType != eWindowType_popup) {
4103
0
            mIMContext = new IMContextWrapper(this);
4104
0
        }
4105
0
    } else if (!mIMContext) {
4106
0
        nsWindow *container = GetContainerWindow();
4107
0
        if (container) {
4108
0
            mIMContext = container->mIMContext;
4109
0
        }
4110
0
    }
4111
0
4112
0
    if (eventWidget) {
4113
0
4114
0
        // These events are sent to the owning widget of the relevant window
4115
0
        // and propagate up to the first widget that handles the events, so we
4116
0
        // need only connect on mShell, if it exists, to catch events on its
4117
0
        // window and windows of mContainer.
4118
0
        g_signal_connect(eventWidget, "enter-notify-event",
4119
0
                         G_CALLBACK(enter_notify_event_cb), nullptr);
4120
0
        g_signal_connect(eventWidget, "leave-notify-event",
4121
0
                         G_CALLBACK(leave_notify_event_cb), nullptr);
4122
0
        g_signal_connect(eventWidget, "motion-notify-event",
4123
0
                         G_CALLBACK(motion_notify_event_cb), nullptr);
4124
0
        g_signal_connect(eventWidget, "button-press-event",
4125
0
                         G_CALLBACK(button_press_event_cb), nullptr);
4126
0
        g_signal_connect(eventWidget, "button-release-event",
4127
0
                         G_CALLBACK(button_release_event_cb), nullptr);
4128
0
        g_signal_connect(eventWidget, "scroll-event",
4129
0
                         G_CALLBACK(scroll_event_cb), nullptr);
4130
0
#if GTK_CHECK_VERSION(3,4,0)
4131
0
        g_signal_connect(eventWidget, "touch-event",
4132
0
                         G_CALLBACK(touch_event_cb), nullptr);
4133
0
#endif
4134
0
    }
4135
0
4136
0
    LOG(("nsWindow [%p]\n", (void *)this));
4137
0
    if (mShell) {
4138
0
        LOG(("\tmShell %p mContainer %p mGdkWindow %p 0x%lx\n",
4139
0
             mShell, mContainer, mGdkWindow,
4140
0
             mIsX11Display ? gdk_x11_window_get_xid(mGdkWindow) : 0));
4141
0
    } else if (mContainer) {
4142
0
        LOG(("\tmContainer %p mGdkWindow %p\n", mContainer, mGdkWindow));
4143
0
    }
4144
0
    else if (mGdkWindow) {
4145
0
        LOG(("\tmGdkWindow %p parent %p\n",
4146
0
             mGdkWindow, gdk_window_get_parent(mGdkWindow)));
4147
0
    }
4148
0
4149
0
    // resize so that everything is set to the right dimensions
4150
0
    if (!mIsTopLevel)
4151
0
        Resize(mBounds.x, mBounds.y, mBounds.width, mBounds.height, false);
4152
0
4153
0
#ifdef MOZ_X11
4154
0
    if (mIsX11Display && mGdkWindow) {
4155
0
      mXDisplay = GDK_WINDOW_XDISPLAY(mGdkWindow);
4156
0
      mXWindow = gdk_x11_window_get_xid(mGdkWindow);
4157
0
4158
0
      GdkVisual* gdkVisual = gdk_window_get_visual(mGdkWindow);
4159
0
      mXVisual = gdk_x11_visual_get_xvisual(gdkVisual);
4160
0
      mXDepth = gdk_visual_get_depth(gdkVisual);
4161
0
      bool shaped = needsAlphaVisual && !mHasAlphaVisual;
4162
0
4163
0
      mSurfaceProvider.Initialize(mXDisplay, mXWindow, mXVisual, mXDepth,
4164
0
                                  shaped);
4165
0
    }
4166
#ifdef MOZ_WAYLAND
4167
    else if (!mIsX11Display) {
4168
      mSurfaceProvider.Initialize(this);
4169
    }
4170
#endif
4171
#endif
4172
0
    return NS_OK;
4173
0
}
4174
4175
void
4176
nsWindow::RefreshWindowClass(void)
4177
0
{
4178
0
    if (mGtkWindowTypeName.IsEmpty() || mGtkWindowRoleName.IsEmpty())
4179
0
        return;
4180
0
4181
0
    GdkWindow* gdkWindow = gtk_widget_get_window(mShell);
4182
0
    gdk_window_set_role(gdkWindow, mGtkWindowRoleName.get());
4183
0
4184
0
#ifdef MOZ_X11
4185
0
    if (mIsX11Display) {
4186
0
        XClassHint *class_hint = XAllocClassHint();
4187
0
        if (!class_hint) {
4188
0
          return;
4189
0
        }
4190
0
        const char *res_class = gdk_get_program_class();
4191
0
        if (!res_class)
4192
0
          return;
4193
0
4194
0
        class_hint->res_name = const_cast<char*>(mGtkWindowTypeName.get());
4195
0
        class_hint->res_class = const_cast<char*>(res_class);
4196
0
4197
0
        // Can't use gtk_window_set_wmclass() for this; it prints
4198
0
        // a warning & refuses to make the change.
4199
0
        GdkDisplay *display = gdk_display_get_default();
4200
0
        XSetClassHint(GDK_DISPLAY_XDISPLAY(display),
4201
0
                      gdk_x11_window_get_xid(gdkWindow),
4202
0
                      class_hint);
4203
0
        XFree(class_hint);
4204
0
    }
4205
0
#endif /* MOZ_X11 */
4206
0
}
4207
4208
void
4209
nsWindow::SetWindowClass(const nsAString &xulWinType)
4210
0
{
4211
0
    if (!mShell)
4212
0
      return;
4213
0
4214
0
    char *res_name = ToNewCString(xulWinType);
4215
0
    if (!res_name)
4216
0
      return;
4217
0
4218
0
    const char *role = nullptr;
4219
0
4220
0
    // Parse res_name into a name and role. Characters other than
4221
0
    // [A-Za-z0-9_-] are converted to '_'. Anything after the first
4222
0
    // colon is assigned to role; if there's no colon, assign the
4223
0
    // whole thing to both role and res_name.
4224
0
    for (char *c = res_name; *c; c++) {
4225
0
      if (':' == *c) {
4226
0
        *c = 0;
4227
0
        role = c + 1;
4228
0
      }
4229
0
      else if (!isascii(*c) || (!isalnum(*c) && ('_' != *c) && ('-' != *c)))
4230
0
        *c = '_';
4231
0
    }
4232
0
    res_name[0] = toupper(res_name[0]);
4233
0
    if (!role) role = res_name;
4234
0
4235
0
    mGtkWindowTypeName = res_name;
4236
0
    mGtkWindowRoleName = role;
4237
0
    free(res_name);
4238
0
4239
0
    RefreshWindowClass();
4240
0
}
4241
4242
void
4243
nsWindow::NativeResize()
4244
0
{
4245
0
    if (!AreBoundsSane()) {
4246
0
        // If someone has set this so that the needs show flag is false
4247
0
        // and it needs to be hidden, update the flag and hide the
4248
0
        // window.  This flag will be cleared the next time someone
4249
0
        // hides the window or shows it.  It also prevents us from
4250
0
        // calling NativeShow(false) excessively on the window which
4251
0
        // causes unneeded X traffic.
4252
0
        if (!mNeedsShow && mIsShown) {
4253
0
            mNeedsShow = true;
4254
0
            NativeShow(false);
4255
0
        }
4256
0
        return;
4257
0
    }
4258
0
4259
0
    GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mBounds.Size());
4260
0
4261
0
    LOG(("nsWindow::NativeResize [%p] %d %d\n", (void *)this,
4262
0
         size.width, size.height));
4263
0
4264
0
    if (mIsTopLevel) {
4265
0
        MOZ_ASSERT(size.width > 0 && size.height > 0,
4266
0
                   "Can't resize window smaller than 1x1.");
4267
0
        gtk_window_resize(GTK_WINDOW(mShell), size.width, size.height);
4268
0
    }
4269
0
    else if (mContainer) {
4270
0
        GtkWidget *widget = GTK_WIDGET(mContainer);
4271
0
        GtkAllocation allocation, prev_allocation;
4272
0
        gtk_widget_get_allocation(widget, &prev_allocation);
4273
0
        allocation.x = prev_allocation.x;
4274
0
        allocation.y = prev_allocation.y;
4275
0
        allocation.width = size.width;
4276
0
        allocation.height = size.height;
4277
0
        gtk_widget_size_allocate(widget, &allocation);
4278
0
    }
4279
0
    else if (mGdkWindow) {
4280
0
        gdk_window_resize(mGdkWindow, size.width, size.height);
4281
0
    }
4282
0
4283
0
#ifdef MOZ_X11
4284
0
    // Notify the GtkCompositorWidget of a ClientSizeChange
4285
0
    // This is different than OnSizeAllocate to catch initial sizing
4286
0
    if (mCompositorWidgetDelegate) {
4287
0
      mCompositorWidgetDelegate->NotifyClientSizeChanged(GetClientSize());
4288
0
    }
4289
0
#endif
4290
0
4291
0
    // Does it need to be shown because bounds were previously insane?
4292
0
    if (mNeedsShow && mIsShown) {
4293
0
        NativeShow(true);
4294
0
    }
4295
0
}
4296
4297
void
4298
nsWindow::NativeMoveResize()
4299
0
{
4300
0
    if (!AreBoundsSane()) {
4301
0
        // If someone has set this so that the needs show flag is false
4302
0
        // and it needs to be hidden, update the flag and hide the
4303
0
        // window.  This flag will be cleared the next time someone
4304
0
        // hides the window or shows it.  It also prevents us from
4305
0
        // calling NativeShow(false) excessively on the window which
4306
0
        // causes unneeded X traffic.
4307
0
        if (!mNeedsShow && mIsShown) {
4308
0
            mNeedsShow = true;
4309
0
            NativeShow(false);
4310
0
        }
4311
0
        NativeMove();
4312
0
4313
0
        return;
4314
0
    }
4315
0
4316
0
    GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mBounds.Size());
4317
0
    GdkPoint topLeft = DevicePixelsToGdkPointRoundDown(mBounds.TopLeft());
4318
0
4319
0
    LOG(("nsWindow::NativeMoveResize [%p] %d %d %d %d\n", (void *)this,
4320
0
         topLeft.x, topLeft.y, size.width, size.height));
4321
0
4322
0
    if (mIsTopLevel) {
4323
0
        // x and y give the position of the window manager frame top-left.
4324
0
        gtk_window_move(GTK_WINDOW(mShell), topLeft.x, topLeft.y);
4325
0
        // This sets the client window size.
4326
0
        MOZ_ASSERT(size.width > 0 && size.height > 0,
4327
0
                   "Can't resize window smaller than 1x1.");
4328
0
        gtk_window_resize(GTK_WINDOW(mShell), size.width, size.height);
4329
0
    }
4330
0
    else if (mContainer) {
4331
0
        GtkAllocation allocation;
4332
0
        allocation.x = topLeft.x;
4333
0
        allocation.y = topLeft.y;
4334
0
        allocation.width = size.width;
4335
0
        allocation.height = size.height;
4336
0
        gtk_widget_size_allocate(GTK_WIDGET(mContainer), &allocation);
4337
0
    }
4338
0
    else if (mGdkWindow) {
4339
0
        gdk_window_move_resize(mGdkWindow,
4340
0
                               topLeft.x, topLeft.y, size.width, size.height);
4341
0
    }
4342
0
4343
0
#ifdef MOZ_X11
4344
0
    // Notify the GtkCompositorWidget of a ClientSizeChange
4345
0
    // This is different than OnSizeAllocate to catch initial sizing
4346
0
    if (mCompositorWidgetDelegate) {
4347
0
      mCompositorWidgetDelegate->NotifyClientSizeChanged(GetClientSize());
4348
0
    }
4349
0
#endif
4350
0
4351
0
    // Does it need to be shown because bounds were previously insane?
4352
0
    if (mNeedsShow && mIsShown) {
4353
0
        NativeShow(true);
4354
0
    }
4355
0
}
4356
4357
void
4358
nsWindow::NativeShow(bool aAction)
4359
0
{
4360
0
    if (aAction) {
4361
0
        // unset our flag now that our window has been shown
4362
0
        mNeedsShow = false;
4363
0
4364
0
        if (mIsTopLevel) {
4365
0
            // Set up usertime/startupID metadata for the created window.
4366
0
            if (mWindowType != eWindowType_invisible) {
4367
0
                SetUserTimeAndStartupIDForActivatedWindow(mShell);
4368
0
            }
4369
0
4370
0
            gtk_widget_show(mShell);
4371
0
        }
4372
0
        else if (mContainer) {
4373
0
            gtk_widget_show(GTK_WIDGET(mContainer));
4374
0
        }
4375
0
        else if (mGdkWindow) {
4376
0
            gdk_window_show_unraised(mGdkWindow);
4377
0
        }
4378
0
    }
4379
0
    else {
4380
#ifdef MOZ_WAYLAND
4381
        if (mContainer && moz_container_has_wl_egl_window(mContainer)) {
4382
            // Because wl_egl_window is destroyed on moz_container_unmap(),
4383
            // the current compositor cannot use it anymore. To avoid crash,
4384
            // destroy the compositor & recreate a new compositor on next
4385
            // expose event.
4386
            DestroyLayerManager();
4387
        }
4388
#endif
4389
4390
0
        if (mIsTopLevel) {
4391
0
            // Workaround window freezes on GTK versions before 3.21.2 by
4392
0
            // ensuring that configure events get dispatched to windows before
4393
0
            // they are unmapped. See bug 1225044.
4394
0
            if (gtk_check_version(3, 21, 2) != nullptr && mPendingConfigures > 0) {
4395
0
                GtkAllocation allocation;
4396
0
                gtk_widget_get_allocation(GTK_WIDGET(mShell), &allocation);
4397
0
4398
0
                GdkEventConfigure event;
4399
0
                PodZero(&event);
4400
0
                event.type = GDK_CONFIGURE;
4401
0
                event.window = mGdkWindow;
4402
0
                event.send_event = TRUE;
4403
0
                event.x = allocation.x;
4404
0
                event.y = allocation.y;
4405
0
                event.width = allocation.width;
4406
0
                event.height = allocation.height;
4407
0
4408
0
                auto shellClass = GTK_WIDGET_GET_CLASS(mShell);
4409
0
                for (unsigned int i = 0; i < mPendingConfigures; i++) {
4410
0
                    Unused << shellClass->configure_event(mShell, &event);
4411
0
                }
4412
0
                mPendingConfigures = 0;
4413
0
            }
4414
0
4415
0
            gtk_widget_hide(mShell);
4416
0
4417
0
            ClearTransparencyBitmap(); // Release some resources
4418
0
        }
4419
0
        else if (mContainer) {
4420
0
            gtk_widget_hide(GTK_WIDGET(mContainer));
4421
0
        }
4422
0
        else if (mGdkWindow) {
4423
0
            gdk_window_hide(mGdkWindow);
4424
0
        }
4425
0
    }
4426
0
}
4427
4428
void
4429
nsWindow::SetHasMappedToplevel(bool aState)
4430
0
{
4431
0
    // Even when aState == mHasMappedToplevel (as when this method is called
4432
0
    // from Show()), child windows need to have their state checked, so don't
4433
0
    // return early.
4434
0
    bool oldState = mHasMappedToplevel;
4435
0
    mHasMappedToplevel = aState;
4436
0
4437
0
    // mHasMappedToplevel is not updated for children of windows that are
4438
0
    // hidden; GDK knows not to send expose events for these windows.  The
4439
0
    // state is recorded on the hidden window itself, but, for child trees of
4440
0
    // hidden windows, their state essentially becomes disconnected from their
4441
0
    // hidden parent.  When the hidden parent gets shown, the child trees are
4442
0
    // reconnected, and the state of the window being shown can be easily
4443
0
    // propagated.
4444
0
    if (!mIsShown || !mGdkWindow)
4445
0
        return;
4446
0
4447
0
    if (aState && !oldState && !mIsFullyObscured) {
4448
0
        // GDK_EXPOSE events have been ignored but the window is now visible,
4449
0
        // so make sure GDK doesn't think that the window has already been
4450
0
        // painted.
4451
0
        gdk_window_invalidate_rect(mGdkWindow, nullptr, FALSE);
4452
0
4453
0
        // Check that a grab didn't fail due to the window not being
4454
0
        // viewable.
4455
0
        EnsureGrabs();
4456
0
    }
4457
0
4458
0
    for (GList *children = gdk_window_peek_children(mGdkWindow);
4459
0
         children;
4460
0
         children = children->next) {
4461
0
        GdkWindow *gdkWin = GDK_WINDOW(children->data);
4462
0
        nsWindow *child = get_window_for_gdk_window(gdkWin);
4463
0
4464
0
        if (child && child->mHasMappedToplevel != aState) {
4465
0
            child->SetHasMappedToplevel(aState);
4466
0
        }
4467
0
    }
4468
0
}
4469
4470
LayoutDeviceIntSize
4471
nsWindow::GetSafeWindowSize(LayoutDeviceIntSize aSize)
4472
0
{
4473
0
    // The X protocol uses CARD32 for window sizes, but the server (1.11.3)
4474
0
    // reads it as CARD16.  Sizes of pixmaps, used for drawing, are (unsigned)
4475
0
    // CARD16 in the protocol, but the server's ProcCreatePixmap returns
4476
0
    // BadAlloc if dimensions cannot be represented by signed shorts.
4477
0
    LayoutDeviceIntSize result = aSize;
4478
0
    const int32_t kInt16Max = 32767;
4479
0
    if (result.width > kInt16Max) {
4480
0
        result.width = kInt16Max;
4481
0
    }
4482
0
    if (result.height > kInt16Max) {
4483
0
        result.height = kInt16Max;
4484
0
    }
4485
0
    return result;
4486
0
}
4487
4488
void
4489
nsWindow::EnsureGrabs(void)
4490
0
{
4491
0
    if (mRetryPointerGrab)
4492
0
        GrabPointer(sRetryGrabTime);
4493
0
}
4494
4495
void
4496
0
nsWindow::CleanLayerManagerRecursive(void) {
4497
0
    if (mLayerManager) {
4498
0
        mLayerManager->Destroy();
4499
0
        mLayerManager = nullptr;
4500
0
    }
4501
0
4502
0
    DestroyCompositor();
4503
0
4504
0
    GList* children = gdk_window_peek_children(mGdkWindow);
4505
0
    for (GList* list = children; list; list = list->next) {
4506
0
        nsWindow* window = get_window_for_gdk_window(GDK_WINDOW(list->data));
4507
0
        if (window) {
4508
0
            window->CleanLayerManagerRecursive();
4509
0
        }
4510
0
    }
4511
0
}
4512
4513
void
4514
nsWindow::SetTransparencyMode(nsTransparencyMode aMode)
4515
0
{
4516
0
    if (!mShell) {
4517
0
        // Pass the request to the toplevel window
4518
0
        GtkWidget *topWidget = GetToplevelWidget();
4519
0
        if (!topWidget)
4520
0
            return;
4521
0
4522
0
        nsWindow *topWindow = get_window_for_gtk_widget(topWidget);
4523
0
        if (!topWindow)
4524
0
            return;
4525
0
4526
0
        topWindow->SetTransparencyMode(aMode);
4527
0
        return;
4528
0
    }
4529
0
4530
0
    bool isTransparent = aMode == eTransparencyTransparent;
4531
0
4532
0
    if (mIsTransparent == isTransparent) {
4533
0
        return;
4534
0
    } else if (mWindowType != eWindowType_popup) {
4535
0
        NS_WARNING("Cannot set transparency mode on non-popup windows.");
4536
0
        return;
4537
0
    }
4538
0
4539
0
    if (!isTransparent) {
4540
0
        ClearTransparencyBitmap();
4541
0
    } // else the new default alpha values are "all 1", so we don't
4542
0
    // need to change anything yet
4543
0
4544
0
    mIsTransparent = isTransparent;
4545
0
4546
0
    // Need to clean our LayerManager up while still alive because
4547
0
    // we don't want to use layers acceleration on shaped windows
4548
0
    CleanLayerManagerRecursive();
4549
0
}
4550
4551
nsTransparencyMode
4552
nsWindow::GetTransparencyMode()
4553
0
{
4554
0
    if (!mShell) {
4555
0
        // Pass the request to the toplevel window
4556
0
        GtkWidget *topWidget = GetToplevelWidget();
4557
0
        if (!topWidget) {
4558
0
            return eTransparencyOpaque;
4559
0
        }
4560
0
4561
0
        nsWindow *topWindow = get_window_for_gtk_widget(topWidget);
4562
0
        if (!topWindow) {
4563
0
            return eTransparencyOpaque;
4564
0
        }
4565
0
4566
0
        return topWindow->GetTransparencyMode();
4567
0
    }
4568
0
4569
0
    return mIsTransparent ? eTransparencyTransparent : eTransparencyOpaque;
4570
0
}
4571
4572
// For setting the draggable titlebar region from CSS
4573
// with -moz-window-dragging: drag.
4574
void
4575
nsWindow::UpdateWindowDraggingRegion(const LayoutDeviceIntRegion& aRegion)
4576
0
{
4577
0
  if (mDraggableRegion != aRegion) {
4578
0
    mDraggableRegion = aRegion;
4579
0
  }
4580
0
}
4581
4582
#if (MOZ_WIDGET_GTK >= 3)
4583
void nsWindow::UpdateOpaqueRegion(const LayoutDeviceIntRegion& aOpaqueRegion)
4584
0
{
4585
0
    // Available as of GTK 3.10+
4586
0
    static auto sGdkWindowSetOpaqueRegion =
4587
0
        (void (*)(GdkWindow*, cairo_region_t*))
4588
0
            dlsym(RTLD_DEFAULT, "gdk_window_set_opaque_region");
4589
0
4590
0
    if (sGdkWindowSetOpaqueRegion && mGdkWindow &&
4591
0
        gdk_window_get_window_type(mGdkWindow) == GDK_WINDOW_TOPLEVEL) {
4592
0
        if (aOpaqueRegion.IsEmpty()) {
4593
0
            (*sGdkWindowSetOpaqueRegion)(mGdkWindow, nullptr);
4594
0
        } else {
4595
0
            cairo_region_t *region = cairo_region_create();
4596
0
            for (auto iter = aOpaqueRegion.RectIter(); !iter.Done();
4597
0
                 iter.Next()) {
4598
0
                const LayoutDeviceIntRect &r = iter.Get();
4599
0
                cairo_rectangle_int_t rect = { r.x, r.y, r.width, r.height };
4600
0
                cairo_region_union_rectangle(region, &rect);
4601
0
            }
4602
0
            (*sGdkWindowSetOpaqueRegion)(mGdkWindow, region);
4603
0
            cairo_region_destroy(region);
4604
0
        }
4605
0
    }
4606
0
}
4607
#endif
4608
4609
nsresult
4610
nsWindow::ConfigureChildren(const nsTArray<Configuration>& aConfigurations)
4611
0
{
4612
0
    // If this is a remotely updated widget we receive clipping, position, and
4613
0
    // size information from a source other than our owner. Don't let our parent
4614
0
    // update this information.
4615
0
    if (mWindowType == eWindowType_plugin_ipc_chrome) {
4616
0
      return NS_OK;
4617
0
    }
4618
0
4619
0
    for (uint32_t i = 0; i < aConfigurations.Length(); ++i) {
4620
0
        const Configuration& configuration = aConfigurations[i];
4621
0
        auto* w = static_cast<nsWindow*>(configuration.mChild.get());
4622
0
        NS_ASSERTION(w->GetParent() == this,
4623
0
                     "Configured widget is not a child");
4624
0
        w->SetWindowClipRegion(configuration.mClipRegion, true);
4625
0
        if (w->mBounds.Size() != configuration.mBounds.Size()) {
4626
0
            w->Resize(configuration.mBounds.x, configuration.mBounds.y,
4627
0
                      configuration.mBounds.width, configuration.mBounds.height,
4628
0
                      true);
4629
0
        } else if (w->mBounds.TopLeft() != configuration.mBounds.TopLeft()) {
4630
0
            w->Move(configuration.mBounds.x, configuration.mBounds.y);
4631
0
        }
4632
0
        w->SetWindowClipRegion(configuration.mClipRegion, false);
4633
0
    }
4634
0
    return NS_OK;
4635
0
}
4636
4637
nsresult
4638
nsWindow::SetWindowClipRegion(const nsTArray<LayoutDeviceIntRect>& aRects,
4639
                              bool aIntersectWithExisting)
4640
0
{
4641
0
    const nsTArray<LayoutDeviceIntRect>* newRects = &aRects;
4642
0
4643
0
    AutoTArray<LayoutDeviceIntRect,1> intersectRects;
4644
0
    if (aIntersectWithExisting) {
4645
0
        AutoTArray<LayoutDeviceIntRect,1> existingRects;
4646
0
        GetWindowClipRegion(&existingRects);
4647
0
4648
0
        LayoutDeviceIntRegion existingRegion = RegionFromArray(existingRects);
4649
0
        LayoutDeviceIntRegion newRegion = RegionFromArray(aRects);
4650
0
        LayoutDeviceIntRegion intersectRegion;
4651
0
        intersectRegion.And(newRegion, existingRegion);
4652
0
4653
0
        // If mClipRects is null we haven't set a clip rect yet, so we
4654
0
        // need to set the clip even if it is equal.
4655
0
        if (mClipRects && intersectRegion.IsEqual(existingRegion)) {
4656
0
            return NS_OK;
4657
0
        }
4658
0
4659
0
        if (!intersectRegion.IsEqual(newRegion)) {
4660
0
            ArrayFromRegion(intersectRegion, intersectRects);
4661
0
            newRects = &intersectRects;
4662
0
        }
4663
0
    }
4664
0
4665
0
    if (IsWindowClipRegionEqual(*newRects))
4666
0
        return NS_OK;
4667
0
4668
0
    StoreWindowClipRegion(*newRects);
4669
0
4670
0
    if (!mGdkWindow)
4671
0
        return NS_OK;
4672
0
4673
0
    cairo_region_t *region = cairo_region_create();
4674
0
    for (uint32_t i = 0; i < newRects->Length(); ++i) {
4675
0
        const LayoutDeviceIntRect& r = newRects->ElementAt(i);
4676
0
        cairo_rectangle_int_t rect = { r.x, r.y, r.width, r.height };
4677
0
        cairo_region_union_rectangle(region, &rect);
4678
0
    }
4679
0
4680
0
    gdk_window_shape_combine_region(mGdkWindow, region, 0, 0);
4681
0
    cairo_region_destroy(region);
4682
0
4683
0
    return NS_OK;
4684
0
}
4685
4686
void
4687
nsWindow::ResizeTransparencyBitmap()
4688
0
{
4689
0
    if (!mTransparencyBitmap)
4690
0
        return;
4691
0
4692
0
    if (mBounds.width == mTransparencyBitmapWidth &&
4693
0
        mBounds.height == mTransparencyBitmapHeight)
4694
0
        return;
4695
0
4696
0
    int32_t newRowBytes = GetBitmapStride(mBounds.width);
4697
0
    int32_t newSize = newRowBytes * mBounds.height;
4698
0
    auto* newBits = new gchar[newSize];
4699
0
    // fill new mask with "transparent", first
4700
0
    memset(newBits, 0, newSize);
4701
0
4702
0
    // Now copy the intersection of the old and new areas into the new mask
4703
0
    int32_t copyWidth = std::min(mBounds.width, mTransparencyBitmapWidth);
4704
0
    int32_t copyHeight = std::min(mBounds.height, mTransparencyBitmapHeight);
4705
0
    int32_t oldRowBytes = GetBitmapStride(mTransparencyBitmapWidth);
4706
0
    int32_t copyBytes = GetBitmapStride(copyWidth);
4707
0
4708
0
    int32_t i;
4709
0
    gchar* fromPtr = mTransparencyBitmap;
4710
0
    gchar* toPtr = newBits;
4711
0
    for (i = 0; i < copyHeight; i++) {
4712
0
        memcpy(toPtr, fromPtr, copyBytes);
4713
0
        fromPtr += oldRowBytes;
4714
0
        toPtr += newRowBytes;
4715
0
    }
4716
0
4717
0
    delete[] mTransparencyBitmap;
4718
0
    mTransparencyBitmap = newBits;
4719
0
    mTransparencyBitmapWidth = mBounds.width;
4720
0
    mTransparencyBitmapHeight = mBounds.height;
4721
0
}
4722
4723
static bool
4724
ChangedMaskBits(gchar* aMaskBits, int32_t aMaskWidth, int32_t aMaskHeight,
4725
        const nsIntRect& aRect, uint8_t* aAlphas, int32_t aStride)
4726
0
{
4727
0
    int32_t x, y, xMax = aRect.XMost(), yMax = aRect.YMost();
4728
0
    int32_t maskBytesPerRow = GetBitmapStride(aMaskWidth);
4729
0
    for (y = aRect.y; y < yMax; y++) {
4730
0
        gchar* maskBytes = aMaskBits + y*maskBytesPerRow;
4731
0
        uint8_t* alphas = aAlphas;
4732
0
        for (x = aRect.x; x < xMax; x++) {
4733
0
            bool newBit = *alphas > 0x7f;
4734
0
            alphas++;
4735
0
4736
0
            gchar maskByte = maskBytes[x >> 3];
4737
0
            bool maskBit = (maskByte & (1 << (x & 7))) != 0;
4738
0
4739
0
            if (maskBit != newBit) {
4740
0
                return true;
4741
0
            }
4742
0
        }
4743
0
        aAlphas += aStride;
4744
0
    }
4745
0
4746
0
    return false;
4747
0
}
4748
4749
static
4750
void UpdateMaskBits(gchar* aMaskBits, int32_t aMaskWidth, int32_t aMaskHeight,
4751
        const nsIntRect& aRect, uint8_t* aAlphas, int32_t aStride)
4752
0
{
4753
0
    int32_t x, y, xMax = aRect.XMost(), yMax = aRect.YMost();
4754
0
    int32_t maskBytesPerRow = GetBitmapStride(aMaskWidth);
4755
0
    for (y = aRect.y; y < yMax; y++) {
4756
0
        gchar* maskBytes = aMaskBits + y*maskBytesPerRow;
4757
0
        uint8_t* alphas = aAlphas;
4758
0
        for (x = aRect.x; x < xMax; x++) {
4759
0
            bool newBit = *alphas > 0x7f;
4760
0
            alphas++;
4761
0
4762
0
            gchar mask = 1 << (x & 7);
4763
0
            gchar maskByte = maskBytes[x >> 3];
4764
0
            // Note: '-newBit' turns 0 into 00...00 and 1 into 11...11
4765
0
            maskBytes[x >> 3] = (maskByte & ~mask) | (-newBit & mask);
4766
0
        }
4767
0
        aAlphas += aStride;
4768
0
    }
4769
0
}
4770
4771
void
4772
nsWindow::ApplyTransparencyBitmap()
4773
0
{
4774
0
#ifdef MOZ_X11
4775
0
    // We use X11 calls where possible, because GDK handles expose events
4776
0
    // for shaped windows in a way that's incompatible with us (Bug 635903).
4777
0
    // It doesn't occur when the shapes are set through X.
4778
0
    Display* xDisplay = GDK_WINDOW_XDISPLAY(mGdkWindow);
4779
0
    Window xDrawable = GDK_WINDOW_XID(mGdkWindow);
4780
0
    Pixmap maskPixmap = XCreateBitmapFromData(xDisplay,
4781
0
                                              xDrawable,
4782
0
                                              mTransparencyBitmap,
4783
0
                                              mTransparencyBitmapWidth,
4784
0
                                              mTransparencyBitmapHeight);
4785
0
    XShapeCombineMask(xDisplay, xDrawable,
4786
0
                      ShapeBounding, 0, 0,
4787
0
                      maskPixmap, ShapeSet);
4788
0
    XFreePixmap(xDisplay, maskPixmap);
4789
#else
4790
    cairo_surface_t *maskBitmap;
4791
    maskBitmap = cairo_image_surface_create_for_data((unsigned char*)mTransparencyBitmap,
4792
                                                     CAIRO_FORMAT_A1,
4793
                                                     mTransparencyBitmapWidth,
4794
                                                     mTransparencyBitmapHeight,
4795
                                                     GetBitmapStride(mTransparencyBitmapWidth));
4796
    if (!maskBitmap)
4797
        return;
4798
4799
    cairo_region_t * maskRegion = gdk_cairo_region_create_from_surface(maskBitmap);
4800
    gtk_widget_shape_combine_region(mShell, maskRegion);
4801
    cairo_region_destroy(maskRegion);
4802
    cairo_surface_destroy(maskBitmap);
4803
#endif // MOZ_X11
4804
}
4805
4806
void
4807
nsWindow::ClearTransparencyBitmap()
4808
0
{
4809
0
    if (!mTransparencyBitmap)
4810
0
        return;
4811
0
4812
0
    delete[] mTransparencyBitmap;
4813
0
    mTransparencyBitmap = nullptr;
4814
0
    mTransparencyBitmapWidth = 0;
4815
0
    mTransparencyBitmapHeight = 0;
4816
0
4817
0
    if (!mShell)
4818
0
        return;
4819
0
4820
0
#ifdef MOZ_X11
4821
0
    if (!mGdkWindow)
4822
0
        return;
4823
0
4824
0
    Display* xDisplay = GDK_WINDOW_XDISPLAY(mGdkWindow);
4825
0
    Window xWindow = gdk_x11_window_get_xid(mGdkWindow);
4826
0
4827
0
    XShapeCombineMask(xDisplay, xWindow, ShapeBounding, 0, 0, X11None, ShapeSet);
4828
0
#endif
4829
0
}
4830
4831
nsresult
4832
nsWindow::UpdateTranslucentWindowAlphaInternal(const nsIntRect& aRect,
4833
                                               uint8_t* aAlphas, int32_t aStride)
4834
0
{
4835
0
    if (!mShell) {
4836
0
        // Pass the request to the toplevel window
4837
0
        GtkWidget *topWidget = GetToplevelWidget();
4838
0
        if (!topWidget)
4839
0
            return NS_ERROR_FAILURE;
4840
0
4841
0
        nsWindow *topWindow = get_window_for_gtk_widget(topWidget);
4842
0
        if (!topWindow)
4843
0
            return NS_ERROR_FAILURE;
4844
0
4845
0
        return topWindow->UpdateTranslucentWindowAlphaInternal(aRect, aAlphas, aStride);
4846
0
    }
4847
0
4848
0
    NS_ASSERTION(mIsTransparent, "Window is not transparent");
4849
0
4850
0
    if (mTransparencyBitmap == nullptr) {
4851
0
        int32_t size = GetBitmapStride(mBounds.width)*mBounds.height;
4852
0
        mTransparencyBitmap = new gchar[size];
4853
0
        memset(mTransparencyBitmap, 255, size);
4854
0
        mTransparencyBitmapWidth = mBounds.width;
4855
0
        mTransparencyBitmapHeight = mBounds.height;
4856
0
    } else {
4857
0
        ResizeTransparencyBitmap();
4858
0
    }
4859
0
4860
0
    nsIntRect rect;
4861
0
    rect.IntersectRect(aRect, nsIntRect(0, 0, mBounds.width, mBounds.height));
4862
0
4863
0
    if (!ChangedMaskBits(mTransparencyBitmap, mBounds.width, mBounds.height,
4864
0
                         rect, aAlphas, aStride))
4865
0
        // skip the expensive stuff if the mask bits haven't changed; hopefully
4866
0
        // this is the common case
4867
0
        return NS_OK;
4868
0
4869
0
    UpdateMaskBits(mTransparencyBitmap, mBounds.width, mBounds.height,
4870
0
                   rect, aAlphas, aStride);
4871
0
4872
0
    if (!mNeedsShow) {
4873
0
        ApplyTransparencyBitmap();
4874
0
    }
4875
0
    return NS_OK;
4876
0
}
4877
4878
void
4879
nsWindow::GrabPointer(guint32 aTime)
4880
0
{
4881
0
    LOG(("GrabPointer time=0x%08x retry=%d\n",
4882
0
         (unsigned int)aTime, mRetryPointerGrab));
4883
0
4884
0
    mRetryPointerGrab = false;
4885
0
    sRetryGrabTime = aTime;
4886
0
4887
0
    // If the window isn't visible, just set the flag to retry the
4888
0
    // grab.  When this window becomes visible, the grab will be
4889
0
    // retried.
4890
0
    if (!mHasMappedToplevel || mIsFullyObscured) {
4891
0
        LOG(("GrabPointer: window not visible\n"));
4892
0
        mRetryPointerGrab = true;
4893
0
        return;
4894
0
    }
4895
0
4896
0
    if (!mGdkWindow)
4897
0
        return;
4898
0
4899
0
    if (!mIsX11Display) {
4900
0
        // Don't to the grab on Wayland as it causes a regression
4901
0
        // from Bug 1377084.
4902
0
        return;
4903
0
    }
4904
0
4905
0
    gint retval;
4906
0
    // Note that we need GDK_TOUCH_MASK below to work around a GDK/X11 bug that
4907
0
    // causes touch events that would normally be received by this client on
4908
0
    // other windows to be discarded during the grab.
4909
0
    retval = gdk_pointer_grab(mGdkWindow, TRUE,
4910
0
                              (GdkEventMask)(GDK_BUTTON_PRESS_MASK |
4911
0
                                             GDK_BUTTON_RELEASE_MASK |
4912
0
                                             GDK_ENTER_NOTIFY_MASK |
4913
0
                                             GDK_LEAVE_NOTIFY_MASK |
4914
0
                                             GDK_POINTER_MOTION_MASK |
4915
0
                                             GDK_TOUCH_MASK),
4916
0
                              (GdkWindow *)nullptr, nullptr, aTime);
4917
0
4918
0
    if (retval == GDK_GRAB_NOT_VIEWABLE) {
4919
0
        LOG(("GrabPointer: window not viewable; will retry\n"));
4920
0
        mRetryPointerGrab = true;
4921
0
    } else if (retval != GDK_GRAB_SUCCESS) {
4922
0
        LOG(("GrabPointer: pointer grab failed: %i\n", retval));
4923
0
        // A failed grab indicates that another app has grabbed the pointer.
4924
0
        // Check for rollup now, because, without the grab, we likely won't
4925
0
        // get subsequent button press events. Do this with an event so that
4926
0
        // popups don't rollup while potentially adjusting the grab for
4927
0
        // this popup.
4928
0
        nsCOMPtr<nsIRunnable> event =
4929
0
          NewRunnableMethod("nsWindow::CheckForRollupDuringGrab",
4930
0
                            this,
4931
0
                            &nsWindow::CheckForRollupDuringGrab);
4932
0
        NS_DispatchToCurrentThread(event.forget());
4933
0
    }
4934
0
}
4935
4936
void
4937
nsWindow::ReleaseGrabs(void)
4938
0
{
4939
0
    LOG(("ReleaseGrabs\n"));
4940
0
4941
0
    mRetryPointerGrab = false;
4942
0
4943
0
    if (!mIsX11Display) {
4944
0
        // Don't to the ungrab on Wayland as it causes a regression
4945
0
        // from Bug 1377084.
4946
0
        return;
4947
0
    }
4948
0
4949
0
    gdk_pointer_ungrab(GDK_CURRENT_TIME);
4950
0
}
4951
4952
GtkWidget *
4953
nsWindow::GetToplevelWidget()
4954
0
{
4955
0
    if (mShell) {
4956
0
        return mShell;
4957
0
    }
4958
0
4959
0
    GtkWidget *widget = GetMozContainerWidget();
4960
0
    if (!widget)
4961
0
        return nullptr;
4962
0
4963
0
    return gtk_widget_get_toplevel(widget);
4964
0
}
4965
4966
GtkWidget *
4967
nsWindow::GetMozContainerWidget()
4968
0
{
4969
0
    if (!mGdkWindow)
4970
0
        return nullptr;
4971
0
4972
0
    if (mContainer)
4973
0
        return GTK_WIDGET(mContainer);
4974
0
4975
0
    GtkWidget *owningWidget =
4976
0
        get_gtk_widget_for_gdk_window(mGdkWindow);
4977
0
    return owningWidget;
4978
0
}
4979
4980
nsWindow *
4981
nsWindow::GetContainerWindow()
4982
0
{
4983
0
    GtkWidget *owningWidget = GetMozContainerWidget();
4984
0
    if (!owningWidget)
4985
0
        return nullptr;
4986
0
4987
0
    nsWindow *window = get_window_for_gtk_widget(owningWidget);
4988
0
    NS_ASSERTION(window, "No nsWindow for container widget");
4989
0
    return window;
4990
0
}
4991
4992
void
4993
nsWindow::SetUrgencyHint(GtkWidget *top_window, bool state)
4994
0
{
4995
0
    if (!top_window)
4996
0
        return;
4997
0
4998
0
    gdk_window_set_urgency_hint(gtk_widget_get_window(top_window), state);
4999
0
}
5000
5001
void
5002
nsWindow::SetDefaultIcon(void)
5003
0
{
5004
0
    SetIcon(NS_LITERAL_STRING("default"));
5005
0
}
5006
5007
gint
5008
nsWindow::ConvertBorderStyles(nsBorderStyle aStyle)
5009
0
{
5010
0
    gint w = 0;
5011
0
5012
0
    if (aStyle == eBorderStyle_default)
5013
0
        return -1;
5014
0
5015
0
    // note that we don't handle eBorderStyle_close yet
5016
0
    if (aStyle & eBorderStyle_all)
5017
0
        w |= GDK_DECOR_ALL;
5018
0
    if (aStyle & eBorderStyle_border)
5019
0
        w |= GDK_DECOR_BORDER;
5020
0
    if (aStyle & eBorderStyle_resizeh)
5021
0
        w |= GDK_DECOR_RESIZEH;
5022
0
    if (aStyle & eBorderStyle_title)
5023
0
        w |= GDK_DECOR_TITLE;
5024
0
    if (aStyle & eBorderStyle_menu)
5025
0
        w |= GDK_DECOR_MENU;
5026
0
    if (aStyle & eBorderStyle_minimize)
5027
0
        w |= GDK_DECOR_MINIMIZE;
5028
0
    if (aStyle & eBorderStyle_maximize)
5029
0
        w |= GDK_DECOR_MAXIMIZE;
5030
0
5031
0
    return w;
5032
0
}
5033
5034
class FullscreenTransitionWindow final : public nsISupports
5035
{
5036
public:
5037
    NS_DECL_ISUPPORTS
5038
5039
    explicit FullscreenTransitionWindow(GtkWidget* aWidget);
5040
5041
    GtkWidget* mWindow;
5042
5043
private:
5044
    ~FullscreenTransitionWindow();
5045
};
5046
5047
NS_IMPL_ISUPPORTS0(FullscreenTransitionWindow)
5048
5049
FullscreenTransitionWindow::FullscreenTransitionWindow(GtkWidget* aWidget)
5050
0
{
5051
0
    mWindow = gtk_window_new(GTK_WINDOW_POPUP);
5052
0
    GtkWindow* gtkWin = GTK_WINDOW(mWindow);
5053
0
5054
0
    gtk_window_set_type_hint(gtkWin, GDK_WINDOW_TYPE_HINT_SPLASHSCREEN);
5055
0
    gtk_window_set_transient_for(gtkWin, GTK_WINDOW(aWidget));
5056
0
    gtk_window_set_decorated(gtkWin, false);
5057
0
5058
0
    GdkWindow* gdkWin = gtk_widget_get_window(aWidget);
5059
0
    GdkScreen* screen = gtk_widget_get_screen(aWidget);
5060
0
    gint monitorNum = gdk_screen_get_monitor_at_window(screen, gdkWin);
5061
0
    GdkRectangle monitorRect;
5062
0
    gdk_screen_get_monitor_geometry(screen, monitorNum, &monitorRect);
5063
0
    gtk_window_set_screen(gtkWin, screen);
5064
0
    gtk_window_move(gtkWin, monitorRect.x, monitorRect.y);
5065
0
    MOZ_ASSERT(monitorRect.width > 0 && monitorRect.height > 0,
5066
0
               "Can't resize window smaller than 1x1.");
5067
0
    gtk_window_resize(gtkWin, monitorRect.width, monitorRect.height);
5068
0
5069
0
    GdkColor bgColor;
5070
0
    bgColor.red = bgColor.green = bgColor.blue = 0;
5071
0
    gtk_widget_modify_bg(mWindow, GTK_STATE_NORMAL, &bgColor);
5072
0
5073
0
    gtk_window_set_opacity(gtkWin, 0.0);
5074
0
    gtk_widget_show(mWindow);
5075
0
}
5076
5077
FullscreenTransitionWindow::~FullscreenTransitionWindow()
5078
0
{
5079
0
    gtk_widget_destroy(mWindow);
5080
0
}
5081
5082
class FullscreenTransitionData
5083
{
5084
public:
5085
    FullscreenTransitionData(nsIWidget::FullscreenTransitionStage aStage,
5086
                             uint16_t aDuration, nsIRunnable* aCallback,
5087
                             FullscreenTransitionWindow* aWindow)
5088
        : mStage(aStage)
5089
        , mStartTime(TimeStamp::Now())
5090
        , mDuration(TimeDuration::FromMilliseconds(aDuration))
5091
        , mCallback(aCallback)
5092
0
        , mWindow(aWindow) { }
5093
5094
    static const guint sInterval = 1000 / 30; // 30fps
5095
    static gboolean TimeoutCallback(gpointer aData);
5096
5097
private:
5098
    nsIWidget::FullscreenTransitionStage mStage;
5099
    TimeStamp mStartTime;
5100
    TimeDuration mDuration;
5101
    nsCOMPtr<nsIRunnable> mCallback;
5102
    RefPtr<FullscreenTransitionWindow> mWindow;
5103
};
5104
5105
/* static */ gboolean
5106
FullscreenTransitionData::TimeoutCallback(gpointer aData)
5107
0
{
5108
0
    bool finishing = false;
5109
0
    auto data = static_cast<FullscreenTransitionData*>(aData);
5110
0
    gdouble opacity = (TimeStamp::Now() - data->mStartTime) / data->mDuration;
5111
0
    if (opacity >= 1.0) {
5112
0
        opacity = 1.0;
5113
0
        finishing = true;
5114
0
    }
5115
0
    if (data->mStage == nsIWidget::eAfterFullscreenToggle) {
5116
0
        opacity = 1.0 - opacity;
5117
0
    }
5118
0
    gtk_window_set_opacity(GTK_WINDOW(data->mWindow->mWindow), opacity);
5119
0
5120
0
    if (!finishing) {
5121
0
        return TRUE;
5122
0
    }
5123
0
    NS_DispatchToMainThread(data->mCallback.forget());
5124
0
    delete data;
5125
0
    return FALSE;
5126
0
}
5127
5128
/* virtual */ bool
5129
nsWindow::PrepareForFullscreenTransition(nsISupports** aData)
5130
0
{
5131
0
    GdkScreen* screen = gtk_widget_get_screen(mShell);
5132
0
    if (!gdk_screen_is_composited(screen)) {
5133
0
        return false;
5134
0
    }
5135
0
    *aData = do_AddRef(new FullscreenTransitionWindow(mShell)).take();
5136
0
    return true;
5137
0
}
5138
5139
/* virtual */ void
5140
nsWindow::PerformFullscreenTransition(FullscreenTransitionStage aStage,
5141
                                      uint16_t aDuration, nsISupports* aData,
5142
                                      nsIRunnable* aCallback)
5143
0
{
5144
0
    auto data = static_cast<FullscreenTransitionWindow*>(aData);
5145
0
    // This will be released at the end of the last timeout callback for it.
5146
0
    auto transitionData = new FullscreenTransitionData(aStage, aDuration,
5147
0
                                                       aCallback, data);
5148
0
    g_timeout_add_full(G_PRIORITY_HIGH,
5149
0
                       FullscreenTransitionData::sInterval,
5150
0
                       FullscreenTransitionData::TimeoutCallback,
5151
0
                       transitionData, nullptr);
5152
0
}
5153
5154
already_AddRefed<nsIScreen>
5155
nsWindow::GetWidgetScreen()
5156
0
{
5157
0
  nsCOMPtr<nsIScreenManager> screenManager;
5158
0
  screenManager = do_GetService("@mozilla.org/gfx/screenmanager;1");
5159
0
  if (!screenManager) {
5160
0
    return nullptr;
5161
0
  }
5162
0
5163
0
  // GetScreenBounds() is slow for the GTK port so we override and use
5164
0
  // mBounds directly.
5165
0
  LayoutDeviceIntRect bounds = mBounds;
5166
0
  if (!mIsTopLevel) {
5167
0
      bounds.MoveTo(WidgetToScreenOffset());
5168
0
  }
5169
0
5170
0
  DesktopIntRect deskBounds = RoundedToInt(bounds / GetDesktopToDeviceScale());
5171
0
  nsCOMPtr<nsIScreen> screen;
5172
0
  screenManager->ScreenForRect(deskBounds.x, deskBounds.y,
5173
0
                               deskBounds.width, deskBounds.height,
5174
0
                               getter_AddRefs(screen));
5175
0
  return screen.forget();
5176
0
}
5177
5178
static bool
5179
IsFullscreenSupported(GtkWidget* aShell)
5180
0
{
5181
0
#ifdef MOZ_X11
5182
0
    GdkScreen* screen = gtk_widget_get_screen(aShell);
5183
0
    GdkAtom atom = gdk_atom_intern("_NET_WM_STATE_FULLSCREEN", FALSE);
5184
0
    if (!gdk_x11_screen_supports_net_wm_hint(screen, atom)) {
5185
0
        return false;
5186
0
    }
5187
0
#endif
5188
0
    return true;
5189
0
}
5190
5191
nsresult
5192
nsWindow::MakeFullScreen(bool aFullScreen, nsIScreen* aTargetScreen)
5193
0
{
5194
0
    LOG(("nsWindow::MakeFullScreen [%p] aFullScreen %d\n",
5195
0
         (void *)this, aFullScreen));
5196
0
5197
0
    if (mIsX11Display && !IsFullscreenSupported(mShell)) {
5198
0
        return NS_ERROR_NOT_AVAILABLE;
5199
0
    }
5200
0
5201
0
    if (aFullScreen) {
5202
0
        if (mSizeMode != nsSizeMode_Fullscreen)
5203
0
            mLastSizeMode = mSizeMode;
5204
0
5205
0
        mSizeMode = nsSizeMode_Fullscreen;
5206
0
        gtk_window_fullscreen(GTK_WINDOW(mShell));
5207
0
    }
5208
0
    else {
5209
0
        mSizeMode = mLastSizeMode;
5210
0
        gtk_window_unfullscreen(GTK_WINDOW(mShell));
5211
0
    }
5212
0
5213
0
    NS_ASSERTION(mLastSizeMode != nsSizeMode_Fullscreen,
5214
0
                 "mLastSizeMode should never be fullscreen");
5215
0
    return NS_OK;
5216
0
}
5217
5218
void
5219
nsWindow::SetWindowDecoration(nsBorderStyle aStyle)
5220
0
{
5221
0
    if (!mShell) {
5222
0
        // Pass the request to the toplevel window
5223
0
        GtkWidget *topWidget = GetToplevelWidget();
5224
0
        if (!topWidget)
5225
0
            return;
5226
0
5227
0
        nsWindow *topWindow = get_window_for_gtk_widget(topWidget);
5228
0
        if (!topWindow)
5229
0
            return;
5230
0
5231
0
        topWindow->SetWindowDecoration(aStyle);
5232
0
        return;
5233
0
    }
5234
0
5235
0
    // We can't use mGdkWindow directly here as it can be
5236
0
    // derived from mContainer which is not a top-level GdkWindow.
5237
0
    GdkWindow *window = gtk_widget_get_window(mShell);
5238
0
5239
0
    // Sawfish, metacity, and presumably other window managers get
5240
0
    // confused if we change the window decorations while the window
5241
0
    // is visible.
5242
0
    bool wasVisible = false;
5243
0
    if (gdk_window_is_visible(window)) {
5244
0
        gdk_window_hide(window);
5245
0
        wasVisible = true;
5246
0
    }
5247
0
5248
0
    gint wmd = ConvertBorderStyles(aStyle);
5249
0
    if (wmd != -1)
5250
0
      gdk_window_set_decorations(window, (GdkWMDecoration) wmd);
5251
0
5252
0
    if (wasVisible)
5253
0
        gdk_window_show(window);
5254
0
5255
0
    // For some window managers, adding or removing window decorations
5256
0
    // requires unmapping and remapping our toplevel window.  Go ahead
5257
0
    // and flush the queue here so that we don't end up with a BadWindow
5258
0
    // error later when this happens (when the persistence timer fires
5259
0
    // and GetWindowPos is called)
5260
0
#ifdef MOZ_X11
5261
0
    if (mIsX11Display) {
5262
0
        XSync(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()) , False);
5263
0
    } else
5264
0
#endif /* MOZ_X11 */
5265
0
    {
5266
0
        gdk_flush ();
5267
0
    }
5268
0
}
5269
5270
void
5271
nsWindow::HideWindowChrome(bool aShouldHide)
5272
0
{
5273
0
    SetWindowDecoration(aShouldHide ? eBorderStyle_none : mBorderStyle);
5274
0
}
5275
5276
bool
5277
nsWindow::CheckForRollup(gdouble aMouseX, gdouble aMouseY,
5278
                         bool aIsWheel, bool aAlwaysRollup)
5279
0
{
5280
0
    nsIRollupListener* rollupListener = GetActiveRollupListener();
5281
0
    nsCOMPtr<nsIWidget> rollupWidget;
5282
0
    if (rollupListener) {
5283
0
        rollupWidget = rollupListener->GetRollupWidget();
5284
0
    }
5285
0
    if (!rollupWidget) {
5286
0
        nsBaseWidget::gRollupListener = nullptr;
5287
0
        return false;
5288
0
    }
5289
0
5290
0
    bool retVal = false;
5291
0
    auto *currentPopup =
5292
0
        (GdkWindow *)rollupWidget->GetNativeData(NS_NATIVE_WINDOW);
5293
0
    if (aAlwaysRollup || !is_mouse_in_window(currentPopup, aMouseX, aMouseY)) {
5294
0
        bool rollup = true;
5295
0
        if (aIsWheel) {
5296
0
            rollup = rollupListener->ShouldRollupOnMouseWheelEvent();
5297
0
            retVal = rollupListener->ShouldConsumeOnMouseWheelEvent();
5298
0
        }
5299
0
        // if we're dealing with menus, we probably have submenus and
5300
0
        // we don't want to rollup if the click is in a parent menu of
5301
0
        // the current submenu
5302
0
        uint32_t popupsToRollup = UINT32_MAX;
5303
0
        if (!aAlwaysRollup) {
5304
0
            AutoTArray<nsIWidget*, 5> widgetChain;
5305
0
            uint32_t sameTypeCount = rollupListener->GetSubmenuWidgetChain(&widgetChain);
5306
0
            for (uint32_t i=0; i<widgetChain.Length(); ++i) {
5307
0
                nsIWidget* widget = widgetChain[i];
5308
0
                auto* currWindow =
5309
0
                    (GdkWindow*) widget->GetNativeData(NS_NATIVE_WINDOW);
5310
0
                if (is_mouse_in_window(currWindow, aMouseX, aMouseY)) {
5311
0
                  // don't roll up if the mouse event occurred within a
5312
0
                  // menu of the same type. If the mouse event occurred
5313
0
                  // in a menu higher than that, roll up, but pass the
5314
0
                  // number of popups to Rollup so that only those of the
5315
0
                  // same type close up.
5316
0
                  if (i < sameTypeCount) {
5317
0
                    rollup = false;
5318
0
                  }
5319
0
                  else {
5320
0
                    popupsToRollup = sameTypeCount;
5321
0
                  }
5322
0
                  break;
5323
0
                }
5324
0
            } // foreach parent menu widget
5325
0
        } // if rollup listener knows about menus
5326
0
5327
0
        // if we've determined that we should still rollup, do it.
5328
0
        bool usePoint = !aIsWheel && !aAlwaysRollup;
5329
0
        IntPoint point = IntPoint::Truncate(aMouseX, aMouseY);
5330
0
        if (rollup && rollupListener->Rollup(popupsToRollup, true, usePoint ? &point : nullptr, nullptr)) {
5331
0
            retVal = true;
5332
0
        }
5333
0
    }
5334
0
    return retVal;
5335
0
}
5336
5337
/* static */
5338
bool
5339
nsWindow::DragInProgress(void)
5340
0
{
5341
0
    nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
5342
0
5343
0
    if (!dragService)
5344
0
        return false;
5345
0
5346
0
    nsCOMPtr<nsIDragSession> currentDragSession;
5347
0
    dragService->GetCurrentSession(getter_AddRefs(currentDragSession));
5348
0
5349
0
    return currentDragSession != nullptr;
5350
0
}
5351
5352
static bool
5353
is_mouse_in_window (GdkWindow* aWindow, gdouble aMouseX, gdouble aMouseY)
5354
0
{
5355
0
    gint x = 0;
5356
0
    gint y = 0;
5357
0
    gint w, h;
5358
0
5359
0
    gint offsetX = 0;
5360
0
    gint offsetY = 0;
5361
0
5362
0
    GdkWindow *window = aWindow;
5363
0
5364
0
    while (window) {
5365
0
        gint tmpX = 0;
5366
0
        gint tmpY = 0;
5367
0
5368
0
        gdk_window_get_position(window, &tmpX, &tmpY);
5369
0
        GtkWidget *widget = get_gtk_widget_for_gdk_window(window);
5370
0
5371
0
        // if this is a window, compute x and y given its origin and our
5372
0
        // offset
5373
0
        if (GTK_IS_WINDOW(widget)) {
5374
0
            x = tmpX + offsetX;
5375
0
            y = tmpY + offsetY;
5376
0
            break;
5377
0
        }
5378
0
5379
0
        offsetX += tmpX;
5380
0
        offsetY += tmpY;
5381
0
        window = gdk_window_get_parent(window);
5382
0
    }
5383
0
5384
0
    w = gdk_window_get_width(aWindow);
5385
0
    h = gdk_window_get_height(aWindow);
5386
0
5387
0
    if (aMouseX > x && aMouseX < x + w &&
5388
0
        aMouseY > y && aMouseY < y + h)
5389
0
        return true;
5390
0
5391
0
    return false;
5392
0
}
5393
5394
static nsWindow *
5395
get_window_for_gtk_widget(GtkWidget *widget)
5396
0
{
5397
0
    gpointer user_data = g_object_get_data(G_OBJECT(widget), "nsWindow");
5398
0
5399
0
    return static_cast<nsWindow *>(user_data);
5400
0
}
5401
5402
static nsWindow *
5403
get_window_for_gdk_window(GdkWindow *window)
5404
0
{
5405
0
    gpointer user_data = g_object_get_data(G_OBJECT(window), "nsWindow");
5406
0
5407
0
    return static_cast<nsWindow *>(user_data);
5408
0
}
5409
5410
static GtkWidget *
5411
get_gtk_widget_for_gdk_window(GdkWindow *window)
5412
0
{
5413
0
    gpointer user_data = nullptr;
5414
0
    gdk_window_get_user_data(window, &user_data);
5415
0
5416
0
    return GTK_WIDGET(user_data);
5417
0
}
5418
5419
static GdkCursor *
5420
get_gtk_cursor(nsCursor aCursor)
5421
0
{
5422
0
    GdkCursor *gdkcursor = nullptr;
5423
0
    uint8_t newType = 0xff;
5424
0
5425
0
    if ((gdkcursor = gCursorCache[aCursor])) {
5426
0
        return gdkcursor;
5427
0
    }
5428
0
5429
0
    GdkDisplay *defaultDisplay = gdk_display_get_default();
5430
0
5431
0
    // The strategy here is to use standard GDK cursors, and, if not available,
5432
0
    // load by standard name with gdk_cursor_new_from_name.
5433
0
    // Spec is here: http://www.freedesktop.org/wiki/Specifications/cursor-spec/
5434
0
    switch (aCursor) {
5435
0
    case eCursor_standard:
5436
0
        gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_LEFT_PTR);
5437
0
        break;
5438
0
    case eCursor_wait:
5439
0
        gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_WATCH);
5440
0
        break;
5441
0
    case eCursor_select:
5442
0
        gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_XTERM);
5443
0
        break;
5444
0
    case eCursor_hyperlink:
5445
0
        gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_HAND2);
5446
0
        break;
5447
0
    case eCursor_n_resize:
5448
0
        gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_TOP_SIDE);
5449
0
        break;
5450
0
    case eCursor_s_resize:
5451
0
        gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_BOTTOM_SIDE);
5452
0
        break;
5453
0
    case eCursor_w_resize:
5454
0
        gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_LEFT_SIDE);
5455
0
        break;
5456
0
    case eCursor_e_resize:
5457
0
        gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_RIGHT_SIDE);
5458
0
        break;
5459
0
    case eCursor_nw_resize:
5460
0
        gdkcursor = gdk_cursor_new_for_display(defaultDisplay,
5461
0
                                               GDK_TOP_LEFT_CORNER);
5462
0
        break;
5463
0
    case eCursor_se_resize:
5464
0
        gdkcursor = gdk_cursor_new_for_display(defaultDisplay,
5465
0
                                               GDK_BOTTOM_RIGHT_CORNER);
5466
0
        break;
5467
0
    case eCursor_ne_resize:
5468
0
        gdkcursor = gdk_cursor_new_for_display(defaultDisplay,
5469
0
                                               GDK_TOP_RIGHT_CORNER);
5470
0
        break;
5471
0
    case eCursor_sw_resize:
5472
0
        gdkcursor = gdk_cursor_new_for_display(defaultDisplay,
5473
0
                                               GDK_BOTTOM_LEFT_CORNER);
5474
0
        break;
5475
0
    case eCursor_crosshair:
5476
0
        gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_CROSSHAIR);
5477
0
        break;
5478
0
    case eCursor_move:
5479
0
        gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_FLEUR);
5480
0
        break;
5481
0
    case eCursor_help:
5482
0
        gdkcursor = gdk_cursor_new_for_display(defaultDisplay,
5483
0
                                               GDK_QUESTION_ARROW);
5484
0
        break;
5485
0
    case eCursor_copy: // CSS3
5486
0
        gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "copy");
5487
0
        if (!gdkcursor)
5488
0
            newType = MOZ_CURSOR_COPY;
5489
0
        break;
5490
0
    case eCursor_alias:
5491
0
        gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "alias");
5492
0
        if (!gdkcursor)
5493
0
            newType = MOZ_CURSOR_ALIAS;
5494
0
        break;
5495
0
    case eCursor_context_menu:
5496
0
        gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "context-menu");
5497
0
        if (!gdkcursor)
5498
0
            newType = MOZ_CURSOR_CONTEXT_MENU;
5499
0
        break;
5500
0
    case eCursor_cell:
5501
0
        gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_PLUS);
5502
0
        break;
5503
0
    // Those two aren’t standardized. Trying both KDE’s and GNOME’s names
5504
0
    case eCursor_grab:
5505
0
        gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "openhand");
5506
0
        if (!gdkcursor)
5507
0
            newType = MOZ_CURSOR_HAND_GRAB;
5508
0
        break;
5509
0
    case eCursor_grabbing:
5510
0
        gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "closedhand");
5511
0
        if (!gdkcursor)
5512
0
            gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "grabbing");
5513
0
        if (!gdkcursor)
5514
0
            newType = MOZ_CURSOR_HAND_GRABBING;
5515
0
        break;
5516
0
    case eCursor_spinning:
5517
0
        gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "progress");
5518
0
        if (!gdkcursor)
5519
0
            newType = MOZ_CURSOR_SPINNING;
5520
0
        break;
5521
0
    case eCursor_zoom_in:
5522
0
        gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "zoom-in");
5523
0
        if (!gdkcursor)
5524
0
            newType = MOZ_CURSOR_ZOOM_IN;
5525
0
        break;
5526
0
    case eCursor_zoom_out:
5527
0
        gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "zoom-out");
5528
0
        if (!gdkcursor)
5529
0
            newType = MOZ_CURSOR_ZOOM_OUT;
5530
0
        break;
5531
0
    case eCursor_not_allowed:
5532
0
        gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "not-allowed");
5533
0
        if (!gdkcursor) // nonstandard, yet common
5534
0
            gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "crossed_circle");
5535
0
        if (!gdkcursor)
5536
0
            newType = MOZ_CURSOR_NOT_ALLOWED;
5537
0
        break;
5538
0
    case eCursor_no_drop:
5539
0
        gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "no-drop");
5540
0
        if (!gdkcursor) // this nonstandard sequence makes it work on KDE and GNOME
5541
0
            gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "forbidden");
5542
0
        if (!gdkcursor)
5543
0
            gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "circle");
5544
0
        if (!gdkcursor)
5545
0
            newType = MOZ_CURSOR_NOT_ALLOWED;
5546
0
        break;
5547
0
    case eCursor_vertical_text:
5548
0
        newType = MOZ_CURSOR_VERTICAL_TEXT;
5549
0
        break;
5550
0
    case eCursor_all_scroll:
5551
0
        gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_FLEUR);
5552
0
        break;
5553
0
    case eCursor_nesw_resize:
5554
0
        gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "size_bdiag");
5555
0
        if (!gdkcursor)
5556
0
            newType = MOZ_CURSOR_NESW_RESIZE;
5557
0
        break;
5558
0
    case eCursor_nwse_resize:
5559
0
        gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "size_fdiag");
5560
0
        if (!gdkcursor)
5561
0
            newType = MOZ_CURSOR_NWSE_RESIZE;
5562
0
        break;
5563
0
    case eCursor_ns_resize:
5564
0
        gdkcursor = gdk_cursor_new_for_display(defaultDisplay,
5565
0
                                               GDK_SB_V_DOUBLE_ARROW);
5566
0
        break;
5567
0
    case eCursor_ew_resize:
5568
0
        gdkcursor = gdk_cursor_new_for_display(defaultDisplay,
5569
0
                                               GDK_SB_H_DOUBLE_ARROW);
5570
0
        break;
5571
0
    // Here, two better fitting cursors exist in some cursor themes. Try those first
5572
0
    case eCursor_row_resize:
5573
0
        gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "split_v");
5574
0
        if (!gdkcursor)
5575
0
            gdkcursor = gdk_cursor_new_for_display(defaultDisplay,
5576
0
                                                   GDK_SB_V_DOUBLE_ARROW);
5577
0
        break;
5578
0
    case eCursor_col_resize:
5579
0
        gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "split_h");
5580
0
        if (!gdkcursor)
5581
0
            gdkcursor = gdk_cursor_new_for_display(defaultDisplay,
5582
0
                                                   GDK_SB_H_DOUBLE_ARROW);
5583
0
        break;
5584
0
    case eCursor_none:
5585
0
        newType = MOZ_CURSOR_NONE;
5586
0
        break;
5587
0
    default:
5588
0
        NS_ASSERTION(aCursor, "Invalid cursor type");
5589
0
        gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_LEFT_PTR);
5590
0
        break;
5591
0
    }
5592
0
5593
0
    // If by now we don't have a xcursor, this means we have to make a custom
5594
0
    // one. First, we try creating a named cursor based on the hash of our
5595
0
    // custom bitmap, as libXcursor has some magic to convert bitmapped cursors
5596
0
    // to themed cursors
5597
0
    if (newType != 0xFF && GtkCursors[newType].hash) {
5598
0
        gdkcursor = gdk_cursor_new_from_name(defaultDisplay, GtkCursors[newType].hash);
5599
0
    }
5600
0
5601
0
    // If we still don't have a xcursor, we now really create a bitmap cursor
5602
0
    if (newType != 0xff && !gdkcursor) {
5603
0
        GdkPixbuf * cursor_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32);
5604
0
        if (!cursor_pixbuf)
5605
0
            return nullptr;
5606
0
5607
0
        guchar *data = gdk_pixbuf_get_pixels(cursor_pixbuf);
5608
0
5609
0
        // Read data from GtkCursors and compose RGBA surface from 1bit bitmap and mask
5610
0
        // GtkCursors bits and mask are 32x32 monochrome bitmaps (1 bit for each pixel)
5611
0
        // so it's 128 byte array (4 bytes for are one bitmap row and there are 32 rows here).
5612
0
        const unsigned char *bits = GtkCursors[newType].bits;
5613
0
        const unsigned char *mask_bits = GtkCursors[newType].mask_bits;
5614
0
5615
0
        for (int i = 0; i < 128; i++) {
5616
0
            char bit = *bits++;
5617
0
            char mask = *mask_bits++;
5618
0
            for (int j = 0; j < 8; j++) {
5619
0
                unsigned char pix = ~(((bit >> j) & 0x01) * 0xff);
5620
0
                *data++ = pix;
5621
0
                *data++ = pix;
5622
0
                *data++ = pix;
5623
0
                *data++ = (((mask >> j) & 0x01) * 0xff);
5624
0
            }
5625
0
        }
5626
0
5627
0
        gdkcursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), cursor_pixbuf,
5628
0
                                               GtkCursors[newType].hot_x,
5629
0
                                               GtkCursors[newType].hot_y);
5630
0
5631
0
        g_object_unref(cursor_pixbuf);
5632
0
    }
5633
0
5634
0
    gCursorCache[aCursor] = gdkcursor;
5635
0
5636
0
    return gdkcursor;
5637
0
}
5638
5639
// gtk callbacks
5640
5641
void
5642
draw_window_of_widget(GtkWidget *widget, GdkWindow *aWindow, cairo_t *cr)
5643
0
{
5644
0
    if (gtk_cairo_should_draw_window(cr, aWindow)) {
5645
0
        RefPtr<nsWindow> window = get_window_for_gdk_window(aWindow);
5646
0
        if (!window) {
5647
0
            NS_WARNING("Cannot get nsWindow from GtkWidget");
5648
0
        }
5649
0
        else {
5650
0
            cairo_save(cr);
5651
0
            gtk_cairo_transform_to_window(cr, widget, aWindow);
5652
0
            // TODO - window->OnExposeEvent() can destroy this or other windows,
5653
0
            // do we need to handle it somehow?
5654
0
            window->OnExposeEvent(cr);
5655
0
            cairo_restore(cr);
5656
0
        }
5657
0
    }
5658
0
5659
0
    GList *children = gdk_window_get_children(aWindow);
5660
0
    GList *child = children;
5661
0
    while (child) {
5662
0
        GdkWindow *window = GDK_WINDOW(child->data);
5663
0
        gpointer windowWidget;
5664
0
        gdk_window_get_user_data(window, &windowWidget);
5665
0
        if (windowWidget == widget) {
5666
0
            draw_window_of_widget(widget, window, cr);
5667
0
        }
5668
0
        child = g_list_next(child);
5669
0
    }
5670
0
    g_list_free(children);
5671
0
}
5672
5673
/* static */
5674
gboolean
5675
expose_event_cb(GtkWidget *widget, cairo_t *cr)
5676
0
{
5677
0
    draw_window_of_widget(widget, gtk_widget_get_window(widget), cr);
5678
0
5679
0
    // A strong reference is already held during "draw" signal emission,
5680
0
    // but GTK+ 3.4 wants the object to live a little longer than that
5681
0
    // (bug 1225970).
5682
0
    g_object_ref(widget);
5683
0
    g_idle_add(
5684
0
        [](gpointer data) -> gboolean {
5685
0
            g_object_unref(data);
5686
0
            return G_SOURCE_REMOVE;
5687
0
        },
5688
0
        widget);
5689
0
5690
0
    return FALSE;
5691
0
}
5692
5693
static gboolean
5694
configure_event_cb(GtkWidget *widget,
5695
                   GdkEventConfigure *event)
5696
0
{
5697
0
    RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
5698
0
    if (!window)
5699
0
        return FALSE;
5700
0
5701
0
    return window->OnConfigureEvent(widget, event);
5702
0
}
5703
5704
static void
5705
container_unrealize_cb (GtkWidget *widget)
5706
0
{
5707
0
    RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
5708
0
    if (!window)
5709
0
        return;
5710
0
5711
0
    window->OnContainerUnrealize();
5712
0
}
5713
5714
static void
5715
size_allocate_cb (GtkWidget *widget, GtkAllocation *allocation)
5716
0
{
5717
0
    RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
5718
0
    if (!window)
5719
0
        return;
5720
0
5721
0
    window->OnSizeAllocate(allocation);
5722
0
}
5723
5724
static gboolean
5725
delete_event_cb(GtkWidget *widget, GdkEventAny *event)
5726
0
{
5727
0
    RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
5728
0
    if (!window)
5729
0
        return FALSE;
5730
0
5731
0
    window->OnDeleteEvent();
5732
0
5733
0
    return TRUE;
5734
0
}
5735
5736
static gboolean
5737
enter_notify_event_cb(GtkWidget *widget,
5738
                      GdkEventCrossing *event)
5739
0
{
5740
0
    RefPtr<nsWindow> window = get_window_for_gdk_window(event->window);
5741
0
    if (!window)
5742
0
        return TRUE;
5743
0
5744
0
    window->OnEnterNotifyEvent(event);
5745
0
5746
0
    return TRUE;
5747
0
}
5748
5749
static gboolean
5750
leave_notify_event_cb(GtkWidget *widget,
5751
                      GdkEventCrossing *event)
5752
0
{
5753
0
    if (is_parent_grab_leave(event)) {
5754
0
        return TRUE;
5755
0
    }
5756
0
5757
0
    // bug 369599: Suppress LeaveNotify events caused by pointer grabs to
5758
0
    // avoid generating spurious mouse exit events.
5759
0
    auto x = gint(event->x_root);
5760
0
    auto y = gint(event->y_root);
5761
0
    GdkDisplay* display = gtk_widget_get_display(widget);
5762
0
    GdkWindow* winAtPt = gdk_display_get_window_at_pointer(display, &x, &y);
5763
0
    if (winAtPt == event->window) {
5764
0
        return TRUE;
5765
0
    }
5766
0
5767
0
    RefPtr<nsWindow> window = get_window_for_gdk_window(event->window);
5768
0
    if (!window)
5769
0
        return TRUE;
5770
0
5771
0
    window->OnLeaveNotifyEvent(event);
5772
0
5773
0
    return TRUE;
5774
0
}
5775
5776
static nsWindow*
5777
GetFirstNSWindowForGDKWindow(GdkWindow *aGdkWindow)
5778
0
{
5779
0
    nsWindow* window;
5780
0
    while (!(window = get_window_for_gdk_window(aGdkWindow))) {
5781
0
        // The event has bubbled to the moz_container widget as passed into each caller's *widget parameter,
5782
0
        // but its corresponding nsWindow is an ancestor of the window that we need.  Instead, look at
5783
0
        // event->window and find the first ancestor nsWindow of it because event->window may be in a plugin.
5784
0
        aGdkWindow = gdk_window_get_parent(aGdkWindow);
5785
0
        if (!aGdkWindow) {
5786
0
            window = nullptr;
5787
0
            break;
5788
0
        }
5789
0
    }
5790
0
    return window;
5791
0
}
5792
5793
static gboolean
5794
motion_notify_event_cb(GtkWidget *widget, GdkEventMotion *event)
5795
0
{
5796
0
    UpdateLastInputEventTime(event);
5797
0
5798
0
    nsWindow *window = GetFirstNSWindowForGDKWindow(event->window);
5799
0
    if (!window)
5800
0
        return FALSE;
5801
0
5802
0
    window->OnMotionNotifyEvent(event);
5803
0
5804
0
    return TRUE;
5805
0
}
5806
5807
static gboolean
5808
button_press_event_cb(GtkWidget *widget, GdkEventButton *event)
5809
0
{
5810
0
    UpdateLastInputEventTime(event);
5811
0
5812
0
    nsWindow *window = GetFirstNSWindowForGDKWindow(event->window);
5813
0
    if (!window)
5814
0
        return FALSE;
5815
0
5816
0
    window->OnButtonPressEvent(event);
5817
0
5818
0
    return TRUE;
5819
0
}
5820
5821
static gboolean
5822
button_release_event_cb(GtkWidget *widget, GdkEventButton *event)
5823
0
{
5824
0
    UpdateLastInputEventTime(event);
5825
0
5826
0
    nsWindow *window = GetFirstNSWindowForGDKWindow(event->window);
5827
0
    if (!window)
5828
0
        return FALSE;
5829
0
5830
0
    window->OnButtonReleaseEvent(event);
5831
0
5832
0
    return TRUE;
5833
0
}
5834
5835
static gboolean
5836
focus_in_event_cb(GtkWidget *widget, GdkEventFocus *event)
5837
0
{
5838
0
    RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
5839
0
    if (!window)
5840
0
        return FALSE;
5841
0
5842
0
    window->OnContainerFocusInEvent(event);
5843
0
5844
0
    return FALSE;
5845
0
}
5846
5847
static gboolean
5848
focus_out_event_cb(GtkWidget *widget, GdkEventFocus *event)
5849
0
{
5850
0
    RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
5851
0
    if (!window)
5852
0
        return FALSE;
5853
0
5854
0
    window->OnContainerFocusOutEvent(event);
5855
0
5856
0
    return FALSE;
5857
0
}
5858
5859
#ifdef MOZ_X11
5860
// For long-lived popup windows that don't really take focus themselves but
5861
// may have elements that accept keyboard input when the parent window is
5862
// active, focus is handled specially.  These windows include noautohide
5863
// panels.  (This special handling is not necessary for temporary popups where
5864
// the keyboard is grabbed.)
5865
//
5866
// Mousing over or clicking on these windows should not cause them to steal
5867
// focus from their parent windows, so, the input field of WM_HINTS is set to
5868
// False to request that the window manager not set the input focus to this
5869
// window.  http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7
5870
//
5871
// However, these windows can still receive WM_TAKE_FOCUS messages from the
5872
// window manager, so they can still detect when the user has indicated that
5873
// they wish to direct keyboard input at these windows.  When the window
5874
// manager offers focus to these windows (after a mouse over or click, for
5875
// example), a request to make the parent window active is issued.  When the
5876
// parent window becomes active, keyboard events will be received.
5877
5878
static GdkFilterReturn
5879
popup_take_focus_filter(GdkXEvent *gdk_xevent,
5880
                        GdkEvent *event,
5881
                        gpointer data)
5882
0
{
5883
0
    auto* xevent = static_cast<XEvent*>(gdk_xevent);
5884
0
    if (xevent->type != ClientMessage)
5885
0
        return GDK_FILTER_CONTINUE;
5886
0
5887
0
    XClientMessageEvent& xclient = xevent->xclient;
5888
0
    if (xclient.message_type != gdk_x11_get_xatom_by_name("WM_PROTOCOLS"))
5889
0
        return GDK_FILTER_CONTINUE;
5890
0
5891
0
    Atom atom = xclient.data.l[0];
5892
0
    if (atom != gdk_x11_get_xatom_by_name("WM_TAKE_FOCUS"))
5893
0
        return GDK_FILTER_CONTINUE;
5894
0
5895
0
    guint32 timestamp = xclient.data.l[1];
5896
0
5897
0
    GtkWidget* widget = get_gtk_widget_for_gdk_window(event->any.window);
5898
0
    if (!widget)
5899
0
        return GDK_FILTER_CONTINUE;
5900
0
5901
0
    GtkWindow* parent = gtk_window_get_transient_for(GTK_WINDOW(widget));
5902
0
    if (!parent)
5903
0
        return GDK_FILTER_CONTINUE;
5904
0
5905
0
    if (gtk_window_is_active(parent))
5906
0
        return GDK_FILTER_REMOVE; // leave input focus on the parent
5907
0
5908
0
    GdkWindow* parent_window = gtk_widget_get_window(GTK_WIDGET(parent));
5909
0
    if (!parent_window)
5910
0
        return GDK_FILTER_CONTINUE;
5911
0
5912
0
    // In case the parent has not been deconified.
5913
0
    gdk_window_show_unraised(parent_window);
5914
0
5915
0
    // Request focus on the parent window.
5916
0
    // Use gdk_window_focus rather than gtk_window_present to avoid
5917
0
    // raising the parent window.
5918
0
    gdk_window_focus(parent_window, timestamp);
5919
0
    return GDK_FILTER_REMOVE;
5920
0
}
5921
#endif /* MOZ_X11 */
5922
5923
static gboolean
5924
key_press_event_cb(GtkWidget *widget, GdkEventKey *event)
5925
0
{
5926
0
    LOG(("key_press_event_cb\n"));
5927
0
5928
0
    UpdateLastInputEventTime(event);
5929
0
5930
0
    // find the window with focus and dispatch this event to that widget
5931
0
    nsWindow *window = get_window_for_gtk_widget(widget);
5932
0
    if (!window)
5933
0
        return FALSE;
5934
0
5935
0
    RefPtr<nsWindow> focusWindow = gFocusWindow ? gFocusWindow : window;
5936
0
5937
0
#ifdef MOZ_X11
5938
0
    // Keyboard repeat can cause key press events to queue up when there are
5939
0
    // slow event handlers (bug 301029).  Throttle these events by removing
5940
0
    // consecutive pending duplicate KeyPress events to the same window.
5941
0
    // We use the event time of the last one.
5942
0
    // Note: GDK calls XkbSetDetectableAutorepeat so that KeyRelease events
5943
0
    // are generated only when the key is physically released.
5944
0
#define NS_GDKEVENT_MATCH_MASK 0x1FFF /* GDK_SHIFT_MASK .. GDK_BUTTON5_MASK */
5945
0
    GdkDisplay* gdkDisplay = gtk_widget_get_display(widget);
5946
0
    if (GDK_IS_X11_DISPLAY(gdkDisplay)) {
5947
0
        Display* dpy = GDK_DISPLAY_XDISPLAY(gdkDisplay);
5948
0
        while (XPending(dpy)) {
5949
0
            XEvent next_event;
5950
0
            XPeekEvent(dpy, &next_event);
5951
0
            GdkWindow* nextGdkWindow =
5952
0
                gdk_x11_window_lookup_for_display(gdkDisplay, next_event.xany.window);
5953
0
            if (nextGdkWindow != event->window ||
5954
0
                next_event.type != KeyPress ||
5955
0
                next_event.xkey.keycode != event->hardware_keycode ||
5956
0
                next_event.xkey.state != (event->state & NS_GDKEVENT_MATCH_MASK)) {
5957
0
                break;
5958
0
            }
5959
0
            XNextEvent(dpy, &next_event);
5960
0
            event->time = next_event.xkey.time;
5961
0
        }
5962
0
    }
5963
0
#endif
5964
0
5965
0
    return focusWindow->OnKeyPressEvent(event);
5966
0
}
5967
5968
static gboolean
5969
key_release_event_cb(GtkWidget *widget, GdkEventKey *event)
5970
0
{
5971
0
    LOG(("key_release_event_cb\n"));
5972
0
5973
0
    UpdateLastInputEventTime(event);
5974
0
5975
0
    // find the window with focus and dispatch this event to that widget
5976
0
    nsWindow *window = get_window_for_gtk_widget(widget);
5977
0
    if (!window)
5978
0
        return FALSE;
5979
0
5980
0
    RefPtr<nsWindow> focusWindow = gFocusWindow ? gFocusWindow : window;
5981
0
5982
0
    return focusWindow->OnKeyReleaseEvent(event);
5983
0
}
5984
5985
static gboolean
5986
property_notify_event_cb(GtkWidget* aWidget, GdkEventProperty* aEvent)
5987
0
{
5988
0
    RefPtr<nsWindow> window = get_window_for_gdk_window(aEvent->window);
5989
0
    if (!window)
5990
0
        return FALSE;
5991
0
5992
0
    return window->OnPropertyNotifyEvent(aWidget, aEvent);
5993
0
}
5994
5995
static gboolean
5996
scroll_event_cb(GtkWidget *widget, GdkEventScroll *event)
5997
0
{
5998
0
    nsWindow *window = GetFirstNSWindowForGDKWindow(event->window);
5999
0
    if (!window)
6000
0
        return FALSE;
6001
0
6002
0
    window->OnScrollEvent(event);
6003
0
6004
0
    return TRUE;
6005
0
}
6006
6007
static gboolean
6008
visibility_notify_event_cb (GtkWidget *widget, GdkEventVisibility *event)
6009
0
{
6010
0
    RefPtr<nsWindow> window = get_window_for_gdk_window(event->window);
6011
0
    if (!window)
6012
0
        return FALSE;
6013
0
6014
0
    window->OnVisibilityNotifyEvent(event);
6015
0
6016
0
    return TRUE;
6017
0
}
6018
6019
static void
6020
hierarchy_changed_cb (GtkWidget *widget,
6021
                      GtkWidget *previous_toplevel)
6022
0
{
6023
0
    GtkWidget *toplevel = gtk_widget_get_toplevel(widget);
6024
0
    GdkWindowState old_window_state = GDK_WINDOW_STATE_WITHDRAWN;
6025
0
    GdkEventWindowState event;
6026
0
6027
0
    event.new_window_state = GDK_WINDOW_STATE_WITHDRAWN;
6028
0
6029
0
    if (GTK_IS_WINDOW(previous_toplevel)) {
6030
0
        g_signal_handlers_disconnect_by_func(previous_toplevel,
6031
0
                                             FuncToGpointer(window_state_event_cb),
6032
0
                                             widget);
6033
0
        GdkWindow *win = gtk_widget_get_window(previous_toplevel);
6034
0
        if (win) {
6035
0
            old_window_state = gdk_window_get_state(win);
6036
0
        }
6037
0
    }
6038
0
6039
0
    if (GTK_IS_WINDOW(toplevel)) {
6040
0
        g_signal_connect_swapped(toplevel, "window-state-event",
6041
0
                                 G_CALLBACK(window_state_event_cb), widget);
6042
0
        GdkWindow *win = gtk_widget_get_window(toplevel);
6043
0
        if (win) {
6044
0
            event.new_window_state = gdk_window_get_state(win);
6045
0
        }
6046
0
    }
6047
0
6048
0
    event.changed_mask = static_cast<GdkWindowState>
6049
0
        (old_window_state ^ event.new_window_state);
6050
0
6051
0
    if (event.changed_mask) {
6052
0
        event.type = GDK_WINDOW_STATE;
6053
0
        event.window = nullptr;
6054
0
        event.send_event = TRUE;
6055
0
        window_state_event_cb(widget, &event);
6056
0
    }
6057
0
}
6058
6059
static gboolean
6060
window_state_event_cb (GtkWidget *widget, GdkEventWindowState *event)
6061
0
{
6062
0
    RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
6063
0
    if (!window)
6064
0
        return FALSE;
6065
0
6066
0
    window->OnWindowStateEvent(widget, event);
6067
0
6068
0
    return FALSE;
6069
0
}
6070
6071
static void
6072
settings_changed_cb (GtkSettings *settings, GParamSpec *pspec, nsWindow *data)
6073
0
{
6074
0
    RefPtr<nsWindow> window = data;
6075
0
    window->ThemeChanged();
6076
0
}
6077
6078
static void
6079
check_resize_cb (GtkContainer* container, gpointer user_data)
6080
0
{
6081
0
    RefPtr<nsWindow> window = get_window_for_gtk_widget(GTK_WIDGET(container));
6082
0
    if (!window) {
6083
0
      return;
6084
0
    }
6085
0
    window->OnCheckResize();
6086
0
}
6087
6088
static void
6089
screen_composited_changed_cb (GdkScreen* screen, gpointer user_data)
6090
0
{
6091
0
    // This callback can run before gfxPlatform::Init() in rare
6092
0
    // cases involving the profile manager. When this happens,
6093
0
    // we have no reason to reset any compositors as graphics
6094
0
    // hasn't been initialized yet.
6095
0
    if (GPUProcessManager::Get()) {
6096
0
        GPUProcessManager::Get()->ResetCompositors();
6097
0
    }
6098
0
}
6099
6100
static void
6101
widget_composited_changed_cb (GtkWidget* widget, gpointer user_data)
6102
0
{
6103
0
    RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
6104
0
    if (!window) {
6105
0
      return;
6106
0
    }
6107
0
    window->OnCompositedChanged();
6108
0
}
6109
6110
static void
6111
scale_changed_cb (GtkWidget* widget, GParamSpec* aPSpec, gpointer aPointer)
6112
0
{
6113
0
    RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
6114
0
    if (!window) {
6115
0
      return;
6116
0
    }
6117
0
    // This eventually propagate new scale to the PuppetWidgets
6118
0
    window->OnDPIChanged();
6119
0
6120
0
    // configure_event is already fired before scale-factor signal,
6121
0
    // but size-allocate isn't fired by changing scale
6122
0
    GtkAllocation allocation;
6123
0
    gtk_widget_get_allocation(widget, &allocation);
6124
0
    window->OnSizeAllocate(&allocation);
6125
0
}
6126
6127
#if GTK_CHECK_VERSION(3,4,0)
6128
static gboolean
6129
touch_event_cb(GtkWidget* aWidget, GdkEventTouch* aEvent)
6130
0
{
6131
0
    UpdateLastInputEventTime(aEvent);
6132
0
6133
0
    nsWindow* window = GetFirstNSWindowForGDKWindow(aEvent->window);
6134
0
    if (!window) {
6135
0
        return FALSE;
6136
0
    }
6137
0
6138
0
    return window->OnTouchEvent(aEvent);
6139
0
}
6140
#endif
6141
6142
//////////////////////////////////////////////////////////////////////
6143
// These are all of our drag and drop operations
6144
6145
void
6146
nsWindow::InitDragEvent(WidgetDragEvent &aEvent)
6147
0
{
6148
0
    // set the keyboard modifiers
6149
0
    guint modifierState = KeymapWrapper::GetCurrentModifierState();
6150
0
    KeymapWrapper::InitInputEvent(aEvent, modifierState);
6151
0
}
6152
6153
gboolean
6154
WindowDragMotionHandler(GtkWidget *aWidget,
6155
                        GdkDragContext *aDragContext,
6156
                        nsWaylandDragContext *aWaylandDragContext,
6157
                        gint aX,
6158
                        gint aY,
6159
                        guint aTime)
6160
0
{
6161
0
    RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
6162
0
    if (!window)
6163
0
        return FALSE;
6164
0
6165
0
    // figure out which internal widget this drag motion actually happened on
6166
0
    nscoord retx = 0;
6167
0
    nscoord rety = 0;
6168
0
6169
0
    GdkWindow *innerWindow =
6170
0
        get_inner_gdk_window(gtk_widget_get_window(aWidget), aX, aY,
6171
0
                             &retx, &rety);
6172
0
    RefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow);
6173
0
6174
0
    if (!innerMostWindow) {
6175
0
        innerMostWindow = window;
6176
0
    }
6177
0
6178
0
    LOGDRAG(("nsWindow drag-motion signal for %p\n", (void*)innerMostWindow));
6179
0
6180
0
    LayoutDeviceIntPoint point = window->GdkPointToDevicePixels({ retx, rety });
6181
0
6182
0
    RefPtr<nsDragService> dragService = nsDragService::GetInstance();
6183
0
    return dragService->
6184
0
        ScheduleMotionEvent(innerMostWindow, aDragContext, aWaylandDragContext,
6185
0
                            point, aTime);
6186
0
}
6187
6188
static gboolean
6189
drag_motion_event_cb(GtkWidget *aWidget,
6190
                     GdkDragContext *aDragContext,
6191
                     gint aX,
6192
                     gint aY,
6193
                     guint aTime,
6194
                     gpointer aData)
6195
0
{
6196
0
    return WindowDragMotionHandler(aWidget, aDragContext, nullptr,
6197
0
                                   aX, aY, aTime);
6198
0
}
6199
6200
void
6201
WindowDragLeaveHandler(GtkWidget *aWidget)
6202
0
{
6203
0
    RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
6204
0
    if (!window)
6205
0
        return;
6206
0
6207
0
    RefPtr<nsDragService> dragService = nsDragService::GetInstance();
6208
0
6209
0
    nsWindow *mostRecentDragWindow = dragService->GetMostRecentDestWindow();
6210
0
    if (!mostRecentDragWindow) {
6211
0
        // This can happen when the target will not accept a drop.  A GTK drag
6212
0
        // source sends the leave message to the destination before the
6213
0
        // drag-failed signal on the source widget, but the leave message goes
6214
0
        // via the X server, and so doesn't get processed at least until the
6215
0
        // event loop runs again.
6216
0
        return;
6217
0
    }
6218
0
6219
0
    GtkWidget *mozContainer = mostRecentDragWindow->GetMozContainerWidget();
6220
0
    if (aWidget != mozContainer)
6221
0
    {
6222
0
        // When the drag moves between widgets, GTK can send leave signal for
6223
0
        // the old widget after the motion or drop signal for the new widget.
6224
0
        // We'll send the leave event when the motion or drop event is run.
6225
0
        return;
6226
0
    }
6227
0
6228
0
    LOGDRAG(("nsWindow drag-leave signal for %p\n",
6229
0
             (void*)mostRecentDragWindow));
6230
0
6231
0
    dragService->ScheduleLeaveEvent();
6232
0
}
6233
6234
static void
6235
drag_leave_event_cb(GtkWidget *aWidget,
6236
                    GdkDragContext *aDragContext,
6237
                    guint aTime,
6238
                    gpointer aData)
6239
0
{
6240
0
    WindowDragLeaveHandler(aWidget);
6241
0
}
6242
6243
gboolean
6244
WindowDragDropHandler(GtkWidget *aWidget,
6245
                      GdkDragContext *aDragContext,
6246
                      nsWaylandDragContext *aWaylandDragContext,
6247
                      gint aX,
6248
                      gint aY,
6249
                      guint aTime)
6250
0
{
6251
0
    RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
6252
0
    if (!window)
6253
0
        return FALSE;
6254
0
6255
0
    // figure out which internal widget this drag motion actually happened on
6256
0
    nscoord retx = 0;
6257
0
    nscoord rety = 0;
6258
0
6259
0
    GdkWindow *innerWindow =
6260
0
        get_inner_gdk_window(gtk_widget_get_window(aWidget), aX, aY,
6261
0
                             &retx, &rety);
6262
0
    RefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow);
6263
0
6264
0
    if (!innerMostWindow) {
6265
0
        innerMostWindow = window;
6266
0
    }
6267
0
6268
0
    LOGDRAG(("nsWindow drag-drop signal for %p\n", (void*)innerMostWindow));
6269
0
6270
0
    LayoutDeviceIntPoint point = window->GdkPointToDevicePixels({ retx, rety });
6271
0
6272
0
    RefPtr<nsDragService> dragService = nsDragService::GetInstance();
6273
0
    return dragService->
6274
0
        ScheduleDropEvent(innerMostWindow, aDragContext, aWaylandDragContext,
6275
0
                          point, aTime);
6276
0
}
6277
6278
static gboolean
6279
drag_drop_event_cb(GtkWidget *aWidget,
6280
                   GdkDragContext *aDragContext,
6281
                   gint aX,
6282
                   gint aY,
6283
                   guint aTime,
6284
                   gpointer aData)
6285
0
{
6286
0
    return WindowDragDropHandler(aWidget, aDragContext, nullptr, aX, aY, aTime);
6287
0
}
6288
6289
static void
6290
drag_data_received_event_cb(GtkWidget *aWidget,
6291
                            GdkDragContext *aDragContext,
6292
                            gint aX,
6293
                            gint aY,
6294
                            GtkSelectionData  *aSelectionData,
6295
                            guint aInfo,
6296
                            guint aTime,
6297
                            gpointer aData)
6298
0
{
6299
0
    RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
6300
0
    if (!window)
6301
0
        return;
6302
0
6303
0
    window->OnDragDataReceivedEvent(aWidget,
6304
0
                                    aDragContext,
6305
0
                                    aX, aY,
6306
0
                                    aSelectionData,
6307
0
                                    aInfo, aTime, aData);
6308
0
}
6309
6310
static nsresult
6311
initialize_prefs(void)
6312
0
{
6313
0
    gRaiseWindows =
6314
0
        Preferences::GetBool("mozilla.widget.raise-on-setfocus", true);
6315
0
6316
0
    return NS_OK;
6317
0
}
6318
6319
static GdkWindow *
6320
get_inner_gdk_window (GdkWindow *aWindow,
6321
                      gint x, gint y,
6322
                      gint *retx, gint *rety)
6323
0
{
6324
0
    gint cx, cy, cw, ch;
6325
0
    GList *children = gdk_window_peek_children(aWindow);
6326
0
    for (GList *child = g_list_last(children);
6327
0
         child;
6328
0
         child = g_list_previous(child)) {
6329
0
        auto *childWindow = (GdkWindow *) child->data;
6330
0
        if (get_window_for_gdk_window(childWindow)) {
6331
0
            gdk_window_get_geometry(childWindow, &cx, &cy, &cw, &ch);
6332
0
            if ((cx < x) && (x < (cx + cw)) &&
6333
0
                (cy < y) && (y < (cy + ch)) &&
6334
0
                gdk_window_is_visible(childWindow)) {
6335
0
                return get_inner_gdk_window(childWindow,
6336
0
                                            x - cx, y - cy,
6337
0
                                            retx, rety);
6338
0
            }
6339
0
        }
6340
0
    }
6341
0
    *retx = x;
6342
0
    *rety = y;
6343
0
    return aWindow;
6344
0
}
6345
6346
static int
6347
is_parent_ungrab_enter(GdkEventCrossing *aEvent)
6348
0
{
6349
0
    return (GDK_CROSSING_UNGRAB == aEvent->mode) &&
6350
0
        ((GDK_NOTIFY_ANCESTOR == aEvent->detail) ||
6351
0
         (GDK_NOTIFY_VIRTUAL == aEvent->detail));
6352
0
6353
0
}
6354
6355
static int
6356
is_parent_grab_leave(GdkEventCrossing *aEvent)
6357
0
{
6358
0
    return (GDK_CROSSING_GRAB == aEvent->mode) &&
6359
0
        ((GDK_NOTIFY_ANCESTOR == aEvent->detail) ||
6360
0
            (GDK_NOTIFY_VIRTUAL == aEvent->detail));
6361
0
}
6362
6363
#ifdef ACCESSIBILITY
6364
void
6365
nsWindow::CreateRootAccessible()
6366
0
{
6367
0
    if (mIsTopLevel && !mRootAccessible) {
6368
0
        LOG(("nsWindow:: Create Toplevel Accessibility\n"));
6369
0
        mRootAccessible = GetRootAccessible();
6370
0
    }
6371
0
}
6372
6373
void
6374
nsWindow::DispatchEventToRootAccessible(uint32_t aEventType)
6375
0
{
6376
0
    if (!a11y::ShouldA11yBeEnabled()) {
6377
0
        return;
6378
0
    }
6379
0
6380
0
    nsAccessibilityService* accService = GetOrCreateAccService();
6381
0
    if (!accService) {
6382
0
        return;
6383
0
    }
6384
0
6385
0
    // Get the root document accessible and fire event to it.
6386
0
    a11y::Accessible* acc = GetRootAccessible();
6387
0
    if (acc) {
6388
0
        accService->FireAccessibleEvent(aEventType, acc);
6389
0
    }
6390
0
}
6391
6392
void
6393
nsWindow::DispatchActivateEventAccessible(void)
6394
0
{
6395
0
    DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_ACTIVATE);
6396
0
}
6397
6398
void
6399
nsWindow::DispatchDeactivateEventAccessible(void)
6400
0
{
6401
0
    DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_DEACTIVATE);
6402
0
}
6403
6404
void
6405
nsWindow::DispatchMaximizeEventAccessible(void)
6406
0
{
6407
0
    DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_MAXIMIZE);
6408
0
}
6409
6410
void
6411
nsWindow::DispatchMinimizeEventAccessible(void)
6412
0
{
6413
0
    DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_MINIMIZE);
6414
0
}
6415
6416
void
6417
nsWindow::DispatchRestoreEventAccessible(void)
6418
0
{
6419
0
    DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_RESTORE);
6420
0
}
6421
6422
#endif /* #ifdef ACCESSIBILITY */
6423
6424
void
6425
nsWindow::SetInputContext(const InputContext& aContext,
6426
                          const InputContextAction& aAction)
6427
0
{
6428
0
    if (!mIMContext) {
6429
0
        return;
6430
0
    }
6431
0
    mIMContext->SetInputContext(this, &aContext, &aAction);
6432
0
}
6433
6434
InputContext
6435
nsWindow::GetInputContext()
6436
0
{
6437
0
  InputContext context;
6438
0
  if (!mIMContext) {
6439
0
      context.mIMEState.mEnabled = IMEState::DISABLED;
6440
0
      context.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED;
6441
0
  } else {
6442
0
      context = mIMContext->GetInputContext();
6443
0
  }
6444
0
  return context;
6445
0
}
6446
6447
TextEventDispatcherListener*
6448
nsWindow::GetNativeTextEventDispatcherListener()
6449
0
{
6450
0
    if (NS_WARN_IF(!mIMContext)) {
6451
0
        return nullptr;
6452
0
    }
6453
0
    return mIMContext;
6454
0
}
6455
6456
void
6457
nsWindow::GetEditCommandsRemapped(NativeKeyBindingsType aType,
6458
                                  const WidgetKeyboardEvent& aEvent,
6459
                                  nsTArray<CommandInt>& aCommands,
6460
                                  uint32_t aGeckoKeyCode,
6461
                                  uint32_t aNativeKeyCode)
6462
0
{
6463
0
    // If aEvent.mNativeKeyEvent is nullptr, the event was created by chrome
6464
0
    // script.  In such case, we shouldn't expose the OS settings to it.
6465
0
    // So, just ignore such events here.
6466
0
    if (!aEvent.mNativeKeyEvent) {
6467
0
        return;
6468
0
    }
6469
0
    WidgetKeyboardEvent modifiedEvent(aEvent);
6470
0
    modifiedEvent.mKeyCode = aGeckoKeyCode;
6471
0
    static_cast<GdkEventKey*>(modifiedEvent.mNativeKeyEvent)->keyval =
6472
0
        aNativeKeyCode;
6473
0
6474
0
    NativeKeyBindings* keyBindings = NativeKeyBindings::GetInstance(aType);
6475
0
    keyBindings->GetEditCommands(modifiedEvent, aCommands);
6476
0
}
6477
6478
void
6479
nsWindow::GetEditCommands(NativeKeyBindingsType aType,
6480
                          const WidgetKeyboardEvent& aEvent,
6481
                          nsTArray<CommandInt>& aCommands)
6482
0
{
6483
0
    // Validate the arguments.
6484
0
    nsIWidget::GetEditCommands(aType, aEvent, aCommands);
6485
0
6486
0
    if (aEvent.mKeyCode >= NS_VK_LEFT && aEvent.mKeyCode <= NS_VK_DOWN) {
6487
0
        // Check if we're targeting content with vertical writing mode,
6488
0
        // and if so remap the arrow keys.
6489
0
        // XXX This may be expensive.
6490
0
        WidgetQueryContentEvent query(true, eQuerySelectedText, this);
6491
0
        nsEventStatus status;
6492
0
        DispatchEvent(&query, status);
6493
0
6494
0
        if (query.mSucceeded && query.mReply.mWritingMode.IsVertical()) {
6495
0
            uint32_t geckoCode = 0;
6496
0
            uint32_t gdkCode = 0;
6497
0
            switch (aEvent.mKeyCode) {
6498
0
            case NS_VK_LEFT:
6499
0
                if (query.mReply.mWritingMode.IsVerticalLR()) {
6500
0
                    geckoCode = NS_VK_UP;
6501
0
                    gdkCode = GDK_Up;
6502
0
                } else {
6503
0
                    geckoCode = NS_VK_DOWN;
6504
0
                    gdkCode = GDK_Down;
6505
0
                }
6506
0
                break;
6507
0
6508
0
            case NS_VK_RIGHT:
6509
0
                if (query.mReply.mWritingMode.IsVerticalLR()) {
6510
0
                    geckoCode = NS_VK_DOWN;
6511
0
                    gdkCode = GDK_Down;
6512
0
                } else {
6513
0
                    geckoCode = NS_VK_UP;
6514
0
                    gdkCode = GDK_Up;
6515
0
                }
6516
0
                break;
6517
0
6518
0
            case NS_VK_UP:
6519
0
                geckoCode = NS_VK_LEFT;
6520
0
                gdkCode = GDK_Left;
6521
0
                break;
6522
0
6523
0
            case NS_VK_DOWN:
6524
0
                geckoCode = NS_VK_RIGHT;
6525
0
                gdkCode = GDK_Right;
6526
0
                break;
6527
0
            }
6528
0
6529
0
            GetEditCommandsRemapped(aType, aEvent, aCommands,
6530
0
                                    geckoCode, gdkCode);
6531
0
            return;
6532
0
        }
6533
0
    }
6534
0
6535
0
    NativeKeyBindings* keyBindings = NativeKeyBindings::GetInstance(aType);
6536
0
    keyBindings->GetEditCommands(aEvent, aCommands);
6537
0
}
6538
6539
already_AddRefed<DrawTarget>
6540
nsWindow::StartRemoteDrawingInRegion(LayoutDeviceIntRegion& aInvalidRegion, BufferMode* aBufferMode)
6541
0
{
6542
0
  return mSurfaceProvider.StartRemoteDrawingInRegion(aInvalidRegion, aBufferMode);
6543
0
}
6544
6545
void
6546
nsWindow::EndRemoteDrawingInRegion(DrawTarget* aDrawTarget,
6547
                                   LayoutDeviceIntRegion& aInvalidRegion)
6548
0
{
6549
0
  mSurfaceProvider.EndRemoteDrawingInRegion(aDrawTarget, aInvalidRegion);
6550
0
}
6551
6552
// Code shared begin BeginMoveDrag and BeginResizeDrag
6553
bool
6554
nsWindow::GetDragInfo(WidgetMouseEvent* aMouseEvent,
6555
                      GdkWindow** aWindow, gint* aButton,
6556
                      gint* aRootX, gint* aRootY)
6557
0
{
6558
0
    if (aMouseEvent->button != WidgetMouseEvent::eLeftButton) {
6559
0
        // we can only begin a move drag with the left mouse button
6560
0
        return false;
6561
0
    }
6562
0
    *aButton = 1;
6563
0
6564
0
    // get the gdk window for this widget
6565
0
    GdkWindow* gdk_window = mGdkWindow;
6566
0
    if (!gdk_window) {
6567
0
        return false;
6568
0
    }
6569
#ifdef DEBUG
6570
    // GDK_IS_WINDOW(...) expands to a statement-expression, and
6571
    // statement-expressions are not allowed in template-argument lists. So we
6572
    // have to make the MOZ_ASSERT condition indirect.
6573
    if (!GDK_IS_WINDOW(gdk_window)) {
6574
        MOZ_ASSERT(false, "must really be window");
6575
    }
6576
#endif
6577
6578
0
    // find the top-level window
6579
0
    gdk_window = gdk_window_get_toplevel(gdk_window);
6580
0
    MOZ_ASSERT(gdk_window,
6581
0
               "gdk_window_get_toplevel should not return null");
6582
0
    *aWindow = gdk_window;
6583
0
6584
0
    if (!aMouseEvent->mWidget) {
6585
0
        return false;
6586
0
    }
6587
0
6588
0
    if (mIsX11Display) {
6589
0
      // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=789054
6590
0
      // To avoid crashes disable double-click on WM without _NET_WM_MOVERESIZE.
6591
0
      // See _should_perform_ewmh_drag() at gdkwindow-x11.c
6592
0
      GdkScreen* screen = gdk_window_get_screen(gdk_window);
6593
0
      GdkAtom atom = gdk_atom_intern("_NET_WM_MOVERESIZE", FALSE);
6594
0
      if (!gdk_x11_screen_supports_net_wm_hint(screen, atom)) {
6595
0
          static unsigned int lastTimeStamp = 0;
6596
0
          if (lastTimeStamp != aMouseEvent->mTime) {
6597
0
              lastTimeStamp = aMouseEvent->mTime;
6598
0
          } else {
6599
0
              return false;
6600
0
          }
6601
0
      }
6602
0
    }
6603
0
6604
0
    // FIXME: It would be nice to have the widget position at the time
6605
0
    // of the event, but it's relatively unlikely that the widget has
6606
0
    // moved since the mousedown.  (On the other hand, it's quite likely
6607
0
    // that the mouse has moved, which is why we use the mouse position
6608
0
    // from the event.)
6609
0
    LayoutDeviceIntPoint offset = aMouseEvent->mWidget->WidgetToScreenOffset();
6610
0
    *aRootX = aMouseEvent->mRefPoint.x + offset.x;
6611
0
    *aRootY = aMouseEvent->mRefPoint.y + offset.y;
6612
0
6613
0
    return true;
6614
0
}
6615
6616
nsresult
6617
nsWindow::BeginMoveDrag(WidgetMouseEvent* aEvent)
6618
0
{
6619
0
    MOZ_ASSERT(aEvent, "must have event");
6620
0
    MOZ_ASSERT(aEvent->mClass == eMouseEventClass,
6621
0
               "event must have correct struct type");
6622
0
6623
0
    GdkWindow *gdk_window;
6624
0
    gint button, screenX, screenY;
6625
0
    if (!GetDragInfo(aEvent, &gdk_window, &button, &screenX, &screenY)) {
6626
0
        return NS_ERROR_FAILURE;
6627
0
    }
6628
0
6629
0
    // tell the window manager to start the move
6630
0
    screenX = DevicePixelsToGdkCoordRoundDown(screenX);
6631
0
    screenY = DevicePixelsToGdkCoordRoundDown(screenY);
6632
0
    gdk_window_begin_move_drag(gdk_window, button, screenX, screenY,
6633
0
                               aEvent->mTime);
6634
0
6635
0
    return NS_OK;
6636
0
}
6637
6638
nsresult
6639
nsWindow::BeginResizeDrag(WidgetGUIEvent* aEvent,
6640
                          int32_t aHorizontal,
6641
                          int32_t aVertical)
6642
0
{
6643
0
    NS_ENSURE_ARG_POINTER(aEvent);
6644
0
6645
0
    if (aEvent->mClass != eMouseEventClass) {
6646
0
        // you can only begin a resize drag with a mouse event
6647
0
        return NS_ERROR_INVALID_ARG;
6648
0
    }
6649
0
6650
0
    GdkWindow *gdk_window;
6651
0
    gint button, screenX, screenY;
6652
0
    if (!GetDragInfo(aEvent->AsMouseEvent(), &gdk_window, &button,
6653
0
                     &screenX, &screenY)) {
6654
0
        return NS_ERROR_FAILURE;
6655
0
    }
6656
0
6657
0
    // work out what GdkWindowEdge we're talking about
6658
0
    GdkWindowEdge window_edge;
6659
0
    if (aVertical < 0) {
6660
0
        if (aHorizontal < 0) {
6661
0
            window_edge = GDK_WINDOW_EDGE_NORTH_WEST;
6662
0
        } else if (aHorizontal == 0) {
6663
0
            window_edge = GDK_WINDOW_EDGE_NORTH;
6664
0
        } else {
6665
0
            window_edge = GDK_WINDOW_EDGE_NORTH_EAST;
6666
0
        }
6667
0
    } else if (aVertical == 0) {
6668
0
        if (aHorizontal < 0) {
6669
0
            window_edge = GDK_WINDOW_EDGE_WEST;
6670
0
        } else if (aHorizontal == 0) {
6671
0
            return NS_ERROR_INVALID_ARG;
6672
0
        } else {
6673
0
            window_edge = GDK_WINDOW_EDGE_EAST;
6674
0
        }
6675
0
    } else {
6676
0
        if (aHorizontal < 0) {
6677
0
            window_edge = GDK_WINDOW_EDGE_SOUTH_WEST;
6678
0
        } else if (aHorizontal == 0) {
6679
0
            window_edge = GDK_WINDOW_EDGE_SOUTH;
6680
0
        } else {
6681
0
            window_edge = GDK_WINDOW_EDGE_SOUTH_EAST;
6682
0
        }
6683
0
    }
6684
0
6685
0
    // tell the window manager to start the resize
6686
0
    gdk_window_begin_resize_drag(gdk_window, window_edge, button,
6687
0
                                 screenX, screenY, aEvent->mTime);
6688
0
6689
0
    return NS_OK;
6690
0
}
6691
6692
nsIWidget::LayerManager*
6693
nsWindow::GetLayerManager(PLayerTransactionChild* aShadowManager,
6694
                          LayersBackend aBackendHint,
6695
                          LayerManagerPersistence aPersistence)
6696
0
{
6697
0
    if (mIsDestroyed) {
6698
0
      // Prevent external code from triggering the re-creation of the LayerManager/Compositor
6699
0
      // during shutdown. Just return what we currently have, which is most likely null.
6700
0
      return mLayerManager;
6701
0
    }
6702
0
6703
0
    return nsBaseWidget::GetLayerManager(aShadowManager, aBackendHint, aPersistence);
6704
0
}
6705
6706
void
6707
nsWindow::SetCompositorWidgetDelegate(CompositorWidgetDelegate* delegate)
6708
0
{
6709
0
    if (delegate) {
6710
0
        mCompositorWidgetDelegate = delegate->AsPlatformSpecificDelegate();
6711
0
        MOZ_ASSERT(mCompositorWidgetDelegate,
6712
0
                   "nsWindow::SetCompositorWidgetDelegate called with a non-PlatformCompositorWidgetDelegate");
6713
0
    } else {
6714
0
        mCompositorWidgetDelegate = nullptr;
6715
0
    }
6716
0
}
6717
6718
void
6719
nsWindow::ClearCachedResources()
6720
0
{
6721
0
    if (mLayerManager &&
6722
0
        mLayerManager->GetBackendType() == mozilla::layers::LayersBackend::LAYERS_BASIC) {
6723
0
        mLayerManager->ClearCachedResources();
6724
0
    }
6725
0
6726
0
    GList* children = gdk_window_peek_children(mGdkWindow);
6727
0
    for (GList* list = children; list; list = list->next) {
6728
0
        nsWindow* window = get_window_for_gdk_window(GDK_WINDOW(list->data));
6729
0
        if (window) {
6730
0
            window->ClearCachedResources();
6731
0
        }
6732
0
    }
6733
0
}
6734
6735
/* nsWindow::UpdateClientOffsetForCSDWindow() is designed to be called from
6736
 * paint code to update mClientOffset any time. It also propagates
6737
 * the mClientOffset to child tabs.
6738
 *
6739
 * It works only for CSD decorated GtkWindow.
6740
 */
6741
void
6742
nsWindow::UpdateClientOffsetForCSDWindow()
6743
0
{
6744
0
    // We update window offset on X11 as the window position is calculated
6745
0
    // relatively to mShell. We don't do that on Wayland as our wl_subsurface
6746
0
    // is attached to mContainer and mShell is ignored.
6747
0
    if (!mIsX11Display) {
6748
0
        return;
6749
0
    }
6750
0
6751
0
    // _NET_FRAME_EXTENTS is not set on client decorated windows,
6752
0
    // so we need to read offset between mContainer and toplevel mShell
6753
0
    // window.
6754
0
    if (mSizeState == nsSizeMode_Normal) {
6755
0
        GtkBorder decorationSize;
6756
0
        GetCSDDecorationSize(GTK_WINDOW(mShell), &decorationSize);
6757
0
        mClientOffset = nsIntPoint(decorationSize.left, decorationSize.top);
6758
0
    } else {
6759
0
        mClientOffset = nsIntPoint(0, 0);
6760
0
    }
6761
0
6762
0
    // Send a WindowMoved notification. This ensures that TabParent
6763
0
    // picks up the new client offset and sends it to the child process
6764
0
    // if appropriate.
6765
0
    NotifyWindowMoved(mBounds.x, mBounds.y);
6766
0
}
6767
6768
nsresult
6769
nsWindow::SetNonClientMargins(LayoutDeviceIntMargin &aMargins)
6770
0
{
6771
0
    SetDrawsInTitlebar(aMargins.top == 0);
6772
0
    return NS_OK;
6773
0
}
6774
6775
void
6776
nsWindow::SetDrawsInTitlebar(bool aState)
6777
0
{
6778
0
    if (!mShell ||
6779
0
        mCSDSupportLevel == CSD_SUPPORT_NONE ||
6780
0
        aState == mDrawInTitlebar) {
6781
0
        return;
6782
0
    }
6783
0
6784
0
    if (mCSDSupportLevel == CSD_SUPPORT_SYSTEM) {
6785
0
        SetWindowDecoration(aState ? eBorderStyle_border : mBorderStyle);
6786
0
    }
6787
0
    else if (mCSDSupportLevel == CSD_SUPPORT_CLIENT) {
6788
0
        /* Window manager does not support GDK_DECOR_BORDER,
6789
0
         * emulate it by CSD.
6790
0
         *
6791
0
         * gtk_window_set_titlebar() works on unrealized widgets only,
6792
0
         * we need to handle mShell carefully here.
6793
0
         * When CSD is enabled mGdkWindow is owned by mContainer which is good
6794
0
         * as we can't delete our mGdkWindow. To make mShell unrealized while
6795
0
         * mContainer is preserved we temporary reparent mContainer to an
6796
0
         * invisible GtkWindow.
6797
0
         */
6798
0
        NativeShow(false);
6799
0
6800
0
        // Using GTK_WINDOW_POPUP rather than
6801
0
        // GTK_WINDOW_TOPLEVEL in the hope that POPUP results in less
6802
0
        // initialization and window manager interaction.
6803
0
        GtkWidget* tmpWindow = gtk_window_new(GTK_WINDOW_POPUP);
6804
0
        gtk_widget_realize(tmpWindow);
6805
0
6806
0
        gtk_widget_reparent(GTK_WIDGET(mContainer), tmpWindow);
6807
0
        gtk_widget_unrealize(GTK_WIDGET(mShell));
6808
0
6809
0
        // Available as of GTK 3.10+
6810
0
        static auto sGtkWindowSetTitlebar = (void (*)(GtkWindow*, GtkWidget*))
6811
0
            dlsym(RTLD_DEFAULT, "gtk_window_set_titlebar");
6812
0
        MOZ_ASSERT(sGtkWindowSetTitlebar,
6813
0
            "Missing gtk_window_set_titlebar(), old Gtk+ library?");
6814
0
6815
0
        if (aState) {
6816
0
            // Add a hidden titlebar widget to trigger CSD, but disable the default
6817
0
            // titlebar.  GtkFixed is a somewhat random choice for a simple unused
6818
0
            // widget. gtk_window_set_titlebar() takes ownership of the titlebar
6819
0
            // widget.
6820
0
            sGtkWindowSetTitlebar(GTK_WINDOW(mShell), gtk_fixed_new());
6821
0
        } else {
6822
0
            sGtkWindowSetTitlebar(GTK_WINDOW(mShell), nullptr);
6823
0
        }
6824
0
6825
0
        /* A workaround for https://bugzilla.gnome.org/show_bug.cgi?id=791081
6826
0
         * gtk_widget_realize() throws:
6827
0
         * "In pixman_region32_init_rect: Invalid rectangle passed"
6828
0
         * when mShell has default 1x1 size.
6829
0
         */
6830
0
        GtkAllocation allocation = {0, 0, 0, 0};
6831
0
        gtk_widget_get_preferred_width(GTK_WIDGET(mShell), nullptr,
6832
0
                                       &allocation.width);
6833
0
        gtk_widget_get_preferred_height(GTK_WIDGET(mShell), nullptr,
6834
0
                                        &allocation.height);
6835
0
        gtk_widget_size_allocate(GTK_WIDGET(mShell), &allocation);
6836
0
6837
0
        gtk_widget_realize(GTK_WIDGET(mShell));
6838
0
        gtk_widget_reparent(GTK_WIDGET(mContainer), GTK_WIDGET(mShell));
6839
0
        mNeedsShow = true;
6840
0
        NativeResize();
6841
0
6842
0
        // Label mShell toplevel window so property_notify_event_cb callback
6843
0
        // can find its way home.
6844
0
        g_object_set_data(G_OBJECT(gtk_widget_get_window(mShell)),
6845
0
                          "nsWindow", this);
6846
0
#ifdef MOZ_X11
6847
0
        SetCompositorHint(GTK_WIDGET_COMPOSIDED_ENABLED);
6848
0
#endif
6849
0
        RefreshWindowClass();
6850
0
6851
0
        // When we use system titlebar setup managed by Gtk+ we also get
6852
0
        // _NET_FRAME_EXTENTS property for our toplevel window so we can't
6853
0
        // update the client offset it here.
6854
0
        if (aState) {
6855
0
            UpdateClientOffsetForCSDWindow();
6856
0
        }
6857
0
6858
0
        gtk_widget_destroy(tmpWindow);
6859
0
    }
6860
0
6861
0
    mDrawInTitlebar = aState;
6862
0
}
6863
6864
gint
6865
nsWindow::GdkScaleFactor()
6866
0
{
6867
0
#if (MOZ_WIDGET_GTK >= 3)
6868
0
    // Available as of GTK 3.10+
6869
0
    static auto sGdkWindowGetScaleFactorPtr = (gint (*)(GdkWindow*))
6870
0
        dlsym(RTLD_DEFAULT, "gdk_window_get_scale_factor");
6871
0
    if (sGdkWindowGetScaleFactorPtr && mGdkWindow)
6872
0
        return (*sGdkWindowGetScaleFactorPtr)(mGdkWindow);
6873
0
#endif
6874
0
    return ScreenHelperGTK::GetGTKMonitorScaleFactor();
6875
0
}
6876
6877
6878
gint
6879
0
nsWindow::DevicePixelsToGdkCoordRoundUp(int pixels) {
6880
0
    gint scale = GdkScaleFactor();
6881
0
    return (pixels + scale - 1) / scale;
6882
0
}
6883
6884
gint
6885
0
nsWindow::DevicePixelsToGdkCoordRoundDown(int pixels) {
6886
0
    gint scale = GdkScaleFactor();
6887
0
    return pixels / scale;
6888
0
}
6889
6890
GdkPoint
6891
0
nsWindow::DevicePixelsToGdkPointRoundDown(LayoutDeviceIntPoint point) {
6892
0
    gint scale = GdkScaleFactor();
6893
0
    return { point.x / scale, point.y / scale };
6894
0
}
6895
6896
GdkRectangle
6897
0
nsWindow::DevicePixelsToGdkRectRoundOut(LayoutDeviceIntRect rect) {
6898
0
    gint scale = GdkScaleFactor();
6899
0
    int x = rect.x / scale;
6900
0
    int y = rect.y / scale;
6901
0
    int right = (rect.x + rect.width + scale - 1) / scale;
6902
0
    int bottom = (rect.y + rect.height + scale - 1) / scale;
6903
0
    return { x, y, right - x, bottom - y };
6904
0
}
6905
6906
GdkRectangle
6907
0
nsWindow::DevicePixelsToGdkSizeRoundUp(LayoutDeviceIntSize pixelSize) {
6908
0
    gint scale = GdkScaleFactor();
6909
0
    gint width = (pixelSize.width + scale - 1) / scale;
6910
0
    gint height = (pixelSize.height + scale - 1) / scale;
6911
0
    return { 0, 0, width, height };
6912
0
}
6913
6914
int
6915
0
nsWindow::GdkCoordToDevicePixels(gint coord) {
6916
0
    return coord * GdkScaleFactor();
6917
0
}
6918
6919
LayoutDeviceIntPoint
6920
nsWindow::GdkEventCoordsToDevicePixels(gdouble x, gdouble y)
6921
0
{
6922
0
    gint scale = GdkScaleFactor();
6923
0
    return LayoutDeviceIntPoint::Round(x * scale, y * scale);
6924
0
}
6925
6926
LayoutDeviceIntPoint
6927
0
nsWindow::GdkPointToDevicePixels(GdkPoint point) {
6928
0
    gint scale = GdkScaleFactor();
6929
0
    return LayoutDeviceIntPoint(point.x * scale,
6930
0
                                point.y * scale);
6931
0
}
6932
6933
LayoutDeviceIntRect
6934
0
nsWindow::GdkRectToDevicePixels(GdkRectangle rect) {
6935
0
    gint scale = GdkScaleFactor();
6936
0
    return LayoutDeviceIntRect(rect.x * scale,
6937
0
                               rect.y * scale,
6938
0
                               rect.width * scale,
6939
0
                               rect.height * scale);
6940
0
}
6941
6942
nsresult
6943
nsWindow::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
6944
                                     uint32_t aNativeMessage,
6945
                                     uint32_t aModifierFlags,
6946
                                     nsIObserver* aObserver)
6947
0
{
6948
0
  AutoObserverNotifier notifier(aObserver, "mouseevent");
6949
0
6950
0
  if (!mGdkWindow) {
6951
0
    return NS_OK;
6952
0
  }
6953
0
6954
0
  GdkDisplay* display = gdk_window_get_display(mGdkWindow);
6955
0
6956
0
  // When a button-press/release event is requested, create it here and put it in the
6957
0
  // event queue. This will not emit a motion event - this needs to be done
6958
0
  // explicitly *before* requesting a button-press/release. You will also need to wait
6959
0
  // for the motion event to be dispatched before requesting a button-press/release
6960
0
  // event to maintain the desired event order.
6961
0
  if (aNativeMessage == GDK_BUTTON_PRESS || aNativeMessage == GDK_BUTTON_RELEASE) {
6962
0
    GdkEvent event;
6963
0
    memset(&event, 0, sizeof(GdkEvent));
6964
0
    event.type = (GdkEventType)aNativeMessage;
6965
0
    event.button.button = 1;
6966
0
    event.button.window = mGdkWindow;
6967
0
    event.button.time = GDK_CURRENT_TIME;
6968
0
6969
0
    // Get device for event source
6970
0
    GdkDeviceManager *device_manager = gdk_display_get_device_manager(display);
6971
0
    event.button.device = gdk_device_manager_get_client_pointer(device_manager);
6972
0
6973
0
    event.button.x_root = DevicePixelsToGdkCoordRoundDown(aPoint.x);
6974
0
    event.button.y_root = DevicePixelsToGdkCoordRoundDown(aPoint.y);
6975
0
6976
0
    LayoutDeviceIntPoint pointInWindow = aPoint - WidgetToScreenOffset();
6977
0
    event.button.x = DevicePixelsToGdkCoordRoundDown(pointInWindow.x);
6978
0
    event.button.y = DevicePixelsToGdkCoordRoundDown(pointInWindow.y);
6979
0
6980
0
    gdk_event_put(&event);
6981
0
  } else {
6982
0
    // We don't support specific events other than button-press/release. In all
6983
0
    // other cases we'll synthesize a motion event that will be emitted by
6984
0
    // gdk_display_warp_pointer().
6985
0
    GdkScreen* screen = gdk_window_get_screen(mGdkWindow);
6986
0
    GdkPoint point = DevicePixelsToGdkPointRoundDown(aPoint);
6987
0
    gdk_display_warp_pointer(display, screen, point.x, point.y);
6988
0
  }
6989
0
6990
0
  return NS_OK;
6991
0
}
6992
6993
nsresult
6994
nsWindow::SynthesizeNativeMouseScrollEvent(mozilla::LayoutDeviceIntPoint aPoint,
6995
                                           uint32_t aNativeMessage,
6996
                                           double aDeltaX,
6997
                                           double aDeltaY,
6998
                                           double aDeltaZ,
6999
                                           uint32_t aModifierFlags,
7000
                                           uint32_t aAdditionalFlags,
7001
                                           nsIObserver* aObserver)
7002
0
{
7003
0
  AutoObserverNotifier notifier(aObserver, "mousescrollevent");
7004
0
7005
0
  if (!mGdkWindow) {
7006
0
    return NS_OK;
7007
0
  }
7008
0
7009
0
  GdkEvent event;
7010
0
  memset(&event, 0, sizeof(GdkEvent));
7011
0
  event.type = GDK_SCROLL;
7012
0
  event.scroll.window = mGdkWindow;
7013
0
  event.scroll.time = GDK_CURRENT_TIME;
7014
0
  // Get device for event source
7015
0
  GdkDisplay* display = gdk_window_get_display(mGdkWindow);
7016
0
  GdkDeviceManager *device_manager = gdk_display_get_device_manager(display);
7017
0
  event.scroll.device = gdk_device_manager_get_client_pointer(device_manager);
7018
0
  event.scroll.x_root = DevicePixelsToGdkCoordRoundDown(aPoint.x);
7019
0
  event.scroll.y_root = DevicePixelsToGdkCoordRoundDown(aPoint.y);
7020
0
7021
0
  LayoutDeviceIntPoint pointInWindow = aPoint - WidgetToScreenOffset();
7022
0
  event.scroll.x = DevicePixelsToGdkCoordRoundDown(pointInWindow.x);
7023
0
  event.scroll.y = DevicePixelsToGdkCoordRoundDown(pointInWindow.y);
7024
0
7025
0
  // The delta values are backwards on Linux compared to Windows and Cocoa,
7026
0
  // hence the negation.
7027
0
#if GTK_CHECK_VERSION(3,4,0)
7028
0
  // TODO: is this correct? I don't have GTK 3.4+ so I can't check
7029
0
  event.scroll.direction = GDK_SCROLL_SMOOTH;
7030
0
  event.scroll.delta_x = -aDeltaX;
7031
0
  event.scroll.delta_y = -aDeltaY;
7032
#else
7033
  if (aDeltaX < 0) {
7034
    event.scroll.direction = GDK_SCROLL_RIGHT;
7035
  } else if (aDeltaX > 0) {
7036
    event.scroll.direction = GDK_SCROLL_LEFT;
7037
  } else if (aDeltaY < 0) {
7038
    event.scroll.direction = GDK_SCROLL_DOWN;
7039
  } else if (aDeltaY > 0) {
7040
    event.scroll.direction = GDK_SCROLL_UP;
7041
  } else {
7042
    return NS_OK;
7043
  }
7044
#endif
7045
7046
0
  gdk_event_put(&event);
7047
0
7048
0
  return NS_OK;
7049
0
}
7050
7051
#if GTK_CHECK_VERSION(3,4,0)
7052
nsresult
7053
nsWindow::SynthesizeNativeTouchPoint(uint32_t aPointerId,
7054
                                     TouchPointerState aPointerState,
7055
                                     LayoutDeviceIntPoint aPoint,
7056
                                     double aPointerPressure,
7057
                                     uint32_t aPointerOrientation,
7058
                                     nsIObserver* aObserver)
7059
0
{
7060
0
  AutoObserverNotifier notifier(aObserver, "touchpoint");
7061
0
7062
0
  if (!mGdkWindow) {
7063
0
    return NS_OK;
7064
0
  }
7065
0
7066
0
  GdkEvent event;
7067
0
  memset(&event, 0, sizeof(GdkEvent));
7068
0
7069
0
  static std::map<uint32_t, GdkEventSequence*> sKnownPointers;
7070
0
7071
0
  auto result = sKnownPointers.find(aPointerId);
7072
0
  switch (aPointerState) {
7073
0
  case TOUCH_CONTACT:
7074
0
    if (result == sKnownPointers.end()) {
7075
0
      // GdkEventSequence isn't a thing we can instantiate, and never gets
7076
0
      // dereferenced in the gtk code. It's an opaque pointer, the only
7077
0
      // requirement is that it be distinct from other instances of
7078
0
      // GdkEventSequence*.
7079
0
      event.touch.sequence = (GdkEventSequence*)((uintptr_t)aPointerId);
7080
0
      sKnownPointers[aPointerId] = event.touch.sequence;
7081
0
      event.type = GDK_TOUCH_BEGIN;
7082
0
    } else {
7083
0
      event.touch.sequence = result->second;
7084
0
      event.type = GDK_TOUCH_UPDATE;
7085
0
    }
7086
0
    break;
7087
0
  case TOUCH_REMOVE:
7088
0
    event.type = GDK_TOUCH_END;
7089
0
    if (result == sKnownPointers.end()) {
7090
0
      NS_WARNING("Tried to synthesize touch-end for unknown pointer!");
7091
0
      return NS_ERROR_UNEXPECTED;
7092
0
    }
7093
0
    event.touch.sequence = result->second;
7094
0
    sKnownPointers.erase(result);
7095
0
    break;
7096
0
  case TOUCH_CANCEL:
7097
0
    event.type = GDK_TOUCH_CANCEL;
7098
0
    if (result == sKnownPointers.end()) {
7099
0
      NS_WARNING("Tried to synthesize touch-cancel for unknown pointer!");
7100
0
      return NS_ERROR_UNEXPECTED;
7101
0
    }
7102
0
    event.touch.sequence = result->second;
7103
0
    sKnownPointers.erase(result);
7104
0
    break;
7105
0
  case TOUCH_HOVER:
7106
0
  default:
7107
0
    return NS_ERROR_NOT_IMPLEMENTED;
7108
0
  }
7109
0
7110
0
  event.touch.window = mGdkWindow;
7111
0
  event.touch.time = GDK_CURRENT_TIME;
7112
0
7113
0
  GdkDisplay* display = gdk_window_get_display(mGdkWindow);
7114
0
  GdkDeviceManager* device_manager = gdk_display_get_device_manager(display);
7115
0
  event.touch.device = gdk_device_manager_get_client_pointer(device_manager);
7116
0
7117
0
  event.touch.x_root = DevicePixelsToGdkCoordRoundDown(aPoint.x);
7118
0
  event.touch.y_root = DevicePixelsToGdkCoordRoundDown(aPoint.y);
7119
0
7120
0
  LayoutDeviceIntPoint pointInWindow = aPoint - WidgetToScreenOffset();
7121
0
  event.touch.x = DevicePixelsToGdkCoordRoundDown(pointInWindow.x);
7122
0
  event.touch.y = DevicePixelsToGdkCoordRoundDown(pointInWindow.y);
7123
0
7124
0
  gdk_event_put(&event);
7125
0
7126
0
  return NS_OK;
7127
0
}
7128
#endif
7129
7130
nsWindow::CSDSupportLevel
7131
0
nsWindow::GetSystemCSDSupportLevel() {
7132
0
    if (sCSDSupportLevel != CSD_SUPPORT_UNKNOWN) {
7133
0
        return sCSDSupportLevel;
7134
0
    }
7135
0
7136
0
    // Require GTK 3.10 for GtkHeaderBar support and compatible window manager.
7137
0
    if (gtk_check_version(3, 10, 0) != nullptr) {
7138
0
        sCSDSupportLevel = CSD_SUPPORT_NONE;
7139
0
        return sCSDSupportLevel;
7140
0
    }
7141
0
7142
0
    const char* currentDesktop = getenv("XDG_CURRENT_DESKTOP");
7143
0
    if (currentDesktop) {
7144
0
        // GNOME Flashback (fallback)
7145
0
        if (strstr(currentDesktop, "GNOME-Flashback:GNOME") != nullptr) {
7146
0
            sCSDSupportLevel = CSD_SUPPORT_CLIENT;
7147
0
        // gnome-shell
7148
0
        } else if (strstr(currentDesktop, "GNOME") != nullptr) {
7149
0
            sCSDSupportLevel = CSD_SUPPORT_SYSTEM;
7150
0
        } else if (strstr(currentDesktop, "XFCE") != nullptr) {
7151
0
            sCSDSupportLevel = CSD_SUPPORT_CLIENT;
7152
0
        } else if (strstr(currentDesktop, "X-Cinnamon") != nullptr) {
7153
0
            sCSDSupportLevel = CSD_SUPPORT_SYSTEM;
7154
0
        // KDE Plasma
7155
0
        } else if (strstr(currentDesktop, "KDE") != nullptr) {
7156
0
            sCSDSupportLevel = CSD_SUPPORT_CLIENT;
7157
0
        } else if (strstr(currentDesktop, "Enlightenment") != nullptr) {
7158
0
            sCSDSupportLevel = CSD_SUPPORT_CLIENT;
7159
0
        } else if (strstr(currentDesktop, "LXDE") != nullptr) {
7160
0
            sCSDSupportLevel = CSD_SUPPORT_CLIENT;
7161
0
        } else if (strstr(currentDesktop, "openbox") != nullptr) {
7162
0
            sCSDSupportLevel = CSD_SUPPORT_CLIENT;
7163
0
        } else if (strstr(currentDesktop, "i3") != nullptr) {
7164
0
            sCSDSupportLevel = CSD_SUPPORT_NONE;
7165
0
        } else if (strstr(currentDesktop, "MATE") != nullptr) {
7166
0
            sCSDSupportLevel = CSD_SUPPORT_CLIENT;
7167
0
        // Ubuntu Unity
7168
0
        } else if (strstr(currentDesktop, "Unity") != nullptr) {
7169
0
            sCSDSupportLevel = CSD_SUPPORT_SYSTEM;
7170
0
        // Elementary OS
7171
0
        } else if (strstr(currentDesktop, "Pantheon") != nullptr) {
7172
0
            sCSDSupportLevel = CSD_SUPPORT_SYSTEM;
7173
0
        } else if (strstr(currentDesktop, "LXQt") != nullptr) {
7174
0
            sCSDSupportLevel = CSD_SUPPORT_SYSTEM;
7175
0
        } else if (strstr(currentDesktop, "Deepin") != nullptr) {
7176
0
            sCSDSupportLevel = CSD_SUPPORT_SYSTEM;
7177
0
        } else {
7178
0
// Release or beta builds are not supposed to be broken
7179
0
// so disable titlebar rendering on untested/unknown systems.
7180
#if defined(RELEASE_OR_BETA)
7181
            sCSDSupportLevel = CSD_SUPPORT_NONE;
7182
#else
7183
            sCSDSupportLevel = CSD_SUPPORT_CLIENT;
7184
0
#endif
7185
0
        }
7186
0
    } else {
7187
0
        sCSDSupportLevel = CSD_SUPPORT_NONE;
7188
0
    }
7189
0
7190
0
    // We don't support CSD_SUPPORT_SYSTEM on Wayland
7191
0
    if (!GDK_IS_X11_DISPLAY(gdk_display_get_default()) &&
7192
0
        sCSDSupportLevel == CSD_SUPPORT_SYSTEM) {
7193
0
        sCSDSupportLevel = CSD_SUPPORT_CLIENT;
7194
0
    }
7195
0
7196
0
    // GTK_CSD forces CSD mode - use also CSD because window manager
7197
0
    // decorations does not work with CSD.
7198
0
    // We check GTK_CSD as well as gtk_window_should_use_csd() does.
7199
0
    if (sCSDSupportLevel == CSD_SUPPORT_SYSTEM) {
7200
0
        const char* csdOverride = getenv("GTK_CSD");
7201
0
        if (csdOverride && g_strcmp0(csdOverride, "1") == 0) {
7202
0
            sCSDSupportLevel = CSD_SUPPORT_CLIENT;
7203
0
        }
7204
0
    }
7205
0
7206
0
    // Allow MOZ_GTK_TITLEBAR_DECORATION to override our heuristics
7207
0
    const char* decorationOverride = getenv("MOZ_GTK_TITLEBAR_DECORATION");
7208
0
    if (decorationOverride) {
7209
0
        if (strcmp(decorationOverride, "none") == 0) {
7210
0
            sCSDSupportLevel = CSD_SUPPORT_NONE;
7211
0
        } else if (strcmp(decorationOverride, "client") == 0) {
7212
0
            sCSDSupportLevel = CSD_SUPPORT_CLIENT;
7213
0
        } else if (strcmp(decorationOverride, "system") == 0) {
7214
0
            sCSDSupportLevel = CSD_SUPPORT_SYSTEM;
7215
0
        }
7216
0
    }
7217
0
7218
0
    return sCSDSupportLevel;
7219
0
}
7220
7221
bool
7222
nsWindow::TopLevelWindowUseARGBVisual()
7223
0
{
7224
0
    static int useARGBVisual = -1;
7225
0
    if (useARGBVisual != -1) {
7226
0
        return useARGBVisual;
7227
0
    }
7228
0
7229
0
    if (Preferences::HasUserValue("mozilla.widget.use-argb-visuals")) {
7230
0
        useARGBVisual =
7231
0
            Preferences::GetBool("mozilla.widget.use-argb-visuals", false);
7232
0
    } else {
7233
0
        const char* currentDesktop = getenv("XDG_CURRENT_DESKTOP");
7234
0
        useARGBVisual =
7235
0
            (currentDesktop &&
7236
0
             GetSystemCSDSupportLevel() != CSD_SUPPORT_NONE);
7237
0
7238
0
        if (useARGBVisual) {
7239
0
            useARGBVisual =
7240
0
                (strstr(currentDesktop, "GNOME-Flashback:GNOME") != nullptr ||
7241
0
                 strstr(currentDesktop, "GNOME") != nullptr);
7242
0
        }
7243
0
    }
7244
0
7245
0
    return useARGBVisual;
7246
0
}
7247
7248
int32_t
7249
nsWindow::RoundsWidgetCoordinatesTo()
7250
0
{
7251
0
    return GdkScaleFactor();
7252
0
}
7253
7254
void nsWindow::GetCompositorWidgetInitData(mozilla::widget::CompositorWidgetInitData* aInitData)
7255
0
{
7256
0
  // Make sure the window XID is propagated to X server, we can fail otherwise
7257
0
  // in GPU process (Bug 1401634).
7258
0
  if (mXDisplay && mXWindow != X11None) {
7259
0
    XFlush(mXDisplay);
7260
0
  }
7261
0
7262
0
  *aInitData = mozilla::widget::GtkCompositorWidgetInitData(
7263
0
                                (mXWindow != X11None) ? mXWindow : (uintptr_t)nullptr,
7264
0
                                mXDisplay ? nsCString(XDisplayString(mXDisplay)) : nsCString(),
7265
0
                                mIsTransparent && !mHasAlphaVisual,
7266
0
                                GetClientSize());
7267
0
}
7268
7269
#ifdef MOZ_WAYLAND
7270
wl_display*
7271
nsWindow::GetWaylandDisplay()
7272
{
7273
  // Available as of GTK 3.8+
7274
  static auto sGdkWaylandDisplayGetWlDisplay =
7275
      (wl_display *(*)(GdkDisplay *))
7276
      dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_display");
7277
7278
  GdkDisplay* gdkDisplay = gdk_display_get_default();
7279
  return mIsX11Display ? nullptr :
7280
                         sGdkWaylandDisplayGetWlDisplay(gdkDisplay);
7281
}
7282
7283
wl_surface*
7284
nsWindow::GetWaylandSurface()
7285
{
7286
  if (mContainer)
7287
    return moz_container_get_wl_surface(MOZ_CONTAINER(mContainer));
7288
7289
  NS_WARNING("nsWindow::GetWaylandSurfaces(): We don't have any mContainer for drawing!");
7290
  return nullptr;
7291
}
7292
#endif
7293
7294
#ifdef MOZ_X11
7295
/* XApp progress support currently works by setting a property
7296
 * on a window with this Atom name.  A supporting window manager
7297
 * will notice this and pass it along to whatever handling has
7298
 * been implemented on that end (e.g. passing it on to a taskbar
7299
 * widget.)  There is no issue if WM support is lacking, this is
7300
 * simply ignored in that case.
7301
 *
7302
 * See https://github.com/linuxmint/xapps/blob/master/libxapp/xapp-gtk-window.c
7303
 * for further details.
7304
 */
7305
7306
0
#define PROGRESS_HINT  "_NET_WM_XAPP_PROGRESS"
7307
7308
static void
7309
set_window_hint_cardinal (Window       xid,
7310
                          const gchar *atom_name,
7311
                          gulong       cardinal)
7312
0
{
7313
0
  GdkDisplay *display;
7314
0
7315
0
  display = gdk_display_get_default ();
7316
0
7317
0
  if (cardinal > 0)
7318
0
  {
7319
0
    XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
7320
0
                     xid,
7321
0
                     gdk_x11_get_xatom_by_name_for_display (display, atom_name),
7322
0
                     XA_CARDINAL, 32,
7323
0
                     PropModeReplace,
7324
0
                     (guchar *) &cardinal, 1);
7325
0
  }
7326
0
  else
7327
0
  {
7328
0
    XDeleteProperty (GDK_DISPLAY_XDISPLAY (display),
7329
0
                     xid,
7330
0
                     gdk_x11_get_xatom_by_name_for_display (display, atom_name));
7331
0
  }
7332
0
}
7333
#endif // MOZ_X11
7334
7335
void
7336
nsWindow::SetProgress(unsigned long progressPercent)
7337
0
{
7338
0
#ifdef MOZ_X11
7339
0
7340
0
  if (!mIsX11Display) {
7341
0
    return;
7342
0
  }
7343
0
7344
0
  if (!mShell) {
7345
0
    return;
7346
0
  }
7347
0
7348
0
  progressPercent = MIN(progressPercent, 100);
7349
0
7350
0
  set_window_hint_cardinal(GDK_WINDOW_XID(gtk_widget_get_window(mShell)),
7351
0
                           PROGRESS_HINT,
7352
0
                           progressPercent);
7353
0
#endif // MOZ_X11
7354
0
}
7355
7356
#ifdef MOZ_X11
7357
void
7358
nsWindow::SetCompositorHint(WindowComposeRequest aState)
7359
0
{
7360
0
    if (mIsX11Display) {
7361
0
        gulong value = aState;
7362
0
        GdkAtom cardinal_atom = gdk_x11_xatom_to_atom(XA_CARDINAL);
7363
0
        gdk_property_change(gtk_widget_get_window(mShell),
7364
0
                            gdk_atom_intern("_NET_WM_BYPASS_COMPOSITOR", FALSE),
7365
0
                            cardinal_atom,
7366
0
                            32, // format
7367
0
                            GDK_PROP_MODE_REPLACE,
7368
0
                            (guchar*)&value,
7369
0
                            1);
7370
0
    }
7371
0
}
7372
#endif
7373
7374
nsresult
7375
nsWindow::SetSystemFont(const nsCString& aFontName)
7376
0
{
7377
0
    GtkSettings* settings = gtk_settings_get_default();
7378
0
    g_object_set(settings, "gtk-font-name", aFontName.get(), nullptr);
7379
0
    return NS_OK;
7380
0
}
7381
7382
nsresult
7383
nsWindow::GetSystemFont(nsCString& aFontName)
7384
0
{
7385
0
    GtkSettings* settings = gtk_settings_get_default();
7386
0
    gchar* fontName = nullptr;
7387
0
    g_object_get(settings,
7388
0
                 "gtk-font-name", &fontName,
7389
0
                 nullptr);
7390
0
    if (fontName) {
7391
0
        aFontName.Assign(fontName);
7392
0
        g_free(fontName);
7393
0
    }
7394
0
    return NS_OK;
7395
0
}
7396
7397
already_AddRefed<nsIWidget>
7398
nsIWidget::CreateTopLevelWindow()
7399
0
{
7400
0
  nsCOMPtr<nsIWidget> window = new nsWindow();
7401
0
  return window.forget();
7402
0
}
7403
7404
already_AddRefed<nsIWidget>
7405
nsIWidget::CreateChildWindow()
7406
0
{
7407
0
  nsCOMPtr<nsIWidget> window = new nsWindow();
7408
0
  return window.forget();
7409
0
}
7410
7411
bool
7412
nsWindow::GetTopLevelWindowActiveState(nsIFrame *aFrame)
7413
0
{
7414
0
  // Used by window frame and button box rendering. We can end up in here in
7415
0
  // the content process when rendering one of these moz styles freely in a
7416
0
  // page. Fail in this case, there is no applicable window focus state.
7417
0
  if (!XRE_IsParentProcess()) {
7418
0
    return false;
7419
0
  }
7420
0
  // All headless windows are considered active so they are painted.
7421
0
  if (gfxPlatform::IsHeadless()) {
7422
0
    return true;
7423
0
  }
7424
0
  // Get the widget. nsIFrame's GetNearestWidget walks up the view chain
7425
0
  // until it finds a real window.
7426
0
  nsWindow* window = static_cast<nsWindow*>(aFrame->GetNearestWidget());
7427
0
  if (!window) {
7428
0
    return false;
7429
0
  }
7430
0
7431
0
  // Get our toplevel nsWindow.
7432
0
  if (!window->mIsTopLevel) {
7433
0
      GtkWidget *widget = window->GetMozContainerWidget();
7434
0
      if (!widget) {
7435
0
        return false;
7436
0
      }
7437
0
7438
0
      GtkWidget *toplevelWidget = gtk_widget_get_toplevel(widget);
7439
0
      window = get_window_for_gtk_widget(toplevelWidget);
7440
0
      if (!window) {
7441
0
        return false;
7442
0
      }
7443
0
  }
7444
0
7445
0
  return (gFocusWindow == window);
7446
0
}