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