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