summaryrefslogtreecommitdiffstats
path: root/widget/gtk/nsWindow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'widget/gtk/nsWindow.cpp')
-rw-r--r--widget/gtk/nsWindow.cpp7036
1 files changed, 7036 insertions, 0 deletions
diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp
new file mode 100644
index 000000000..d97b35002
--- /dev/null
+++ b/widget/gtk/nsWindow.cpp
@@ -0,0 +1,7036 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsWindow.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/MiscEvents.h"
+#include "mozilla/MouseEvents.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/TextEventDispatcher.h"
+#include "mozilla/TextEvents.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/TouchEvents.h"
+#include "mozilla/UniquePtrExtensions.h"
+#include <algorithm>
+
+#include "GeckoProfiler.h"
+
+#include "prlink.h"
+#include "nsGTKToolkit.h"
+#include "nsIRollupListener.h"
+#include "nsIDOMNode.h"
+
+#include "nsWidgetsCID.h"
+#include "nsDragService.h"
+#include "nsIWidgetListener.h"
+#include "nsIScreenManager.h"
+#include "SystemTimeConverter.h"
+
+#include "nsGtkKeyUtils.h"
+#include "nsGtkCursors.h"
+#include "nsScreenGtk.h"
+
+#include <gtk/gtk.h>
+#if (MOZ_WIDGET_GTK == 3)
+#include <gtk/gtkx.h>
+#endif
+#ifdef MOZ_X11
+#include <gdk/gdkx.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/XShm.h>
+#include <X11/extensions/shape.h>
+#if (MOZ_WIDGET_GTK == 3)
+#include <gdk/gdkkeysyms-compat.h>
+#endif
+
+#if (MOZ_WIDGET_GTK == 2)
+#include "gtk2xtbin.h"
+#endif
+#endif /* MOZ_X11 */
+#include <gdk/gdkkeysyms.h>
+#if (MOZ_WIDGET_GTK == 2)
+#include <gtk/gtkprivate.h>
+#endif
+
+#include "nsGkAtoms.h"
+
+#ifdef MOZ_ENABLE_STARTUP_NOTIFICATION
+#define SN_API_NOT_YET_FROZEN
+#include <startup-notification-1.0/libsn/sn.h>
+#endif
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Likely.h"
+#include "mozilla/Preferences.h"
+#include "nsIPrefService.h"
+#include "nsIGConfService.h"
+#include "nsIServiceManager.h"
+#include "nsIStringBundle.h"
+#include "nsGfxCIID.h"
+#include "nsGtkUtils.h"
+#include "nsIObserverService.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "nsIIdleServiceInternal.h"
+#include "nsIPropertyBag2.h"
+#include "GLContext.h"
+#include "gfx2DGlue.h"
+#include "nsPluginNativeWindowGtk.h"
+
+#ifdef ACCESSIBILITY
+#include "mozilla/a11y/Accessible.h"
+#include "mozilla/a11y/Platform.h"
+#include "nsAccessibilityService.h"
+
+using namespace mozilla;
+using namespace mozilla::widget;
+#endif
+
+/* For SetIcon */
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsXPIDLString.h"
+#include "nsIFile.h"
+
+/* SetCursor(imgIContainer*) */
+#include <gdk/gdk.h>
+#include <wchar.h>
+#include "imgIContainer.h"
+#include "nsGfxCIID.h"
+#include "nsImageToPixbuf.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "ClientLayerManager.h"
+
+#include "gfxPlatformGtk.h"
+#include "gfxContext.h"
+#include "gfxImageSurface.h"
+#include "gfxUtils.h"
+#include "Layers.h"
+#include "GLContextProvider.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/HelpersCairo.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
+
+#ifdef MOZ_X11
+#include "X11CompositorWidget.h"
+#include "gfxXlibSurface.h"
+#include "WindowSurfaceX11Image.h"
+#include "WindowSurfaceX11SHM.h"
+#include "WindowSurfaceXRender.h"
+#endif // MOZ_X11
+
+#include "nsShmImage.h"
+
+#include "nsIDOMWheelEvent.h"
+
+#include "NativeKeyBindings.h"
+
+#include <dlfcn.h>
+
+#include "mozilla/layers/APZCTreeManager.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::widget;
+using namespace mozilla::layers;
+using mozilla::gl::GLContext;
+
+// Don't put more than this many rects in the dirty region, just fluff
+// out to the bounding-box if there are more
+#define MAX_RECTS_IN_REGION 100
+
+const gint kEvents = GDK_EXPOSURE_MASK | GDK_STRUCTURE_MASK |
+ GDK_VISIBILITY_NOTIFY_MASK |
+ GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
+ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+#if GTK_CHECK_VERSION(3,4,0)
+ GDK_SMOOTH_SCROLL_MASK |
+ GDK_TOUCH_MASK |
+#endif
+ GDK_SCROLL_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_PROPERTY_CHANGE_MASK;
+
+/* utility functions */
+static bool is_mouse_in_window(GdkWindow* aWindow,
+ gdouble aMouseX, gdouble aMouseY);
+static nsWindow *get_window_for_gtk_widget(GtkWidget *widget);
+static nsWindow *get_window_for_gdk_window(GdkWindow *window);
+static GtkWidget *get_gtk_widget_for_gdk_window(GdkWindow *window);
+static GdkCursor *get_gtk_cursor(nsCursor aCursor);
+
+static GdkWindow *get_inner_gdk_window (GdkWindow *aWindow,
+ gint x, gint y,
+ gint *retx, gint *rety);
+
+static inline bool is_context_menu_key(const WidgetKeyboardEvent& inKeyEvent);
+
+static int is_parent_ungrab_enter(GdkEventCrossing *aEvent);
+static int is_parent_grab_leave(GdkEventCrossing *aEvent);
+
+static void GetBrandName(nsXPIDLString& brandName);
+
+/* callbacks from widgets */
+#if (MOZ_WIDGET_GTK == 2)
+static gboolean expose_event_cb (GtkWidget *widget,
+ GdkEventExpose *event);
+#else
+static gboolean expose_event_cb (GtkWidget *widget,
+ cairo_t *rect);
+#endif
+static gboolean configure_event_cb (GtkWidget *widget,
+ GdkEventConfigure *event);
+static void container_unrealize_cb (GtkWidget *widget);
+static void size_allocate_cb (GtkWidget *widget,
+ GtkAllocation *allocation);
+static gboolean delete_event_cb (GtkWidget *widget,
+ GdkEventAny *event);
+static gboolean enter_notify_event_cb (GtkWidget *widget,
+ GdkEventCrossing *event);
+static gboolean leave_notify_event_cb (GtkWidget *widget,
+ GdkEventCrossing *event);
+static gboolean motion_notify_event_cb (GtkWidget *widget,
+ GdkEventMotion *event);
+static gboolean button_press_event_cb (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean button_release_event_cb (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean focus_in_event_cb (GtkWidget *widget,
+ GdkEventFocus *event);
+static gboolean focus_out_event_cb (GtkWidget *widget,
+ GdkEventFocus *event);
+static gboolean key_press_event_cb (GtkWidget *widget,
+ GdkEventKey *event);
+static gboolean key_release_event_cb (GtkWidget *widget,
+ GdkEventKey *event);
+static gboolean property_notify_event_cb (GtkWidget *widget,
+ GdkEventProperty *event);
+static gboolean scroll_event_cb (GtkWidget *widget,
+ GdkEventScroll *event);
+static gboolean visibility_notify_event_cb(GtkWidget *widget,
+ GdkEventVisibility *event);
+static void hierarchy_changed_cb (GtkWidget *widget,
+ GtkWidget *previous_toplevel);
+static gboolean window_state_event_cb (GtkWidget *widget,
+ GdkEventWindowState *event);
+static void theme_changed_cb (GtkSettings *settings,
+ GParamSpec *pspec,
+ nsWindow *data);
+static void check_resize_cb (GtkContainer* container,
+ gpointer user_data);
+
+#if (MOZ_WIDGET_GTK == 3)
+static void scale_changed_cb (GtkWidget* widget,
+ GParamSpec* aPSpec,
+ gpointer aPointer);
+#endif
+#if GTK_CHECK_VERSION(3,4,0)
+static gboolean touch_event_cb (GtkWidget* aWidget,
+ GdkEventTouch* aEvent);
+#endif
+static nsWindow* GetFirstNSWindowForGDKWindow (GdkWindow *aGdkWindow);
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+#ifdef MOZ_X11
+static GdkFilterReturn popup_take_focus_filter (GdkXEvent *gdk_xevent,
+ GdkEvent *event,
+ gpointer data);
+static GdkFilterReturn plugin_window_filter_func (GdkXEvent *gdk_xevent,
+ GdkEvent *event,
+ gpointer data);
+static GdkFilterReturn plugin_client_message_filter (GdkXEvent *xevent,
+ GdkEvent *event,
+ gpointer data);
+#endif /* MOZ_X11 */
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+static gboolean drag_motion_event_cb (GtkWidget *aWidget,
+ GdkDragContext *aDragContext,
+ gint aX,
+ gint aY,
+ guint aTime,
+ gpointer aData);
+static void drag_leave_event_cb (GtkWidget *aWidget,
+ GdkDragContext *aDragContext,
+ guint aTime,
+ gpointer aData);
+static gboolean drag_drop_event_cb (GtkWidget *aWidget,
+ GdkDragContext *aDragContext,
+ gint aX,
+ gint aY,
+ guint aTime,
+ gpointer aData);
+static void drag_data_received_event_cb(GtkWidget *aWidget,
+ GdkDragContext *aDragContext,
+ gint aX,
+ gint aY,
+ GtkSelectionData *aSelectionData,
+ guint aInfo,
+ guint32 aTime,
+ gpointer aData);
+
+/* initialization static functions */
+static nsresult initialize_prefs (void);
+
+static guint32 sLastUserInputTime = GDK_CURRENT_TIME;
+static guint32 sRetryGrabTime;
+
+static SystemTimeConverter<guint32>&
+TimeConverter() {
+ static SystemTimeConverter<guint32> sTimeConverterSingleton;
+ return sTimeConverterSingleton;
+}
+
+namespace mozilla {
+
+class CurrentX11TimeGetter
+{
+public:
+ explicit CurrentX11TimeGetter(GdkWindow* aWindow)
+ : mWindow(aWindow)
+ , mAsyncUpdateStart()
+ {
+ }
+
+ guint32 GetCurrentTime() const
+ {
+ return gdk_x11_get_server_time(mWindow);
+ }
+
+ void GetTimeAsyncForPossibleBackwardsSkew(const TimeStamp& aNow)
+ {
+ // Check for in-flight request
+ if (!mAsyncUpdateStart.IsNull()) {
+ return;
+ }
+ mAsyncUpdateStart = aNow;
+
+ Display* xDisplay = GDK_WINDOW_XDISPLAY(mWindow);
+ Window xWindow = GDK_WINDOW_XID(mWindow);
+ unsigned char c = 'a';
+ Atom timeStampPropAtom = TimeStampPropAtom();
+ XChangeProperty(xDisplay, xWindow, timeStampPropAtom,
+ timeStampPropAtom, 8, PropModeReplace, &c, 1);
+ XFlush(xDisplay);
+ }
+
+ gboolean PropertyNotifyHandler(GtkWidget* aWidget,
+ GdkEventProperty* aEvent)
+ {
+ if (aEvent->atom !=
+ gdk_x11_xatom_to_atom(TimeStampPropAtom())) {
+ return FALSE;
+ }
+
+ guint32 eventTime = aEvent->time;
+ TimeStamp lowerBound = mAsyncUpdateStart;
+
+ TimeConverter().CompensateForBackwardsSkew(eventTime, lowerBound);
+ mAsyncUpdateStart = TimeStamp();
+ return TRUE;
+ }
+
+private:
+ static Atom TimeStampPropAtom() {
+ return gdk_x11_get_xatom_by_name_for_display(
+ gdk_display_get_default(), "GDK_TIMESTAMP_PROP");
+ }
+
+ // This is safe because this class is stored as a member of mWindow and
+ // won't outlive it.
+ GdkWindow* mWindow;
+ TimeStamp mAsyncUpdateStart;
+};
+
+} // namespace mozilla
+
+static NS_DEFINE_IID(kCDragServiceCID, NS_DRAGSERVICE_CID);
+
+// The window from which the focus manager asks us to dispatch key events.
+static nsWindow *gFocusWindow = nullptr;
+static bool gBlockActivateEvent = false;
+static bool gGlobalsInitialized = false;
+static bool gRaiseWindows = true;
+static nsWindow *gPluginFocusWindow = nullptr;
+
+#if GTK_CHECK_VERSION(3,4,0)
+static uint32_t gLastTouchID = 0;
+#endif
+
+#define NS_WINDOW_TITLE_MAX_LENGTH 4095
+
+// If after selecting profile window, the startup fail, please refer to
+// http://bugzilla.gnome.org/show_bug.cgi?id=88940
+
+// needed for imgIContainer cursors
+// GdkDisplay* was added in 2.2
+typedef struct _GdkDisplay GdkDisplay;
+
+#define kWindowPositionSlop 20
+
+// cursor cache
+static GdkCursor *gCursorCache[eCursorCount];
+
+static GtkWidget *gInvisibleContainer = nullptr;
+
+// Sometimes this actually also includes the state of the modifier keys, but
+// only the button state bits are used.
+static guint gButtonState;
+
+static inline int32_t
+GetBitmapStride(int32_t width)
+{
+#if defined(MOZ_X11) || (MOZ_WIDGET_GTK == 2)
+ return (width+7)/8;
+#else
+ return cairo_format_stride_for_width(CAIRO_FORMAT_A1, width);
+#endif
+}
+
+static inline bool TimestampIsNewerThan(guint32 a, guint32 b)
+{
+ // Timestamps are just the least significant bits of a monotonically
+ // increasing function, and so the use of unsigned overflow arithmetic.
+ return a - b <= G_MAXUINT32/2;
+}
+
+static void
+UpdateLastInputEventTime(void *aGdkEvent)
+{
+ nsCOMPtr<nsIIdleServiceInternal> idleService =
+ do_GetService("@mozilla.org/widget/idleservice;1");
+ if (idleService) {
+ idleService->ResetIdleTimeOut(0);
+ }
+
+ guint timestamp = gdk_event_get_time(static_cast<GdkEvent*>(aGdkEvent));
+ if (timestamp == GDK_CURRENT_TIME)
+ return;
+
+ sLastUserInputTime = timestamp;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(nsWindow, nsBaseWidget)
+
+nsWindow::nsWindow()
+{
+ mIsTopLevel = false;
+ mIsDestroyed = false;
+ mListenForResizes = false;
+ mNeedsDispatchResized = false;
+ mIsShown = false;
+ mNeedsShow = false;
+ mEnabled = true;
+ mCreated = false;
+#if GTK_CHECK_VERSION(3,4,0)
+ mHandleTouchEvent = false;
+#endif
+ mIsDragPopup = false;
+ mIsX11Display = GDK_IS_X11_DISPLAY(gdk_display_get_default());
+
+ mContainer = nullptr;
+ mGdkWindow = nullptr;
+ mShell = nullptr;
+ mPluginNativeWindow = nullptr;
+ mHasMappedToplevel = false;
+ mIsFullyObscured = false;
+ mRetryPointerGrab = false;
+ mWindowType = eWindowType_child;
+ mSizeState = nsSizeMode_Normal;
+ mLastSizeMode = nsSizeMode_Normal;
+ mSizeConstraints.mMaxSize = GetSafeWindowSize(mSizeConstraints.mMaxSize);
+
+#ifdef MOZ_X11
+ mOldFocusWindow = 0;
+
+ mXDisplay = nullptr;
+ mXWindow = X11None;
+ mXVisual = nullptr;
+ mXDepth = 0;
+#endif /* MOZ_X11 */
+ mPluginType = PluginType_NONE;
+
+ if (!gGlobalsInitialized) {
+ gGlobalsInitialized = true;
+
+ // It's OK if either of these fail, but it may not be one day.
+ initialize_prefs();
+ }
+
+ mLastMotionPressure = 0;
+
+#ifdef ACCESSIBILITY
+ mRootAccessible = nullptr;
+#endif
+
+ mIsTransparent = false;
+ mTransparencyBitmap = nullptr;
+
+ mTransparencyBitmapWidth = 0;
+ mTransparencyBitmapHeight = 0;
+
+#if GTK_CHECK_VERSION(3,4,0)
+ mLastScrollEventTime = GDK_CURRENT_TIME;
+#endif
+ mPendingConfigures = 0;
+}
+
+nsWindow::~nsWindow()
+{
+ LOG(("nsWindow::~nsWindow() [%p]\n", (void *)this));
+
+ delete[] mTransparencyBitmap;
+ mTransparencyBitmap = nullptr;
+
+ Destroy();
+}
+
+/* static */ void
+nsWindow::ReleaseGlobals()
+{
+ for (uint32_t i = 0; i < ArrayLength(gCursorCache); ++i) {
+ if (gCursorCache[i]) {
+#if (MOZ_WIDGET_GTK == 3)
+ g_object_unref(gCursorCache[i]);
+#else
+ gdk_cursor_unref(gCursorCache[i]);
+#endif
+ gCursorCache[i] = nullptr;
+ }
+ }
+}
+
+void
+nsWindow::CommonCreate(nsIWidget *aParent, bool aListenForResizes)
+{
+ mParent = aParent;
+ mListenForResizes = aListenForResizes;
+ mCreated = true;
+}
+
+void
+nsWindow::DispatchActivateEvent(void)
+{
+ NS_ASSERTION(mContainer || mIsDestroyed,
+ "DispatchActivateEvent only intended for container windows");
+
+#ifdef ACCESSIBILITY
+ DispatchActivateEventAccessible();
+#endif //ACCESSIBILITY
+
+ if (mWidgetListener)
+ mWidgetListener->WindowActivated();
+}
+
+void
+nsWindow::DispatchDeactivateEvent(void)
+{
+ if (mWidgetListener)
+ mWidgetListener->WindowDeactivated();
+
+#ifdef ACCESSIBILITY
+ DispatchDeactivateEventAccessible();
+#endif //ACCESSIBILITY
+}
+
+void
+nsWindow::DispatchResized()
+{
+ mNeedsDispatchResized = false;
+ if (mWidgetListener) {
+ mWidgetListener->WindowResized(this, mBounds.width, mBounds.height);
+ }
+ if (mAttachedWidgetListener) {
+ mAttachedWidgetListener->WindowResized(this,
+ mBounds.width, mBounds.height);
+ }
+}
+
+void
+nsWindow::MaybeDispatchResized()
+{
+ if (mNeedsDispatchResized && !mIsDestroyed) {
+ DispatchResized();
+ }
+}
+
+nsIWidgetListener*
+nsWindow::GetListener()
+{
+ return mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener;
+}
+
+nsresult
+nsWindow::DispatchEvent(WidgetGUIEvent* aEvent, nsEventStatus& aStatus)
+{
+#ifdef DEBUG
+ debug_DumpEvent(stdout, aEvent->mWidget, aEvent,
+ "something", 0);
+#endif
+ aStatus = nsEventStatus_eIgnore;
+ nsIWidgetListener* listener = GetListener();
+ if (listener) {
+ aStatus = listener->HandleEvent(aEvent, mUseAttachedEvents);
+ }
+
+ return NS_OK;
+}
+
+void
+nsWindow::OnDestroy(void)
+{
+ if (mOnDestroyCalled)
+ return;
+
+ mOnDestroyCalled = true;
+
+ // Prevent deletion.
+ nsCOMPtr<nsIWidget> kungFuDeathGrip = this;
+
+ // release references to children, device context, toolkit + app shell
+ nsBaseWidget::OnDestroy();
+
+ // Remove association between this object and its parent and siblings.
+ nsBaseWidget::Destroy();
+ mParent = nullptr;
+
+ NotifyWindowDestroyed();
+}
+
+bool
+nsWindow::AreBoundsSane(void)
+{
+ if (mBounds.width > 0 && mBounds.height > 0)
+ return true;
+
+ return false;
+}
+
+static GtkWidget*
+EnsureInvisibleContainer()
+{
+ if (!gInvisibleContainer) {
+ // GtkWidgets need to be anchored to a GtkWindow to be realized (to
+ // have a window). Using GTK_WINDOW_POPUP rather than
+ // GTK_WINDOW_TOPLEVEL in the hope that POPUP results in less
+ // initialization and window manager interaction.
+ GtkWidget* window = gtk_window_new(GTK_WINDOW_POPUP);
+ gInvisibleContainer = moz_container_new();
+ gtk_container_add(GTK_CONTAINER(window), gInvisibleContainer);
+ gtk_widget_realize(gInvisibleContainer);
+
+ }
+ return gInvisibleContainer;
+}
+
+static void
+CheckDestroyInvisibleContainer()
+{
+ NS_PRECONDITION(gInvisibleContainer, "oh, no");
+
+ if (!gdk_window_peek_children(gtk_widget_get_window(gInvisibleContainer))) {
+ // No children, so not in use.
+ // Make sure to destroy the GtkWindow also.
+ gtk_widget_destroy(gtk_widget_get_parent(gInvisibleContainer));
+ gInvisibleContainer = nullptr;
+ }
+}
+
+// Change the containing GtkWidget on a sub-hierarchy of GdkWindows belonging
+// to aOldWidget and rooted at aWindow, and reparent any child GtkWidgets of
+// the GdkWindow hierarchy to aNewWidget.
+static void
+SetWidgetForHierarchy(GdkWindow *aWindow,
+ GtkWidget *aOldWidget,
+ GtkWidget *aNewWidget)
+{
+ gpointer data;
+ gdk_window_get_user_data(aWindow, &data);
+
+ if (data != aOldWidget) {
+ if (!GTK_IS_WIDGET(data))
+ return;
+
+ GtkWidget* widget = static_cast<GtkWidget*>(data);
+ if (gtk_widget_get_parent(widget) != aOldWidget)
+ return;
+
+ // This window belongs to a child widget, which will no longer be a
+ // child of aOldWidget.
+ gtk_widget_reparent(widget, aNewWidget);
+
+ return;
+ }
+
+ GList *children = gdk_window_get_children(aWindow);
+ for(GList *list = children; list; list = list->next) {
+ SetWidgetForHierarchy(GDK_WINDOW(list->data), aOldWidget, aNewWidget);
+ }
+ g_list_free(children);
+
+ gdk_window_set_user_data(aWindow, aNewWidget);
+}
+
+// Walk the list of child windows and call destroy on them.
+void
+nsWindow::DestroyChildWindows()
+{
+ if (!mGdkWindow)
+ return;
+
+ while (GList *children = gdk_window_peek_children(mGdkWindow)) {
+ GdkWindow *child = GDK_WINDOW(children->data);
+ nsWindow *kid = get_window_for_gdk_window(child);
+ if (kid) {
+ kid->Destroy();
+ } else {
+ // This child is not an nsWindow.
+ // Destroy the child GtkWidget.
+ gpointer data;
+ gdk_window_get_user_data(child, &data);
+ if (GTK_IS_WIDGET(data)) {
+ gtk_widget_destroy(static_cast<GtkWidget*>(data));
+ }
+ }
+ }
+}
+
+void
+nsWindow::Destroy()
+{
+ if (mIsDestroyed || !mCreated)
+ return;
+
+ LOG(("nsWindow::Destroy [%p]\n", (void *)this));
+ mIsDestroyed = true;
+ mCreated = false;
+
+ /** Need to clean our LayerManager up while still alive */
+ if (mLayerManager) {
+ mLayerManager->Destroy();
+ }
+ mLayerManager = nullptr;
+
+ // It is safe to call DestroyeCompositor several times (here and
+ // in the parent class) since it will take effect only once.
+ // The reason we call it here is because on gtk platforms we need
+ // to destroy the compositor before we destroy the gdk window (which
+ // destroys the the gl context attached to it).
+ DestroyCompositor();
+
+#ifdef MOZ_X11
+ // Ensure any resources assigned to the window get cleaned up first
+ // to avoid double-freeing.
+ mSurfaceProvider.CleanupResources();
+#endif
+
+ ClearCachedResources();
+
+ g_signal_handlers_disconnect_by_func(gtk_settings_get_default(),
+ FuncToGpointer(theme_changed_cb),
+ this);
+
+ nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
+ if (rollupListener) {
+ nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget();
+ if (static_cast<nsIWidget *>(this) == rollupWidget) {
+ rollupListener->Rollup(0, false, nullptr, nullptr);
+ }
+ }
+
+ // dragService will be null after shutdown of the service manager.
+ nsDragService *dragService = nsDragService::GetInstance();
+ if (dragService && this == dragService->GetMostRecentDestWindow()) {
+ dragService->ScheduleLeaveEvent();
+ }
+
+ NativeShow(false);
+
+ if (mIMContext) {
+ mIMContext->OnDestroyWindow(this);
+ }
+
+ // make sure that we remove ourself as the focus window
+ if (gFocusWindow == this) {
+ LOGFOCUS(("automatically losing focus...\n"));
+ gFocusWindow = nullptr;
+ }
+
+#if (MOZ_WIDGET_GTK == 2) && defined(MOZ_X11)
+ // make sure that we remove ourself as the plugin focus window
+ if (gPluginFocusWindow == this) {
+ gPluginFocusWindow->LoseNonXEmbedPluginFocus();
+ }
+#endif /* MOZ_X11 && MOZ_WIDGET_GTK == 2 && defined(MOZ_X11) */
+
+ GtkWidget *owningWidget = GetMozContainerWidget();
+ if (mShell) {
+ gtk_widget_destroy(mShell);
+ mShell = nullptr;
+ mContainer = nullptr;
+ MOZ_ASSERT(!mGdkWindow,
+ "mGdkWindow should be NULL when mContainer is destroyed");
+ }
+ else if (mContainer) {
+ gtk_widget_destroy(GTK_WIDGET(mContainer));
+ mContainer = nullptr;
+ MOZ_ASSERT(!mGdkWindow,
+ "mGdkWindow should be NULL when mContainer is destroyed");
+ }
+ else if (mGdkWindow) {
+ // Destroy child windows to ensure that their mThebesSurfaces are
+ // released and to remove references from GdkWindows back to their
+ // container widget. (OnContainerUnrealize() does this when the
+ // MozContainer widget is destroyed.)
+ DestroyChildWindows();
+
+ gdk_window_set_user_data(mGdkWindow, nullptr);
+ g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr);
+ gdk_window_destroy(mGdkWindow);
+ mGdkWindow = nullptr;
+ }
+
+ if (gInvisibleContainer && owningWidget == gInvisibleContainer) {
+ CheckDestroyInvisibleContainer();
+ }
+
+#ifdef ACCESSIBILITY
+ if (mRootAccessible) {
+ mRootAccessible = nullptr;
+ }
+#endif
+
+ // Save until last because OnDestroy() may cause us to be deleted.
+ OnDestroy();
+}
+
+nsIWidget *
+nsWindow::GetParent(void)
+{
+ return mParent;
+}
+
+float
+nsWindow::GetDPI()
+{
+ GdkScreen *screen = gdk_display_get_default_screen(gdk_display_get_default());
+ double heightInches = gdk_screen_get_height_mm(screen)/MM_PER_INCH_FLOAT;
+ if (heightInches < 0.25) {
+ // Something's broken, but we'd better not crash.
+ return 96.0f;
+ }
+ return float(gdk_screen_get_height(screen)/heightInches);
+}
+
+double
+nsWindow::GetDefaultScaleInternal()
+{
+ return GdkScaleFactor() * gfxPlatformGtk::GetDPIScale();
+}
+
+NS_IMETHODIMP
+nsWindow::SetParent(nsIWidget *aNewParent)
+{
+ if (mContainer || !mGdkWindow) {
+ NS_NOTREACHED("nsWindow::SetParent called illegally");
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ nsCOMPtr<nsIWidget> kungFuDeathGrip = this;
+ if (mParent) {
+ mParent->RemoveChild(this);
+ }
+
+ mParent = aNewParent;
+
+ GtkWidget* oldContainer = GetMozContainerWidget();
+ if (!oldContainer) {
+ // The GdkWindows have been destroyed so there is nothing else to
+ // reparent.
+ MOZ_ASSERT(gdk_window_is_destroyed(mGdkWindow),
+ "live GdkWindow with no widget");
+ return NS_OK;
+ }
+
+ if (aNewParent) {
+ aNewParent->AddChild(this);
+ ReparentNativeWidget(aNewParent);
+ } else {
+ // aNewParent is nullptr, but reparent to a hidden window to avoid
+ // destroying the GdkWindow and its descendants.
+ // An invisible container widget is needed to hold descendant
+ // GtkWidgets.
+ GtkWidget* newContainer = EnsureInvisibleContainer();
+ GdkWindow* newParentWindow = gtk_widget_get_window(newContainer);
+ ReparentNativeWidgetInternal(aNewParent, newContainer, newParentWindow,
+ oldContainer);
+ }
+ return NS_OK;
+}
+
+bool
+nsWindow::WidgetTypeSupportsAcceleration()
+{
+ return !IsSmallPopup();
+}
+
+void
+nsWindow::ReparentNativeWidget(nsIWidget* aNewParent)
+{
+ NS_PRECONDITION(aNewParent, "");
+ NS_ASSERTION(!mIsDestroyed, "");
+ NS_ASSERTION(!static_cast<nsWindow*>(aNewParent)->mIsDestroyed, "");
+
+ GtkWidget* oldContainer = GetMozContainerWidget();
+ if (!oldContainer) {
+ // The GdkWindows have been destroyed so there is nothing else to
+ // reparent.
+ MOZ_ASSERT(gdk_window_is_destroyed(mGdkWindow),
+ "live GdkWindow with no widget");
+ return;
+ }
+ MOZ_ASSERT(!gdk_window_is_destroyed(mGdkWindow),
+ "destroyed GdkWindow with widget");
+
+ nsWindow* newParent = static_cast<nsWindow*>(aNewParent);
+ GdkWindow* newParentWindow = newParent->mGdkWindow;
+ GtkWidget* newContainer = newParent->GetMozContainerWidget();
+ GtkWindow* shell = GTK_WINDOW(mShell);
+
+ if (shell && gtk_window_get_transient_for(shell)) {
+ GtkWindow* topLevelParent =
+ GTK_WINDOW(gtk_widget_get_toplevel(newContainer));
+ gtk_window_set_transient_for(shell, topLevelParent);
+ }
+
+ ReparentNativeWidgetInternal(aNewParent, newContainer, newParentWindow,
+ oldContainer);
+}
+
+void
+nsWindow::ReparentNativeWidgetInternal(nsIWidget* aNewParent,
+ GtkWidget* aNewContainer,
+ GdkWindow* aNewParentWindow,
+ GtkWidget* aOldContainer)
+{
+ if (!aNewContainer) {
+ // The new parent GdkWindow has been destroyed.
+ MOZ_ASSERT(!aNewParentWindow ||
+ gdk_window_is_destroyed(aNewParentWindow),
+ "live GdkWindow with no widget");
+ Destroy();
+ } else {
+ if (aNewContainer != aOldContainer) {
+ MOZ_ASSERT(!gdk_window_is_destroyed(aNewParentWindow),
+ "destroyed GdkWindow with widget");
+ SetWidgetForHierarchy(mGdkWindow, aOldContainer, aNewContainer);
+
+ if (aOldContainer == gInvisibleContainer) {
+ CheckDestroyInvisibleContainer();
+ }
+ }
+
+ if (!mIsTopLevel) {
+ gdk_window_reparent(mGdkWindow, aNewParentWindow,
+ DevicePixelsToGdkCoordRoundDown(mBounds.x),
+ DevicePixelsToGdkCoordRoundDown(mBounds.y));
+ }
+ }
+
+ nsWindow* newParent = static_cast<nsWindow*>(aNewParent);
+ bool parentHasMappedToplevel =
+ newParent && newParent->mHasMappedToplevel;
+ if (mHasMappedToplevel != parentHasMappedToplevel) {
+ SetHasMappedToplevel(parentHasMappedToplevel);
+ }
+}
+
+void
+nsWindow::SetModal(bool aModal)
+{
+ LOG(("nsWindow::SetModal [%p] %d\n", (void *)this, aModal));
+ if (mIsDestroyed)
+ return;
+ if (!mIsTopLevel || !mShell)
+ return;
+ gtk_window_set_modal(GTK_WINDOW(mShell), aModal ? TRUE : FALSE);
+}
+
+// nsIWidget method, which means IsShown.
+bool
+nsWindow::IsVisible() const
+{
+ return mIsShown;
+}
+
+void
+nsWindow::RegisterTouchWindow()
+{
+#if GTK_CHECK_VERSION(3,4,0)
+ mHandleTouchEvent = true;
+ mTouches.Clear();
+#endif
+}
+
+void
+nsWindow::ConstrainPosition(bool aAllowSlop, int32_t *aX, int32_t *aY)
+{
+ if (!mIsTopLevel || !mShell)
+ return;
+
+ double dpiScale = GetDefaultScale().scale;
+
+ // we need to use the window size in logical screen pixels
+ int32_t logWidth = std::max(NSToIntRound(mBounds.width / dpiScale), 1);
+ int32_t logHeight = std::max(NSToIntRound(mBounds.height / dpiScale), 1);
+
+ /* get our playing field. use the current screen, or failing that
+ for any reason, use device caps for the default screen. */
+ nsCOMPtr<nsIScreen> screen;
+ nsCOMPtr<nsIScreenManager> screenmgr = do_GetService("@mozilla.org/gfx/screenmanager;1");
+ if (screenmgr) {
+ screenmgr->ScreenForRect(*aX, *aY, logWidth, logHeight,
+ getter_AddRefs(screen));
+ }
+
+ // We don't have any screen so leave the coordinates as is
+ if (!screen)
+ return;
+
+ nsIntRect screenRect;
+ if (mSizeMode != nsSizeMode_Fullscreen) {
+ // For normalized windows, use the desktop work area.
+ screen->GetAvailRectDisplayPix(&screenRect.x, &screenRect.y,
+ &screenRect.width, &screenRect.height);
+ } else {
+ // For full screen windows, use the desktop.
+ screen->GetRectDisplayPix(&screenRect.x, &screenRect.y,
+ &screenRect.width, &screenRect.height);
+ }
+
+ if (aAllowSlop) {
+ if (*aX < screenRect.x - logWidth + kWindowPositionSlop)
+ *aX = screenRect.x - logWidth + kWindowPositionSlop;
+ else if (*aX >= screenRect.XMost() - kWindowPositionSlop)
+ *aX = screenRect.XMost() - kWindowPositionSlop;
+
+ if (*aY < screenRect.y - logHeight + kWindowPositionSlop)
+ *aY = screenRect.y - logHeight + kWindowPositionSlop;
+ else if (*aY >= screenRect.YMost() - kWindowPositionSlop)
+ *aY = screenRect.YMost() - kWindowPositionSlop;
+ } else {
+ if (*aX < screenRect.x)
+ *aX = screenRect.x;
+ else if (*aX >= screenRect.XMost() - logWidth)
+ *aX = screenRect.XMost() - logWidth;
+
+ if (*aY < screenRect.y)
+ *aY = screenRect.y;
+ else if (*aY >= screenRect.YMost() - logHeight)
+ *aY = screenRect.YMost() - logHeight;
+ }
+}
+
+void nsWindow::SetSizeConstraints(const SizeConstraints& aConstraints)
+{
+ mSizeConstraints.mMinSize = GetSafeWindowSize(aConstraints.mMinSize);
+ mSizeConstraints.mMaxSize = GetSafeWindowSize(aConstraints.mMaxSize);
+
+ if (mShell) {
+ GdkGeometry geometry;
+ geometry.min_width = DevicePixelsToGdkCoordRoundUp(
+ mSizeConstraints.mMinSize.width);
+ geometry.min_height = DevicePixelsToGdkCoordRoundUp(
+ mSizeConstraints.mMinSize.height);
+ geometry.max_width = DevicePixelsToGdkCoordRoundDown(
+ mSizeConstraints.mMaxSize.width);
+ geometry.max_height = DevicePixelsToGdkCoordRoundDown(
+ mSizeConstraints.mMaxSize.height);
+
+ uint32_t hints = 0;
+ if (aConstraints.mMinSize != LayoutDeviceIntSize(0, 0)) {
+ hints |= GDK_HINT_MIN_SIZE;
+ }
+ if (aConstraints.mMaxSize !=
+ LayoutDeviceIntSize(NS_MAXSIZE, NS_MAXSIZE)) {
+ hints |= GDK_HINT_MAX_SIZE;
+ }
+ gtk_window_set_geometry_hints(GTK_WINDOW(mShell), nullptr,
+ &geometry, GdkWindowHints(hints));
+ }
+}
+
+NS_IMETHODIMP
+nsWindow::Show(bool aState)
+{
+ if (aState == mIsShown)
+ return NS_OK;
+
+ // Clear our cached resources when the window is hidden.
+ if (mIsShown && !aState) {
+ ClearCachedResources();
+ }
+
+ mIsShown = aState;
+
+ LOG(("nsWindow::Show [%p] state %d\n", (void *)this, aState));
+
+ if (aState) {
+ // Now that this window is shown, mHasMappedToplevel needs to be
+ // tracked on viewable descendants.
+ SetHasMappedToplevel(mHasMappedToplevel);
+ }
+
+ // Ok, someone called show on a window that isn't sized to a sane
+ // value. Mark this window as needing to have Show() called on it
+ // and return.
+ if ((aState && !AreBoundsSane()) || !mCreated) {
+ LOG(("\tbounds are insane or window hasn't been created yet\n"));
+ mNeedsShow = true;
+ return NS_OK;
+ }
+
+ // If someone is hiding this widget, clear any needing show flag.
+ if (!aState)
+ mNeedsShow = false;
+
+#ifdef ACCESSIBILITY
+ if (aState && a11y::ShouldA11yBeEnabled())
+ CreateRootAccessible();
+#endif
+
+ NativeShow(aState);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::Resize(double aWidth, double aHeight, bool aRepaint)
+{
+ double scale = BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
+ int32_t width = NSToIntRound(scale * aWidth);
+ int32_t height = NSToIntRound(scale * aHeight);
+ ConstrainSize(&width, &height);
+
+ // For top-level windows, aWidth and aHeight should possibly be
+ // interpreted as frame bounds, but NativeResize treats these as window
+ // bounds (Bug 581866).
+
+ mBounds.SizeTo(width, height);
+
+ if (!mCreated)
+ return NS_OK;
+
+ NativeResize();
+
+ NotifyRollupGeometryChange();
+ ResizePluginSocketWidget();
+
+ // send a resize notification if this is a toplevel
+ if (mIsTopLevel || mListenForResizes) {
+ DispatchResized();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::Resize(double aX, double aY, double aWidth, double aHeight,
+ bool aRepaint)
+{
+ double scale = BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
+ int32_t width = NSToIntRound(scale * aWidth);
+ int32_t height = NSToIntRound(scale * aHeight);
+ ConstrainSize(&width, &height);
+
+ int32_t x = NSToIntRound(scale * aX);
+ int32_t y = NSToIntRound(scale * aY);
+ mBounds.x = x;
+ mBounds.y = y;
+ mBounds.SizeTo(width, height);
+
+ if (!mCreated)
+ return NS_OK;
+
+ NativeMoveResize();
+
+ NotifyRollupGeometryChange();
+ ResizePluginSocketWidget();
+
+ if (mIsTopLevel || mListenForResizes) {
+ DispatchResized();
+ }
+
+ return NS_OK;
+}
+
+void
+nsWindow::ResizePluginSocketWidget()
+{
+ // e10s specific, a eWindowType_plugin_ipc_chrome holds its own
+ // nsPluginNativeWindowGtk wrapper. We are responsible for resizing
+ // the embedded socket widget.
+ if (mWindowType == eWindowType_plugin_ipc_chrome) {
+ nsPluginNativeWindowGtk* wrapper = (nsPluginNativeWindowGtk*)
+ GetNativeData(NS_NATIVE_PLUGIN_OBJECT_PTR);
+ if (wrapper) {
+ wrapper->width = mBounds.width;
+ wrapper->height = mBounds.height;
+ wrapper->SetAllocation();
+ }
+ }
+}
+
+NS_IMETHODIMP
+nsWindow::Enable(bool aState)
+{
+ mEnabled = aState;
+
+ return NS_OK;
+}
+
+bool
+nsWindow::IsEnabled() const
+{
+ return mEnabled;
+}
+
+
+
+NS_IMETHODIMP
+nsWindow::Move(double aX, double aY)
+{
+ LOG(("nsWindow::Move [%p] %f %f\n", (void *)this,
+ aX, aY));
+
+ double scale = BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
+ int32_t x = NSToIntRound(aX * scale);
+ int32_t y = NSToIntRound(aY * scale);
+
+ if (mWindowType == eWindowType_toplevel ||
+ mWindowType == eWindowType_dialog) {
+ SetSizeMode(nsSizeMode_Normal);
+ }
+
+ // Since a popup window's x/y coordinates are in relation to to
+ // the parent, the parent might have moved so we always move a
+ // popup window.
+ if (x == mBounds.x && y == mBounds.y &&
+ mWindowType != eWindowType_popup)
+ return NS_OK;
+
+ // XXX Should we do some AreBoundsSane check here?
+
+ mBounds.x = x;
+ mBounds.y = y;
+
+ if (!mCreated)
+ return NS_OK;
+
+ NativeMove();
+
+ NotifyRollupGeometryChange();
+ return NS_OK;
+}
+
+
+void
+nsWindow::NativeMove()
+{
+ GdkPoint point = DevicePixelsToGdkPointRoundDown(mBounds.TopLeft());
+
+ if (mIsTopLevel) {
+ gtk_window_move(GTK_WINDOW(mShell), point.x, point.y);
+ }
+ else if (mGdkWindow) {
+ gdk_window_move(mGdkWindow, point.x, point.y);
+ }
+}
+
+void
+nsWindow::SetZIndex(int32_t aZIndex)
+{
+ nsIWidget* oldPrev = GetPrevSibling();
+
+ nsBaseWidget::SetZIndex(aZIndex);
+
+ if (GetPrevSibling() == oldPrev) {
+ return;
+ }
+
+ NS_ASSERTION(!mContainer, "Expected Mozilla child widget");
+
+ // We skip the nsWindows that don't have mGdkWindows.
+ // These are probably in the process of being destroyed.
+
+ if (!GetNextSibling()) {
+ // We're to be on top.
+ if (mGdkWindow)
+ gdk_window_raise(mGdkWindow);
+ } else {
+ // All the siblings before us need to be below our widget.
+ for (nsWindow* w = this; w;
+ w = static_cast<nsWindow*>(w->GetPrevSibling())) {
+ if (w->mGdkWindow)
+ gdk_window_lower(w->mGdkWindow);
+ }
+ }
+}
+
+void
+nsWindow::SetSizeMode(nsSizeMode aMode)
+{
+ LOG(("nsWindow::SetSizeMode [%p] %d\n", (void *)this, aMode));
+
+ // Save the requested state.
+ nsBaseWidget::SetSizeMode(aMode);
+
+ // return if there's no shell or our current state is the same as
+ // the mode we were just set to.
+ if (!mShell || mSizeState == mSizeMode) {
+ return;
+ }
+
+ switch (aMode) {
+ case nsSizeMode_Maximized:
+ gtk_window_maximize(GTK_WINDOW(mShell));
+ break;
+ case nsSizeMode_Minimized:
+ gtk_window_iconify(GTK_WINDOW(mShell));
+ break;
+ case nsSizeMode_Fullscreen:
+ MakeFullScreen(true);
+ break;
+
+ default:
+ // nsSizeMode_Normal, really.
+ if (mSizeState == nsSizeMode_Minimized)
+ gtk_window_deiconify(GTK_WINDOW(mShell));
+ else if (mSizeState == nsSizeMode_Maximized)
+ gtk_window_unmaximize(GTK_WINDOW(mShell));
+ break;
+ }
+
+ mSizeState = mSizeMode;
+}
+
+typedef void (* SetUserTimeFunc)(GdkWindow* aWindow, guint32 aTimestamp);
+
+// This will become obsolete when new GTK APIs are widely supported,
+// as described here: http://bugzilla.gnome.org/show_bug.cgi?id=347375
+static void
+SetUserTimeAndStartupIDForActivatedWindow(GtkWidget* aWindow)
+{
+ nsGTKToolkit* GTKToolkit = nsGTKToolkit::GetToolkit();
+ if (!GTKToolkit)
+ return;
+
+ nsAutoCString desktopStartupID;
+ GTKToolkit->GetDesktopStartupID(&desktopStartupID);
+ if (desktopStartupID.IsEmpty()) {
+ // We don't have the data we need. Fall back to an
+ // approximation ... using the timestamp of the remote command
+ // being received as a guess for the timestamp of the user event
+ // that triggered it.
+ uint32_t timestamp = GTKToolkit->GetFocusTimestamp();
+ if (timestamp) {
+ gdk_window_focus(gtk_widget_get_window(aWindow), timestamp);
+ GTKToolkit->SetFocusTimestamp(0);
+ }
+ return;
+ }
+
+#if defined(MOZ_ENABLE_STARTUP_NOTIFICATION)
+ // TODO - Implement for non-X11 Gtk backends (Bug 726479)
+ if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
+ GdkWindow* gdkWindow = gtk_widget_get_window(aWindow);
+
+ GdkScreen* screen = gdk_window_get_screen(gdkWindow);
+ SnDisplay* snd =
+ sn_display_new(gdk_x11_display_get_xdisplay(gdk_window_get_display(gdkWindow)),
+ nullptr, nullptr);
+ if (!snd)
+ return;
+ SnLauncheeContext* ctx =
+ sn_launchee_context_new(snd, gdk_screen_get_number(screen),
+ desktopStartupID.get());
+ if (!ctx) {
+ sn_display_unref(snd);
+ return;
+ }
+
+ if (sn_launchee_context_get_id_has_timestamp(ctx)) {
+ gdk_x11_window_set_user_time(gdkWindow,
+ sn_launchee_context_get_timestamp(ctx));
+ }
+
+ sn_launchee_context_setup_window(ctx, gdk_x11_window_get_xid(gdkWindow));
+ sn_launchee_context_complete(ctx);
+
+ sn_launchee_context_unref(ctx);
+ sn_display_unref(snd);
+ }
+#endif
+
+ // If we used the startup ID, that already contains the focus timestamp;
+ // we don't want to reuse the timestamp next time we raise the window
+ GTKToolkit->SetFocusTimestamp(0);
+ GTKToolkit->SetDesktopStartupID(EmptyCString());
+}
+
+/* static */ guint32
+nsWindow::GetLastUserInputTime()
+{
+ // gdk_x11_display_get_user_time tracks button and key presses,
+ // DESKTOP_STARTUP_ID used to start the app, drop events from external
+ // drags, WM_DELETE_WINDOW delete events, but not usually mouse motion nor
+ // button and key releases. Therefore use the most recent of
+ // gdk_x11_display_get_user_time and the last time that we have seen.
+ guint32 timestamp =
+ gdk_x11_display_get_user_time(gdk_display_get_default());
+ if (sLastUserInputTime != GDK_CURRENT_TIME &&
+ TimestampIsNewerThan(sLastUserInputTime, timestamp)) {
+ return sLastUserInputTime;
+ }
+
+ return timestamp;
+}
+
+NS_IMETHODIMP
+nsWindow::SetFocus(bool aRaise)
+{
+ // Make sure that our owning widget has focus. If it doesn't try to
+ // grab it. Note that we don't set our focus flag in this case.
+
+ LOGFOCUS((" SetFocus %d [%p]\n", aRaise, (void *)this));
+
+ GtkWidget *owningWidget = GetMozContainerWidget();
+ if (!owningWidget)
+ return NS_ERROR_FAILURE;
+
+ // Raise the window if someone passed in true and the prefs are
+ // set properly.
+ GtkWidget *toplevelWidget = gtk_widget_get_toplevel(owningWidget);
+
+ if (gRaiseWindows && aRaise && toplevelWidget &&
+ !gtk_widget_has_focus(owningWidget) &&
+ !gtk_widget_has_focus(toplevelWidget)) {
+ GtkWidget* top_window = GetToplevelWidget();
+ if (top_window && (gtk_widget_get_visible(top_window)))
+ {
+ gdk_window_show_unraised(gtk_widget_get_window(top_window));
+ // Unset the urgency hint if possible.
+ SetUrgencyHint(top_window, false);
+ }
+ }
+
+ RefPtr<nsWindow> owningWindow = get_window_for_gtk_widget(owningWidget);
+ if (!owningWindow)
+ return NS_ERROR_FAILURE;
+
+ if (aRaise) {
+ // aRaise == true means request toplevel activation.
+
+ // This is asynchronous.
+ // If and when the window manager accepts the request, then the focus
+ // widget will get a focus-in-event signal.
+ if (gRaiseWindows && owningWindow->mIsShown && owningWindow->mShell &&
+ !gtk_window_is_active(GTK_WINDOW(owningWindow->mShell))) {
+
+ uint32_t timestamp = GDK_CURRENT_TIME;
+
+ nsGTKToolkit* GTKToolkit = nsGTKToolkit::GetToolkit();
+ if (GTKToolkit)
+ timestamp = GTKToolkit->GetFocusTimestamp();
+
+ LOGFOCUS((" requesting toplevel activation [%p]\n", (void *)this));
+ NS_ASSERTION(owningWindow->mWindowType != eWindowType_popup
+ || mParent,
+ "Presenting an override-redirect window");
+ gtk_window_present_with_time(GTK_WINDOW(owningWindow->mShell), timestamp);
+
+ if (GTKToolkit)
+ GTKToolkit->SetFocusTimestamp(0);
+ }
+
+ return NS_OK;
+ }
+
+ // aRaise == false means that keyboard events should be dispatched
+ // from this widget.
+
+ // Ensure owningWidget is the focused GtkWidget within its toplevel window.
+ //
+ // For eWindowType_popup, this GtkWidget may not actually be the one that
+ // receives the key events as it may be the parent window that is active.
+ if (!gtk_widget_is_focus(owningWidget)) {
+ // This is synchronous. It takes focus from a plugin or from a widget
+ // in an embedder. The focus manager already knows that this window
+ // is active so gBlockActivateEvent avoids another (unnecessary)
+ // activate notification.
+ gBlockActivateEvent = true;
+ gtk_widget_grab_focus(owningWidget);
+ gBlockActivateEvent = false;
+ }
+
+ // If this is the widget that already has focus, return.
+ if (gFocusWindow == this) {
+ LOGFOCUS((" already have focus [%p]\n", (void *)this));
+ return NS_OK;
+ }
+
+ // Set this window to be the focused child window
+ gFocusWindow = this;
+
+ if (mIMContext) {
+ mIMContext->OnFocusWindow(this);
+ }
+
+ LOGFOCUS((" widget now has focus in SetFocus() [%p]\n",
+ (void *)this));
+
+ return NS_OK;
+}
+
+LayoutDeviceIntRect
+nsWindow::GetScreenBounds()
+{
+ LayoutDeviceIntRect rect;
+ if (mIsTopLevel && mContainer) {
+ // use the point including window decorations
+ gint x, y;
+ gdk_window_get_root_origin(gtk_widget_get_window(GTK_WIDGET(mContainer)), &x, &y);
+ rect.MoveTo(GdkPointToDevicePixels({ x, y }));
+ } else {
+ rect.MoveTo(WidgetToScreenOffset());
+ }
+ // mBounds.Size() is the window bounds, not the window-manager frame
+ // bounds (bug 581863). gdk_window_get_frame_extents would give the
+ // frame bounds, but mBounds.Size() is returned here for consistency
+ // with Resize.
+ rect.SizeTo(mBounds.Size());
+ LOG(("GetScreenBounds %d,%d | %dx%d\n",
+ rect.x, rect.y, rect.width, rect.height));
+ return rect;
+}
+
+LayoutDeviceIntSize
+nsWindow::GetClientSize()
+{
+ return LayoutDeviceIntSize(mBounds.width, mBounds.height);
+}
+
+LayoutDeviceIntRect
+nsWindow::GetClientBounds()
+{
+ // GetBounds returns a rect whose top left represents the top left of the
+ // outer bounds, but whose width/height represent the size of the inner
+ // bounds (which is messed up).
+ LayoutDeviceIntRect rect = GetBounds();
+ rect.MoveBy(GetClientOffset());
+ return rect;
+}
+
+void
+nsWindow::UpdateClientOffset()
+{
+ PROFILER_LABEL("nsWindow", "UpdateClientOffset", js::ProfileEntry::Category::GRAPHICS);
+
+ if (!mIsTopLevel || !mShell || !mGdkWindow || !mIsX11Display ||
+ gtk_window_get_window_type(GTK_WINDOW(mShell)) == GTK_WINDOW_POPUP) {
+ mClientOffset = nsIntPoint(0, 0);
+ return;
+ }
+
+ GdkAtom cardinal_atom = gdk_x11_xatom_to_atom(XA_CARDINAL);
+
+ GdkAtom type_returned;
+ int format_returned;
+ int length_returned;
+ long *frame_extents;
+
+ if (!gdk_property_get(mGdkWindow,
+ gdk_atom_intern ("_NET_FRAME_EXTENTS", FALSE),
+ cardinal_atom,
+ 0, // offset
+ 4*4, // length
+ FALSE, // delete
+ &type_returned,
+ &format_returned,
+ &length_returned,
+ (guchar **) &frame_extents) ||
+ length_returned/sizeof(glong) != 4) {
+ mClientOffset = nsIntPoint(0, 0);
+ return;
+ }
+
+ // data returned is in the order left, right, top, bottom
+ int32_t left = int32_t(frame_extents[0]);
+ int32_t top = int32_t(frame_extents[2]);
+
+ g_free(frame_extents);
+
+ mClientOffset = nsIntPoint(left, top);
+}
+
+LayoutDeviceIntPoint
+nsWindow::GetClientOffset()
+{
+ return LayoutDeviceIntPoint::FromUnknownPoint(mClientOffset);
+}
+
+gboolean
+nsWindow::OnPropertyNotifyEvent(GtkWidget* aWidget, GdkEventProperty* aEvent)
+
+{
+ if (aEvent->atom == gdk_atom_intern("_NET_FRAME_EXTENTS", FALSE)) {
+ UpdateClientOffset();
+ return FALSE;
+ }
+
+ if (GetCurrentTimeGetter()->PropertyNotifyHandler(aWidget, aEvent)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+NS_IMETHODIMP
+nsWindow::SetCursor(nsCursor aCursor)
+{
+ // if we're not the toplevel window pass up the cursor request to
+ // the toplevel window to handle it.
+ if (!mContainer && mGdkWindow) {
+ nsWindow *window = GetContainerWindow();
+ if (!window)
+ return NS_ERROR_FAILURE;
+
+ return window->SetCursor(aCursor);
+ }
+
+ // Only change cursor if it's actually been changed
+ if (aCursor != mCursor || mUpdateCursor) {
+ GdkCursor *newCursor = nullptr;
+ mUpdateCursor = false;
+
+ newCursor = get_gtk_cursor(aCursor);
+
+ if (nullptr != newCursor) {
+ mCursor = aCursor;
+
+ if (!mContainer)
+ return NS_OK;
+
+ gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)), newCursor);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::SetCursor(imgIContainer* aCursor,
+ uint32_t aHotspotX, uint32_t aHotspotY)
+{
+ // if we're not the toplevel window pass up the cursor request to
+ // the toplevel window to handle it.
+ if (!mContainer && mGdkWindow) {
+ nsWindow *window = GetContainerWindow();
+ if (!window)
+ return NS_ERROR_FAILURE;
+
+ return window->SetCursor(aCursor, aHotspotX, aHotspotY);
+ }
+
+ mCursor = nsCursor(-1);
+
+ // Get the image's current frame
+ GdkPixbuf* pixbuf = nsImageToPixbuf::ImageToPixbuf(aCursor);
+ if (!pixbuf)
+ return NS_ERROR_NOT_AVAILABLE;
+
+ int width = gdk_pixbuf_get_width(pixbuf);
+ int height = gdk_pixbuf_get_height(pixbuf);
+ // Reject cursors greater than 128 pixels in some direction, to prevent
+ // spoofing.
+ // XXX ideally we should rescale. Also, we could modify the API to
+ // allow trusted content to set larger cursors.
+ if (width > 128 || height > 128) {
+ g_object_unref(pixbuf);
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // Looks like all cursors need an alpha channel (tested on Gtk 2.4.4). This
+ // is of course not documented anywhere...
+ // So add one if there isn't one yet
+ if (!gdk_pixbuf_get_has_alpha(pixbuf)) {
+ GdkPixbuf* alphaBuf = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0);
+ g_object_unref(pixbuf);
+ if (!alphaBuf) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ pixbuf = alphaBuf;
+ }
+
+ GdkCursor* cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(),
+ pixbuf,
+ aHotspotX, aHotspotY);
+ g_object_unref(pixbuf);
+ nsresult rv = NS_ERROR_OUT_OF_MEMORY;
+ if (cursor) {
+ if (mContainer) {
+ gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)), cursor);
+ rv = NS_OK;
+ }
+#if (MOZ_WIDGET_GTK == 3)
+ g_object_unref(cursor);
+#else
+ gdk_cursor_unref(cursor);
+#endif
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsWindow::Invalidate(const LayoutDeviceIntRect& aRect)
+{
+ if (!mGdkWindow)
+ return NS_OK;
+
+ GdkRectangle rect = DevicePixelsToGdkRectRoundOut(aRect);
+ gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
+
+ LOGDRAW(("Invalidate (rect) [%p]: %d %d %d %d\n", (void *)this,
+ rect.x, rect.y, rect.width, rect.height));
+
+ return NS_OK;
+}
+
+void*
+nsWindow::GetNativeData(uint32_t aDataType)
+{
+ switch (aDataType) {
+ case NS_NATIVE_WINDOW:
+ case NS_NATIVE_WIDGET: {
+ if (!mGdkWindow)
+ return nullptr;
+
+ return mGdkWindow;
+ }
+
+ case NS_NATIVE_PLUGIN_PORT:
+ return SetupPluginPort();
+
+ case NS_NATIVE_PLUGIN_ID:
+ if (!mPluginNativeWindow) {
+ NS_WARNING("no native plugin instance!");
+ return nullptr;
+ }
+ // Return the socket widget XID
+ return (void*)mPluginNativeWindow->window;
+
+ case NS_NATIVE_DISPLAY: {
+#ifdef MOZ_X11
+ GdkDisplay* gdkDisplay = gdk_display_get_default();
+ if (GDK_IS_X11_DISPLAY(gdkDisplay)) {
+ return GDK_DISPLAY_XDISPLAY(gdkDisplay);
+ }
+#endif /* MOZ_X11 */
+ return nullptr;
+ }
+ case NS_NATIVE_SHELLWIDGET:
+ return GetToplevelWidget();
+
+ case NS_NATIVE_SHAREABLE_WINDOW:
+ return (void *) GDK_WINDOW_XID(gdk_window_get_toplevel(mGdkWindow));
+ case NS_NATIVE_PLUGIN_OBJECT_PTR:
+ return (void *) mPluginNativeWindow;
+ case NS_RAW_NATIVE_IME_CONTEXT: {
+ void* pseudoIMEContext = GetPseudoIMEContext();
+ if (pseudoIMEContext) {
+ return pseudoIMEContext;
+ }
+ // If IME context isn't available on this widget, we should set |this|
+ // instead of nullptr.
+ if (!mIMContext) {
+ return this;
+ }
+ return mIMContext.get();
+ }
+ case NS_NATIVE_OPENGL_CONTEXT:
+ return nullptr;
+#ifdef MOZ_X11
+ case NS_NATIVE_COMPOSITOR_DISPLAY:
+ return gfxPlatformGtk::GetPlatform()->GetCompositorDisplay();
+#endif // MOZ_X11
+ default:
+ NS_WARNING("nsWindow::GetNativeData called with bad value");
+ return nullptr;
+ }
+}
+
+void
+nsWindow::SetNativeData(uint32_t aDataType, uintptr_t aVal)
+{
+ if (aDataType != NS_NATIVE_PLUGIN_OBJECT_PTR) {
+ NS_WARNING("nsWindow::SetNativeData called with bad value");
+ return;
+ }
+ mPluginNativeWindow = (nsPluginNativeWindowGtk*)aVal;
+}
+
+NS_IMETHODIMP
+nsWindow::SetTitle(const nsAString& aTitle)
+{
+ if (!mShell)
+ return NS_OK;
+
+ // convert the string into utf8 and set the title.
+#define UTF8_FOLLOWBYTE(ch) (((ch) & 0xC0) == 0x80)
+ NS_ConvertUTF16toUTF8 titleUTF8(aTitle);
+ if (titleUTF8.Length() > NS_WINDOW_TITLE_MAX_LENGTH) {
+ // Truncate overlong titles (bug 167315). Make sure we chop after a
+ // complete sequence by making sure the next char isn't a follow-byte.
+ uint32_t len = NS_WINDOW_TITLE_MAX_LENGTH;
+ while(UTF8_FOLLOWBYTE(titleUTF8[len]))
+ --len;
+ titleUTF8.Truncate(len);
+ }
+ gtk_window_set_title(GTK_WINDOW(mShell), (const char *)titleUTF8.get());
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::SetIcon(const nsAString& aIconSpec)
+{
+ if (!mShell)
+ return NS_OK;
+
+ nsAutoCString iconName;
+
+ if (aIconSpec.EqualsLiteral("default")) {
+ nsXPIDLString brandName;
+ GetBrandName(brandName);
+ AppendUTF16toUTF8(brandName, iconName);
+ ToLowerCase(iconName);
+ } else {
+ AppendUTF16toUTF8(aIconSpec, iconName);
+ }
+
+ nsCOMPtr<nsIFile> iconFile;
+ nsAutoCString path;
+
+ gint *iconSizes =
+ gtk_icon_theme_get_icon_sizes(gtk_icon_theme_get_default(),
+ iconName.get());
+ bool foundIcon = (iconSizes[0] != 0);
+ g_free(iconSizes);
+
+ if (!foundIcon) {
+ // Look for icons with the following suffixes appended to the base name
+ // The last two entries (for the old XPM format) will be ignored unless
+ // no icons are found using other suffixes. XPM icons are deprecated.
+
+ const char extensions[6][7] = { ".png", "16.png", "32.png", "48.png",
+ ".xpm", "16.xpm" };
+
+ for (uint32_t i = 0; i < ArrayLength(extensions); i++) {
+ // Don't bother looking for XPM versions if we found a PNG.
+ if (i == ArrayLength(extensions) - 2 && foundIcon)
+ break;
+
+ nsAutoString extension;
+ extension.AppendASCII(extensions[i]);
+
+ ResolveIconName(aIconSpec, extension, getter_AddRefs(iconFile));
+ if (iconFile) {
+ iconFile->GetNativePath(path);
+ GdkPixbuf *icon = gdk_pixbuf_new_from_file(path.get(), nullptr);
+ if (icon) {
+ gtk_icon_theme_add_builtin_icon(iconName.get(),
+ gdk_pixbuf_get_height(icon),
+ icon);
+ g_object_unref(icon);
+ foundIcon = true;
+ }
+ }
+ }
+ }
+
+ // leave the default icon intact if no matching icons were found
+ if (foundIcon) {
+ gtk_window_set_icon_name(GTK_WINDOW(mShell), iconName.get());
+ }
+
+ return NS_OK;
+}
+
+
+LayoutDeviceIntPoint
+nsWindow::WidgetToScreenOffset()
+{
+ gint x = 0, y = 0;
+
+ if (mGdkWindow) {
+ gdk_window_get_origin(mGdkWindow, &x, &y);
+ }
+
+ return GdkPointToDevicePixels({ x, y });
+}
+
+void
+nsWindow::CaptureMouse(bool aCapture)
+{
+ LOG(("CaptureMouse %p\n", (void *)this));
+
+ if (!mGdkWindow)
+ return;
+
+ if (!mContainer)
+ return;
+
+ if (aCapture) {
+ gtk_grab_add(GTK_WIDGET(mContainer));
+ GrabPointer(GetLastUserInputTime());
+ }
+ else {
+ ReleaseGrabs();
+ gtk_grab_remove(GTK_WIDGET(mContainer));
+ }
+}
+
+void
+nsWindow::CaptureRollupEvents(nsIRollupListener *aListener,
+ bool aDoCapture)
+{
+ if (!mGdkWindow)
+ return;
+
+ if (!mContainer)
+ return;
+
+ LOG(("CaptureRollupEvents %p %i\n", this, int(aDoCapture)));
+
+ if (aDoCapture) {
+ gRollupListener = aListener;
+ // Don't add a grab if a drag is in progress, or if the widget is a drag
+ // feedback popup. (panels with type="drag").
+ if (!mIsDragPopup && !nsWindow::DragInProgress()) {
+ gtk_grab_add(GTK_WIDGET(mContainer));
+ GrabPointer(GetLastUserInputTime());
+ }
+ }
+ else {
+ if (!nsWindow::DragInProgress()) {
+ ReleaseGrabs();
+ }
+ // There may not have been a drag in process when aDoCapture was set,
+ // so make sure to remove any added grab. This is a no-op if the grab
+ // was not added to this widget.
+ gtk_grab_remove(GTK_WIDGET(mContainer));
+ gRollupListener = nullptr;
+ }
+}
+
+NS_IMETHODIMP
+nsWindow::GetAttention(int32_t aCycleCount)
+{
+ LOG(("nsWindow::GetAttention [%p]\n", (void *)this));
+
+ GtkWidget* top_window = GetToplevelWidget();
+ GtkWidget* top_focused_window =
+ gFocusWindow ? gFocusWindow->GetToplevelWidget() : nullptr;
+
+ // Don't get attention if the window is focused anyway.
+ if (top_window && (gtk_widget_get_visible(top_window)) &&
+ top_window != top_focused_window) {
+ SetUrgencyHint(top_window, true);
+ }
+
+ return NS_OK;
+}
+
+bool
+nsWindow::HasPendingInputEvent()
+{
+ // This sucks, but gtk/gdk has no way to answer the question we want while
+ // excluding paint events, and there's no X API that will let us peek
+ // without blocking or removing. To prevent event reordering, peek
+ // anything except expose events. Reordering expose and others should be
+ // ok, hopefully.
+ bool haveEvent = false;
+#ifdef MOZ_X11
+ XEvent ev;
+ if (mIsX11Display) {
+ Display *display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
+ haveEvent =
+ XCheckMaskEvent(display,
+ KeyPressMask | KeyReleaseMask | ButtonPressMask |
+ ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
+ PointerMotionMask | PointerMotionHintMask |
+ Button1MotionMask | Button2MotionMask |
+ Button3MotionMask | Button4MotionMask |
+ Button5MotionMask | ButtonMotionMask | KeymapStateMask |
+ VisibilityChangeMask | StructureNotifyMask |
+ ResizeRedirectMask | SubstructureNotifyMask |
+ SubstructureRedirectMask | FocusChangeMask |
+ PropertyChangeMask | ColormapChangeMask |
+ OwnerGrabButtonMask, &ev);
+ if (haveEvent) {
+ XPutBackEvent(display, &ev);
+ }
+ }
+#endif
+ return haveEvent;
+}
+
+#if 0
+#ifdef DEBUG
+// Paint flashing code (disabled for cairo - see below)
+
+#define CAPS_LOCK_IS_ON \
+(KeymapWrapper::AreModifiersCurrentlyActive(KeymapWrapper::CAPS_LOCK))
+
+#define WANT_PAINT_FLASHING \
+(debug_WantPaintFlashing() && CAPS_LOCK_IS_ON)
+
+#ifdef MOZ_X11
+static void
+gdk_window_flash(GdkWindow * aGdkWindow,
+ unsigned int aTimes,
+ unsigned int aInterval, // Milliseconds
+ GdkRegion * aRegion)
+{
+ gint x;
+ gint y;
+ gint width;
+ gint height;
+ guint i;
+ GdkGC * gc = 0;
+ GdkColor white;
+
+#if (MOZ_WIDGET_GTK == 2)
+ gdk_window_get_geometry(aGdkWindow,nullptr,nullptr,&width,&height,nullptr);
+#else
+ gdk_window_get_geometry(aGdkWindow,nullptr,nullptr,&width,&height);
+#endif
+
+ gdk_window_get_origin (aGdkWindow,
+ &x,
+ &y);
+
+ gc = gdk_gc_new(gdk_get_default_root_window());
+
+ white.pixel = WhitePixel(gdk_display,DefaultScreen(gdk_display));
+
+ gdk_gc_set_foreground(gc,&white);
+ gdk_gc_set_function(gc,GDK_XOR);
+ gdk_gc_set_subwindow(gc,GDK_INCLUDE_INFERIORS);
+
+ gdk_region_offset(aRegion, x, y);
+ gdk_gc_set_clip_region(gc, aRegion);
+
+ /*
+ * Need to do this twice so that the XOR effect can replace
+ * the original window contents.
+ */
+ for (i = 0; i < aTimes * 2; i++)
+ {
+ gdk_draw_rectangle(gdk_get_default_root_window(),
+ gc,
+ TRUE,
+ x,
+ y,
+ width,
+ height);
+
+ gdk_flush();
+
+ PR_Sleep(PR_MillisecondsToInterval(aInterval));
+ }
+
+ gdk_gc_destroy(gc);
+
+ gdk_region_offset(aRegion, -x, -y);
+}
+#endif /* MOZ_X11 */
+#endif // DEBUG
+#endif
+
+#if (MOZ_WIDGET_GTK == 2)
+static bool
+ExtractExposeRegion(LayoutDeviceIntRegion& aRegion, GdkEventExpose* aEvent)
+{
+ GdkRectangle* rects;
+ gint nrects;
+ gdk_region_get_rectangles(aEvent->region, &rects, &nrects);
+
+ if (nrects > MAX_RECTS_IN_REGION) {
+ // Just use the bounding box
+ rects[0] = aEvent->area;
+ nrects = 1;
+ }
+
+ for (GdkRectangle* r = rects; r < rects + nrects; r++) {
+ aRegion.Or(aRegion, LayoutDeviceIntRect(r->x, r->y, r->width, r->height));
+ LOGDRAW(("\t%d %d %d %d\n", r->x, r->y, r->width, r->height));
+ }
+
+ g_free(rects);
+ return true;
+}
+
+#else
+# ifdef cairo_copy_clip_rectangle_list
+# error "Looks like we're including Mozilla's cairo instead of system cairo"
+# endif
+static bool
+ExtractExposeRegion(LayoutDeviceIntRegion& aRegion, cairo_t* cr)
+{
+ cairo_rectangle_list_t* rects = cairo_copy_clip_rectangle_list(cr);
+ if (rects->status != CAIRO_STATUS_SUCCESS) {
+ NS_WARNING("Failed to obtain cairo rectangle list.");
+ return false;
+ }
+
+ for (int i = 0; i < rects->num_rectangles; i++) {
+ const cairo_rectangle_t& r = rects->rectangles[i];
+ aRegion.Or(aRegion, LayoutDeviceIntRect::Truncate(r.x, r.y, r.width, r.height));
+ LOGDRAW(("\t%d %d %d %d\n", r.x, r.y, r.width, r.height));
+ }
+
+ cairo_rectangle_list_destroy(rects);
+ return true;
+}
+#endif
+
+#if (MOZ_WIDGET_GTK == 2)
+gboolean
+nsWindow::OnExposeEvent(GdkEventExpose *aEvent)
+#else
+gboolean
+nsWindow::OnExposeEvent(cairo_t *cr)
+#endif
+{
+ // Send any pending resize events so that layout can update.
+ // May run event loop.
+ MaybeDispatchResized();
+
+ if (mIsDestroyed) {
+ return FALSE;
+ }
+
+ // Windows that are not visible will be painted after they become visible.
+ if (!mGdkWindow || mIsFullyObscured || !mHasMappedToplevel)
+ return FALSE;
+
+ nsIWidgetListener *listener = GetListener();
+ if (!listener)
+ return FALSE;
+
+ LayoutDeviceIntRegion exposeRegion;
+#if (MOZ_WIDGET_GTK == 2)
+ if (!ExtractExposeRegion(exposeRegion, aEvent)) {
+#else
+ if (!ExtractExposeRegion(exposeRegion, cr)) {
+#endif
+ return FALSE;
+ }
+
+ gint scale = GdkScaleFactor();
+ LayoutDeviceIntRegion region = exposeRegion;
+ region.ScaleRoundOut(scale, scale);
+
+ ClientLayerManager *clientLayers = GetLayerManager()->AsClientLayerManager();
+
+ if (clientLayers && mCompositorSession) {
+ // We need to paint to the screen even if nothing changed, since if we
+ // don't have a compositing window manager, our pixels could be stale.
+ clientLayers->SetNeedsComposite(true);
+ clientLayers->SendInvalidRegion(region.ToUnknownRegion());
+ }
+
+ RefPtr<nsWindow> strongThis(this);
+
+ // Dispatch WillPaintWindow notification to allow scripts etc. to run
+ // before we paint
+ {
+ listener->WillPaintWindow(this);
+
+ // If the window has been destroyed during the will paint notification,
+ // there is nothing left to do.
+ if (!mGdkWindow)
+ return TRUE;
+
+ // Re-get the listener since the will paint notification might have
+ // killed it.
+ listener = GetListener();
+ if (!listener)
+ return FALSE;
+ }
+
+ if (clientLayers && clientLayers->NeedsComposite()) {
+ clientLayers->Composite();
+ clientLayers->SetNeedsComposite(false);
+ }
+
+ LOGDRAW(("sending expose event [%p] %p 0x%lx (rects follow):\n",
+ (void *)this, (void *)mGdkWindow,
+ gdk_x11_window_get_xid(mGdkWindow)));
+
+ // Our bounds may have changed after calling WillPaintWindow. Clip
+ // to the new bounds here. The region is relative to this
+ // window.
+ region.And(region, LayoutDeviceIntRect(0, 0, mBounds.width, mBounds.height));
+
+ bool shaped = false;
+ if (eTransparencyTransparent == GetTransparencyMode()) {
+ GdkScreen *screen = gdk_window_get_screen(mGdkWindow);
+ if (gdk_screen_is_composited(screen) &&
+ gdk_window_get_visual(mGdkWindow) ==
+ gdk_screen_get_rgba_visual(screen)) {
+ // Remove possible shape mask from when window manger was not
+ // previously compositing.
+ static_cast<nsWindow*>(GetTopLevelWidget())->
+ ClearTransparencyBitmap();
+ } else {
+ shaped = true;
+ }
+ }
+
+ if (!shaped) {
+ GList *children =
+ gdk_window_peek_children(mGdkWindow);
+ while (children) {
+ GdkWindow *gdkWin = GDK_WINDOW(children->data);
+ nsWindow *kid = get_window_for_gdk_window(gdkWin);
+ if (kid && gdk_window_is_visible(gdkWin)) {
+ AutoTArray<LayoutDeviceIntRect,1> clipRects;
+ kid->GetWindowClipRegion(&clipRects);
+ LayoutDeviceIntRect bounds = kid->GetBounds();
+ for (uint32_t i = 0; i < clipRects.Length(); ++i) {
+ LayoutDeviceIntRect r = clipRects[i] + bounds.TopLeft();
+ region.Sub(region, r);
+ }
+ }
+ children = children->next;
+ }
+ }
+
+ if (region.IsEmpty()) {
+ return TRUE;
+ }
+
+ // If this widget uses OMTC...
+ if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) {
+ listener->PaintWindow(this, region);
+
+ // Re-get the listener since the will paint notification might have
+ // killed it.
+ listener = GetListener();
+ if (!listener)
+ return TRUE;
+
+ listener->DidPaintWindow();
+ return TRUE;
+ }
+
+ BufferMode layerBuffering = BufferMode::BUFFERED;
+ RefPtr<DrawTarget> dt = StartRemoteDrawingInRegion(region, &layerBuffering);
+ if (!dt || !dt->IsValid()) {
+ return FALSE;
+ }
+ RefPtr<gfxContext> ctx;
+ IntRect boundsRect = region.GetBounds().ToUnknownRect();
+ IntPoint offset(0, 0);
+ if (dt->GetSize() == boundsRect.Size()) {
+ offset = boundsRect.TopLeft();
+ dt->SetTransform(Matrix::Translation(-offset));
+ }
+
+#ifdef MOZ_X11
+ if (shaped) {
+ // Collapse update area to the bounding box. This is so we only have to
+ // call UpdateTranslucentWindowAlpha once. After we have dropped
+ // support for non-Thebes graphics, UpdateTranslucentWindowAlpha will be
+ // our private interface so we can rework things to avoid this.
+ dt->PushClipRect(Rect(boundsRect));
+
+ // The double buffering is done here to extract the shape mask.
+ // (The shape mask won't be necessary when a visual with an alpha
+ // channel is used on compositing window managers.)
+ layerBuffering = BufferMode::BUFFER_NONE;
+ RefPtr<DrawTarget> destDT = dt->CreateSimilarDrawTarget(boundsRect.Size(), SurfaceFormat::B8G8R8A8);
+ if (!destDT || !destDT->IsValid()) {
+ return FALSE;
+ }
+ destDT->SetTransform(Matrix::Translation(-boundsRect.TopLeft()));
+ ctx = gfxContext::CreatePreservingTransformOrNull(destDT);
+ } else {
+ gfxUtils::ClipToRegion(dt, region.ToUnknownRegion());
+ ctx = gfxContext::CreatePreservingTransformOrNull(dt);
+ }
+ MOZ_ASSERT(ctx); // checked both dt and destDT valid draw target above
+
+#if 0
+ // NOTE: Paint flashing region would be wrong for cairo, since
+ // cairo inflates the update region, etc. So don't paint flash
+ // for cairo.
+#ifdef DEBUG
+ // XXX aEvent->region may refer to a newly-invalid area. FIXME
+ if (0 && WANT_PAINT_FLASHING && gtk_widget_get_window(aEvent))
+ gdk_window_flash(mGdkWindow, 1, 100, aEvent->region);
+#endif
+#endif
+
+#endif // MOZ_X11
+
+ bool painted = false;
+ {
+ if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC) {
+ GdkScreen *screen = gdk_window_get_screen(mGdkWindow);
+ if (GetTransparencyMode() == eTransparencyTransparent &&
+ layerBuffering == BufferMode::BUFFER_NONE &&
+ gdk_screen_is_composited(screen) &&
+ gdk_window_get_visual(mGdkWindow) ==
+ gdk_screen_get_rgba_visual(screen)) {
+ // If our draw target is unbuffered and we use an alpha channel,
+ // clear the image beforehand to ensure we don't get artifacts from a
+ // reused SHM image. See bug 1258086.
+ dt->ClearRect(Rect(boundsRect));
+ }
+ AutoLayerManagerSetup setupLayerManager(this, ctx, layerBuffering);
+ painted = listener->PaintWindow(this, region);
+
+ // Re-get the listener since the will paint notification might have
+ // killed it.
+ listener = GetListener();
+ if (!listener)
+ return TRUE;
+
+ }
+ }
+
+#ifdef MOZ_X11
+ // PaintWindow can Destroy us (bug 378273), avoid doing any paint
+ // operations below if that happened - it will lead to XError and exit().
+ if (shaped) {
+ if (MOZ_LIKELY(!mIsDestroyed)) {
+ if (painted) {
+ RefPtr<SourceSurface> surf = ctx->GetDrawTarget()->Snapshot();
+
+ UpdateAlpha(surf, boundsRect);
+
+ dt->DrawSurface(surf, Rect(boundsRect), Rect(0, 0, boundsRect.width, boundsRect.height),
+ DrawSurfaceOptions(SamplingFilter::POINT),
+ DrawOptions(1.0f, CompositionOp::OP_SOURCE));
+ }
+ }
+ }
+
+ ctx = nullptr;
+ dt->PopClip();
+
+#endif // MOZ_X11
+
+ EndRemoteDrawingInRegion(dt, region);
+
+ listener->DidPaintWindow();
+
+ // Synchronously flush any new dirty areas
+#if (MOZ_WIDGET_GTK == 2)
+ GdkRegion* dirtyArea = gdk_window_get_update_area(mGdkWindow);
+#else
+ cairo_region_t* dirtyArea = gdk_window_get_update_area(mGdkWindow);
+#endif
+
+ if (dirtyArea) {
+ gdk_window_invalidate_region(mGdkWindow, dirtyArea, false);
+#if (MOZ_WIDGET_GTK == 2)
+ gdk_region_destroy(dirtyArea);
+#else
+ cairo_region_destroy(dirtyArea);
+#endif
+ gdk_window_process_updates(mGdkWindow, false);
+ }
+
+ // check the return value!
+ return TRUE;
+}
+
+void
+nsWindow::UpdateAlpha(SourceSurface* aSourceSurface, nsIntRect aBoundsRect)
+{
+ // We need to create our own buffer to force the stride to match the
+ // expected stride.
+ int32_t stride = GetAlignedStride<4>(aBoundsRect.width,
+ BytesPerPixel(SurfaceFormat::A8));
+ if (stride == 0) {
+ return;
+ }
+ int32_t bufferSize = stride * aBoundsRect.height;
+ auto imageBuffer = MakeUniqueFallible<uint8_t[]>(bufferSize);
+ {
+ RefPtr<DrawTarget> drawTarget = gfxPlatform::CreateDrawTargetForData(
+ imageBuffer.get(),
+ aBoundsRect.Size(),
+ stride, SurfaceFormat::A8);
+
+ if (drawTarget) {
+ drawTarget->DrawSurface(aSourceSurface, Rect(0, 0, aBoundsRect.width, aBoundsRect.height),
+ Rect(0, 0, aSourceSurface->GetSize().width, aSourceSurface->GetSize().height),
+ DrawSurfaceOptions(SamplingFilter::POINT),
+ DrawOptions(1.0f, CompositionOp::OP_SOURCE));
+ }
+ }
+ UpdateTranslucentWindowAlphaInternal(aBoundsRect, imageBuffer.get(), stride);
+}
+
+gboolean
+nsWindow::OnConfigureEvent(GtkWidget *aWidget, GdkEventConfigure *aEvent)
+{
+ // These events are only received on toplevel windows.
+ //
+ // GDK ensures that the coordinates are the client window top-left wrt the
+ // root window.
+ //
+ // GDK calculates the cordinates for real ConfigureNotify events on
+ // managed windows (that would normally be relative to the parent
+ // window).
+ //
+ // Synthetic ConfigureNotify events are from the window manager and
+ // already relative to the root window. GDK creates all X windows with
+ // border_width = 0, so synthetic events also indicate the top-left of
+ // the client window.
+ //
+ // Override-redirect windows are children of the root window so parent
+ // coordinates are root coordinates.
+
+ LOG(("configure event [%p] %d %d %d %d\n", (void *)this,
+ aEvent->x, aEvent->y, aEvent->width, aEvent->height));
+
+ if (mPendingConfigures > 0) {
+ mPendingConfigures--;
+ }
+
+ LayoutDeviceIntRect screenBounds = GetScreenBounds();
+
+ if (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog) {
+ // This check avoids unwanted rollup on spurious configure events from
+ // Cygwin/X (bug 672103).
+ if (mBounds.x != screenBounds.x ||
+ mBounds.y != screenBounds.y) {
+ CheckForRollup(0, 0, false, true);
+ }
+ }
+
+ // This event indicates that the window position may have changed.
+ // mBounds.Size() is updated in OnSizeAllocate().
+
+ NS_ASSERTION(GTK_IS_WINDOW(aWidget),
+ "Configure event on widget that is not a GtkWindow");
+ if (gtk_window_get_window_type(GTK_WINDOW(aWidget)) == GTK_WINDOW_POPUP) {
+ // Override-redirect window
+ //
+ // These windows should not be moved by the window manager, and so any
+ // change in position is a result of our direction. mBounds has
+ // already been set in Move() or Resize(), and that is more
+ // up-to-date than the position in the ConfigureNotify event if the
+ // event is from an earlier window move.
+ //
+ // Skipping the WindowMoved call saves context menus from an infinite
+ // loop when nsXULPopupManager::PopupMoved moves the window to the new
+ // position and nsMenuPopupFrame::SetPopupPosition adds
+ // offsetForContextMenu on each iteration.
+ return FALSE;
+ }
+
+ mBounds.MoveTo(screenBounds.TopLeft());
+
+ // XXX mozilla will invalidate the entire window after this move
+ // complete. wtf?
+ NotifyWindowMoved(mBounds.x, mBounds.y);
+
+ return FALSE;
+}
+
+void
+nsWindow::OnContainerUnrealize()
+{
+ // The GdkWindows are about to be destroyed (but not deleted), so remove
+ // their references back to their container widget while the GdkWindow
+ // hierarchy is still available.
+
+ if (mGdkWindow) {
+ DestroyChildWindows();
+
+ g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr);
+ mGdkWindow = nullptr;
+ }
+}
+
+void
+nsWindow::OnSizeAllocate(GtkAllocation *aAllocation)
+{
+ LOG(("size_allocate [%p] %d %d %d %d\n",
+ (void *)this, aAllocation->x, aAllocation->y,
+ aAllocation->width, aAllocation->height));
+
+ LayoutDeviceIntSize size = GdkRectToDevicePixels(*aAllocation).Size();
+
+ if (mBounds.Size() == size)
+ return;
+
+ // Invalidate the new part of the window now for the pending paint to
+ // minimize background flashes (GDK does not do this for external resizes
+ // of toplevels.)
+ if (mBounds.width < size.width) {
+ GdkRectangle rect = DevicePixelsToGdkRectRoundOut(
+ LayoutDeviceIntRect(mBounds.width, 0,
+ size.width - mBounds.width, size.height));
+ gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
+ }
+ if (mBounds.height < size.height) {
+ GdkRectangle rect = DevicePixelsToGdkRectRoundOut(
+ LayoutDeviceIntRect(0, mBounds.height,
+ size.width, size.height - mBounds.height));
+ gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
+ }
+
+ mBounds.SizeTo(size);
+
+#ifdef MOZ_X11
+ // Notify the X11CompositorWidget of a ClientSizeChange
+ if (mCompositorWidgetDelegate) {
+ mCompositorWidgetDelegate->NotifyClientSizeChanged(GetClientSize());
+ }
+#endif
+
+ // Gecko permits running nested event loops during processing of events,
+ // GtkWindow callers of gtk_widget_size_allocate expect the signal
+ // handlers to return sometime in the near future.
+ mNeedsDispatchResized = true;
+ NS_DispatchToCurrentThread(NewRunnableMethod(this, &nsWindow::MaybeDispatchResized));
+}
+
+void
+nsWindow::OnDeleteEvent()
+{
+ if (mWidgetListener)
+ mWidgetListener->RequestWindowClose(this);
+}
+
+void
+nsWindow::OnEnterNotifyEvent(GdkEventCrossing *aEvent)
+{
+ // This skips NotifyVirtual and NotifyNonlinearVirtual enter notify events
+ // when the pointer enters a child window. If the destination window is a
+ // Gecko window then we'll catch the corresponding event on that window,
+ // but we won't notice when the pointer directly enters a foreign (plugin)
+ // child window without passing over a visible portion of a Gecko window.
+ if (aEvent->subwindow != nullptr)
+ return;
+
+ // Check before is_parent_ungrab_enter() as the button state may have
+ // changed while a non-Gecko ancestor window had a pointer grab.
+ DispatchMissedButtonReleases(aEvent);
+
+ if (is_parent_ungrab_enter(aEvent))
+ return;
+
+ WidgetMouseEvent event(true, eMouseEnterIntoWidget, this,
+ WidgetMouseEvent::eReal);
+
+ event.mRefPoint = GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y);
+ event.AssignEventTime(GetWidgetEventTime(aEvent->time));
+
+ LOG(("OnEnterNotify: %p\n", (void *)this));
+
+ DispatchInputEvent(&event);
+}
+
+// XXX Is this the right test for embedding cases?
+static bool
+is_top_level_mouse_exit(GdkWindow* aWindow, GdkEventCrossing *aEvent)
+{
+ gint x = gint(aEvent->x_root);
+ gint y = gint(aEvent->y_root);
+ GdkDisplay* display = gdk_window_get_display(aWindow);
+ GdkWindow* winAtPt = gdk_display_get_window_at_pointer(display, &x, &y);
+ if (!winAtPt)
+ return true;
+ GdkWindow* topLevelAtPt = gdk_window_get_toplevel(winAtPt);
+ GdkWindow* topLevelWidget = gdk_window_get_toplevel(aWindow);
+ return topLevelAtPt != topLevelWidget;
+}
+
+void
+nsWindow::OnLeaveNotifyEvent(GdkEventCrossing *aEvent)
+{
+ // This ignores NotifyVirtual and NotifyNonlinearVirtual leave notify
+ // events when the pointer leaves a child window. If the destination
+ // window is a Gecko window then we'll catch the corresponding event on
+ // that window.
+ //
+ // XXXkt However, we will miss toplevel exits when the pointer directly
+ // leaves a foreign (plugin) child window without passing over a visible
+ // portion of a Gecko window.
+ if (aEvent->subwindow != nullptr)
+ return;
+
+ WidgetMouseEvent event(true, eMouseExitFromWidget, this,
+ WidgetMouseEvent::eReal);
+
+ event.mRefPoint = GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y);
+ event.AssignEventTime(GetWidgetEventTime(aEvent->time));
+
+ event.mExitFrom = is_top_level_mouse_exit(mGdkWindow, aEvent)
+ ? WidgetMouseEvent::eTopLevel : WidgetMouseEvent::eChild;
+
+ LOG(("OnLeaveNotify: %p\n", (void *)this));
+
+ DispatchInputEvent(&event);
+}
+
+template <typename Event> static LayoutDeviceIntPoint
+GetRefPoint(nsWindow* aWindow, Event* aEvent)
+{
+ if (aEvent->window == aWindow->GetGdkWindow()) {
+ // we are the window that the event happened on so no need for expensive WidgetToScreenOffset
+ return aWindow->GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y);
+ }
+ // XXX we're never quite sure which GdkWindow the event came from due to our custom bubbling
+ // in scroll_event_cb(), so use ScreenToWidget to translate the screen root coordinates into
+ // coordinates relative to this widget.
+ return aWindow->GdkEventCoordsToDevicePixels(
+ aEvent->x_root, aEvent->y_root) - aWindow->WidgetToScreenOffset();
+}
+
+void
+nsWindow::OnMotionNotifyEvent(GdkEventMotion *aEvent)
+{
+ // see if we can compress this event
+ // XXXldb Why skip every other motion event when we have multiple,
+ // but not more than that?
+ bool synthEvent = false;
+#ifdef MOZ_X11
+ XEvent xevent;
+
+ if (mIsX11Display) {
+ while (XPending (GDK_WINDOW_XDISPLAY(aEvent->window))) {
+ XEvent peeked;
+ XPeekEvent (GDK_WINDOW_XDISPLAY(aEvent->window), &peeked);
+ if (peeked.xany.window != gdk_x11_window_get_xid(aEvent->window)
+ || peeked.type != MotionNotify)
+ break;
+
+ synthEvent = true;
+ XNextEvent (GDK_WINDOW_XDISPLAY(aEvent->window), &xevent);
+ }
+#if (MOZ_WIDGET_GTK == 2)
+ // if plugins still keeps the focus, get it back
+ if (gPluginFocusWindow && gPluginFocusWindow != this) {
+ RefPtr<nsWindow> kungFuDeathGrip = gPluginFocusWindow;
+ gPluginFocusWindow->LoseNonXEmbedPluginFocus();
+ }
+#endif /* MOZ_WIDGET_GTK == 2 */
+ }
+#endif /* MOZ_X11 */
+
+ WidgetMouseEvent event(true, eMouseMove, this, WidgetMouseEvent::eReal);
+
+ gdouble pressure = 0;
+ gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
+ // Sometime gdk generate 0 pressure value between normal values
+ // We have to ignore that and use last valid value
+ if (pressure)
+ mLastMotionPressure = pressure;
+ event.pressure = mLastMotionPressure;
+
+ guint modifierState;
+ if (synthEvent) {
+#ifdef MOZ_X11
+ event.mRefPoint.x = nscoord(xevent.xmotion.x);
+ event.mRefPoint.y = nscoord(xevent.xmotion.y);
+
+ modifierState = xevent.xmotion.state;
+
+ event.AssignEventTime(GetWidgetEventTime(xevent.xmotion.time));
+#else
+ event.mRefPoint = GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y);
+
+ modifierState = aEvent->state;
+
+ event.AssignEventTime(GetWidgetEventTime(aEvent->time));
+#endif /* MOZ_X11 */
+ } else {
+ event.mRefPoint = GetRefPoint(this, aEvent);
+
+ modifierState = aEvent->state;
+
+ event.AssignEventTime(GetWidgetEventTime(aEvent->time));
+ }
+
+ KeymapWrapper::InitInputEvent(event, modifierState);
+
+ DispatchInputEvent(&event);
+}
+
+// If the automatic pointer grab on ButtonPress has deactivated before
+// ButtonRelease, and the mouse button is released while the pointer is not
+// over any a Gecko window, then the ButtonRelease event will not be received.
+// (A similar situation exists when the pointer is grabbed with owner_events
+// True as the ButtonRelease may be received on a foreign [plugin] window).
+// Use this method to check for released buttons when the pointer returns to a
+// Gecko window.
+void
+nsWindow::DispatchMissedButtonReleases(GdkEventCrossing *aGdkEvent)
+{
+ guint changed = aGdkEvent->state ^ gButtonState;
+ // Only consider button releases.
+ // (Ignore button presses that occurred outside Gecko.)
+ guint released = changed & gButtonState;
+ gButtonState = aGdkEvent->state;
+
+ // Loop over each button, excluding mouse wheel buttons 4 and 5 for which
+ // GDK ignores releases.
+ for (guint buttonMask = GDK_BUTTON1_MASK;
+ buttonMask <= GDK_BUTTON3_MASK;
+ buttonMask <<= 1) {
+
+ if (released & buttonMask) {
+ int16_t buttonType;
+ switch (buttonMask) {
+ case GDK_BUTTON1_MASK:
+ buttonType = WidgetMouseEvent::eLeftButton;
+ break;
+ case GDK_BUTTON2_MASK:
+ buttonType = WidgetMouseEvent::eMiddleButton;
+ break;
+ default:
+ NS_ASSERTION(buttonMask == GDK_BUTTON3_MASK,
+ "Unexpected button mask");
+ buttonType = WidgetMouseEvent::eRightButton;
+ }
+
+ LOG(("Synthesized button %u release on %p\n",
+ guint(buttonType + 1), (void *)this));
+
+ // Dispatch a synthesized button up event to tell Gecko about the
+ // change in state. This event is marked as synthesized so that
+ // it is not dispatched as a DOM event, because we don't know the
+ // position, widget, modifiers, or time/order.
+ WidgetMouseEvent synthEvent(true, eMouseUp, this,
+ WidgetMouseEvent::eSynthesized);
+ synthEvent.button = buttonType;
+ DispatchInputEvent(&synthEvent);
+ }
+ }
+}
+
+void
+nsWindow::InitButtonEvent(WidgetMouseEvent& aEvent,
+ GdkEventButton* aGdkEvent)
+{
+ aEvent.mRefPoint = GetRefPoint(this, aGdkEvent);
+
+ guint modifierState = aGdkEvent->state;
+ // aEvent's state includes the button state from immediately before this
+ // event. If aEvent is a mousedown or mouseup event, we need to update
+ // the button state.
+ guint buttonMask = 0;
+ switch (aGdkEvent->button) {
+ case 1:
+ buttonMask = GDK_BUTTON1_MASK;
+ break;
+ case 2:
+ buttonMask = GDK_BUTTON2_MASK;
+ break;
+ case 3:
+ buttonMask = GDK_BUTTON3_MASK;
+ break;
+ }
+ if (aGdkEvent->type == GDK_BUTTON_RELEASE) {
+ modifierState &= ~buttonMask;
+ } else {
+ modifierState |= buttonMask;
+ }
+
+ KeymapWrapper::InitInputEvent(aEvent, modifierState);
+
+ aEvent.AssignEventTime(GetWidgetEventTime(aGdkEvent->time));
+
+ switch (aGdkEvent->type) {
+ case GDK_2BUTTON_PRESS:
+ aEvent.mClickCount = 2;
+ break;
+ case GDK_3BUTTON_PRESS:
+ aEvent.mClickCount = 3;
+ break;
+ // default is one click
+ default:
+ aEvent.mClickCount = 1;
+ }
+}
+
+static guint ButtonMaskFromGDKButton(guint button)
+{
+ return GDK_BUTTON1_MASK << (button - 1);
+}
+
+void
+nsWindow::OnButtonPressEvent(GdkEventButton *aEvent)
+{
+ LOG(("Button %u press on %p\n", aEvent->button, (void *)this));
+
+ // If you double click in GDK, it will actually generate a second
+ // GDK_BUTTON_PRESS before sending the GDK_2BUTTON_PRESS, and this is
+ // different than the DOM spec. GDK puts this in the queue
+ // programatically, so it's safe to assume that if there's a
+ // double click in the queue, it was generated so we can just drop
+ // this click.
+ GdkEvent *peekedEvent = gdk_event_peek();
+ if (peekedEvent) {
+ GdkEventType type = peekedEvent->any.type;
+ gdk_event_free(peekedEvent);
+ if (type == GDK_2BUTTON_PRESS || type == GDK_3BUTTON_PRESS)
+ return;
+ }
+
+ nsWindow *containerWindow = GetContainerWindow();
+ if (!gFocusWindow && containerWindow) {
+ containerWindow->DispatchActivateEvent();
+ }
+
+ // check to see if we should rollup
+ if (CheckForRollup(aEvent->x_root, aEvent->y_root, false, false))
+ return;
+
+ gdouble pressure = 0;
+ gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
+ mLastMotionPressure = pressure;
+
+ uint16_t domButton;
+ switch (aEvent->button) {
+ case 1:
+ domButton = WidgetMouseEvent::eLeftButton;
+ break;
+ case 2:
+ domButton = WidgetMouseEvent::eMiddleButton;
+ break;
+ case 3:
+ domButton = WidgetMouseEvent::eRightButton;
+ break;
+ // These are mapped to horizontal scroll
+ case 6:
+ case 7:
+ NS_WARNING("We're not supporting legacy horizontal scroll event");
+ return;
+ // Map buttons 8-9 to back/forward
+ case 8:
+ DispatchCommandEvent(nsGkAtoms::Back);
+ return;
+ case 9:
+ DispatchCommandEvent(nsGkAtoms::Forward);
+ return;
+ default:
+ return;
+ }
+
+ gButtonState |= ButtonMaskFromGDKButton(aEvent->button);
+
+ WidgetMouseEvent event(true, eMouseDown, this, WidgetMouseEvent::eReal);
+ event.button = domButton;
+ InitButtonEvent(event, aEvent);
+ event.pressure = mLastMotionPressure;
+
+ DispatchInputEvent(&event);
+
+ // right menu click on linux should also pop up a context menu
+ if (domButton == WidgetMouseEvent::eRightButton &&
+ MOZ_LIKELY(!mIsDestroyed)) {
+ WidgetMouseEvent contextMenuEvent(true, eContextMenu, this,
+ WidgetMouseEvent::eReal);
+ InitButtonEvent(contextMenuEvent, aEvent);
+ contextMenuEvent.pressure = mLastMotionPressure;
+ DispatchInputEvent(&contextMenuEvent);
+ }
+}
+
+void
+nsWindow::OnButtonReleaseEvent(GdkEventButton *aEvent)
+{
+ LOG(("Button %u release on %p\n", aEvent->button, (void *)this));
+
+ uint16_t domButton;
+ switch (aEvent->button) {
+ case 1:
+ domButton = WidgetMouseEvent::eLeftButton;
+ break;
+ case 2:
+ domButton = WidgetMouseEvent::eMiddleButton;
+ break;
+ case 3:
+ domButton = WidgetMouseEvent::eRightButton;
+ break;
+ default:
+ return;
+ }
+
+ gButtonState &= ~ButtonMaskFromGDKButton(aEvent->button);
+
+ WidgetMouseEvent event(true, eMouseUp, this,
+ WidgetMouseEvent::eReal);
+ event.button = domButton;
+ InitButtonEvent(event, aEvent);
+ gdouble pressure = 0;
+ gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
+ event.pressure = pressure ? pressure : mLastMotionPressure;
+
+ DispatchInputEvent(&event);
+ mLastMotionPressure = pressure;
+}
+
+void
+nsWindow::OnContainerFocusInEvent(GdkEventFocus *aEvent)
+{
+ LOGFOCUS(("OnContainerFocusInEvent [%p]\n", (void *)this));
+
+ // Unset the urgency hint, if possible
+ GtkWidget* top_window = GetToplevelWidget();
+ if (top_window && (gtk_widget_get_visible(top_window)))
+ SetUrgencyHint(top_window, false);
+
+ // Return if being called within SetFocus because the focus manager
+ // already knows that the window is active.
+ if (gBlockActivateEvent) {
+ LOGFOCUS(("activated notification is blocked [%p]\n", (void *)this));
+ return;
+ }
+
+ // If keyboard input will be accepted, the focus manager will call
+ // SetFocus to set the correct window.
+ gFocusWindow = nullptr;
+
+ DispatchActivateEvent();
+
+ if (!gFocusWindow) {
+ // We don't really have a window for dispatching key events, but
+ // setting a non-nullptr value here prevents OnButtonPressEvent() from
+ // dispatching an activation notification if the widget is already
+ // active.
+ gFocusWindow = this;
+ }
+
+ LOGFOCUS(("Events sent from focus in event [%p]\n", (void *)this));
+}
+
+void
+nsWindow::OnContainerFocusOutEvent(GdkEventFocus *aEvent)
+{
+ LOGFOCUS(("OnContainerFocusOutEvent [%p]\n", (void *)this));
+
+ if (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog) {
+ nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
+ nsCOMPtr<nsIDragSession> dragSession;
+ dragService->GetCurrentSession(getter_AddRefs(dragSession));
+
+ // Rollup popups when a window is focused out unless a drag is occurring.
+ // This check is because drags grab the keyboard and cause a focus out on
+ // versions of GTK before 2.18.
+ bool shouldRollup = !dragSession;
+ if (!shouldRollup) {
+ // we also roll up when a drag is from a different application
+ nsCOMPtr<nsIDOMNode> sourceNode;
+ dragSession->GetSourceNode(getter_AddRefs(sourceNode));
+ shouldRollup = (sourceNode == nullptr);
+ }
+
+ if (shouldRollup) {
+ CheckForRollup(0, 0, false, true);
+ }
+ }
+
+#if (MOZ_WIDGET_GTK == 2) && defined(MOZ_X11)
+ // plugin lose focus
+ if (gPluginFocusWindow) {
+ RefPtr<nsWindow> kungFuDeathGrip = gPluginFocusWindow;
+ gPluginFocusWindow->LoseNonXEmbedPluginFocus();
+ }
+#endif /* MOZ_X11 && MOZ_WIDGET_GTK == 2 */
+
+ if (gFocusWindow) {
+ RefPtr<nsWindow> kungFuDeathGrip = gFocusWindow;
+ if (gFocusWindow->mIMContext) {
+ gFocusWindow->mIMContext->OnBlurWindow(gFocusWindow);
+ }
+ gFocusWindow = nullptr;
+ }
+
+ DispatchDeactivateEvent();
+
+ LOGFOCUS(("Done with container focus out [%p]\n", (void *)this));
+}
+
+bool
+nsWindow::DispatchCommandEvent(nsIAtom* aCommand)
+{
+ nsEventStatus status;
+ WidgetCommandEvent event(true, nsGkAtoms::onAppCommand, aCommand, this);
+ DispatchEvent(&event, status);
+ return TRUE;
+}
+
+bool
+nsWindow::DispatchContentCommandEvent(EventMessage aMsg)
+{
+ nsEventStatus status;
+ WidgetContentCommandEvent event(true, aMsg, this);
+ DispatchEvent(&event, status);
+ return TRUE;
+}
+
+static bool
+IsCtrlAltTab(GdkEventKey *aEvent)
+{
+ return aEvent->keyval == GDK_Tab &&
+ KeymapWrapper::AreModifiersActive(
+ KeymapWrapper::CTRL | KeymapWrapper::ALT, aEvent->state);
+}
+
+bool
+nsWindow::DispatchKeyDownEvent(GdkEventKey *aEvent, bool *aCancelled)
+{
+ NS_PRECONDITION(aCancelled, "aCancelled must not be null");
+
+ *aCancelled = false;
+
+ if (IsCtrlAltTab(aEvent)) {
+ return false;
+ }
+
+ RefPtr<TextEventDispatcher> dispatcher = GetTextEventDispatcher();
+ nsresult rv = dispatcher->BeginNativeInputTransaction();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return FALSE;
+ }
+
+ WidgetKeyboardEvent keydownEvent(true, eKeyDown, this);
+ KeymapWrapper::InitKeyEvent(keydownEvent, aEvent);
+ nsEventStatus status = nsEventStatus_eIgnore;
+ bool dispatched =
+ dispatcher->DispatchKeyboardEvent(eKeyDown, keydownEvent,
+ status, aEvent);
+ *aCancelled = (status == nsEventStatus_eConsumeNoDefault);
+ return dispatched ? TRUE : FALSE;
+}
+
+WidgetEventTime
+nsWindow::GetWidgetEventTime(guint32 aEventTime)
+{
+ return WidgetEventTime(aEventTime, GetEventTimeStamp(aEventTime));
+}
+
+TimeStamp
+nsWindow::GetEventTimeStamp(guint32 aEventTime)
+{
+ if (MOZ_UNLIKELY(!mGdkWindow)) {
+ // nsWindow has been Destroy()ed.
+ return TimeStamp::Now();
+ }
+ if (aEventTime == 0) {
+ // Some X11 and GDK events may be received with a time of 0 to indicate
+ // that they are synthetic events. Some input method editors do this.
+ // In this case too, just return the current timestamp.
+ return TimeStamp::Now();
+ }
+ CurrentX11TimeGetter* getCurrentTime = GetCurrentTimeGetter();
+ MOZ_ASSERT(getCurrentTime,
+ "Null current time getter despite having a window");
+ return TimeConverter().GetTimeStampFromSystemTime(aEventTime,
+ *getCurrentTime);
+}
+
+mozilla::CurrentX11TimeGetter*
+nsWindow::GetCurrentTimeGetter() {
+ MOZ_ASSERT(mGdkWindow, "Expected mGdkWindow to be set");
+ if (MOZ_UNLIKELY(!mCurrentTimeGetter)) {
+ mCurrentTimeGetter = MakeUnique<CurrentX11TimeGetter>(mGdkWindow);
+ }
+ return mCurrentTimeGetter.get();
+}
+
+gboolean
+nsWindow::OnKeyPressEvent(GdkEventKey *aEvent)
+{
+ LOGFOCUS(("OnKeyPressEvent [%p]\n", (void *)this));
+
+ // if we are in the middle of composing text, XIM gets to see it
+ // before mozilla does.
+ // FYI: Don't dispatch keydown event before notifying IME of the event
+ // because IME may send a key event synchronously and consume the
+ // original event.
+ bool IMEWasEnabled = false;
+ if (mIMContext) {
+ IMEWasEnabled = mIMContext->IsEnabled();
+ if (mIMContext->OnKeyEvent(this, aEvent)) {
+ return TRUE;
+ }
+ }
+
+ // work around for annoying things.
+ if (IsCtrlAltTab(aEvent)) {
+ return TRUE;
+ }
+
+ nsCOMPtr<nsIWidget> kungFuDeathGrip = this;
+
+ // Dispatch keydown event always. At auto repeating, we should send
+ // KEYDOWN -> KEYPRESS -> KEYDOWN -> KEYPRESS ... -> KEYUP
+ // However, old distributions (e.g., Ubuntu 9.10) sent native key
+ // release event, so, on such platform, the DOM events will be:
+ // KEYDOWN -> KEYPRESS -> KEYUP -> KEYDOWN -> KEYPRESS -> KEYUP...
+
+ bool isKeyDownCancelled = false;
+ if (DispatchKeyDownEvent(aEvent, &isKeyDownCancelled) &&
+ (MOZ_UNLIKELY(mIsDestroyed) || isKeyDownCancelled)) {
+ return TRUE;
+ }
+
+ // If a keydown event handler causes to enable IME, i.e., it moves
+ // focus from IME unusable content to IME usable editor, we should
+ // send the native key event to IME for the first input on the editor.
+ if (!IMEWasEnabled && mIMContext && mIMContext->IsEnabled()) {
+ // Notice our keydown event was already dispatched. This prevents
+ // unnecessary DOM keydown event in the editor.
+ if (mIMContext->OnKeyEvent(this, aEvent, true)) {
+ return TRUE;
+ }
+ }
+
+ // Look for specialized app-command keys
+ switch (aEvent->keyval) {
+ case GDK_Back:
+ return DispatchCommandEvent(nsGkAtoms::Back);
+ case GDK_Forward:
+ return DispatchCommandEvent(nsGkAtoms::Forward);
+ case GDK_Refresh:
+ return DispatchCommandEvent(nsGkAtoms::Reload);
+ case GDK_Stop:
+ return DispatchCommandEvent(nsGkAtoms::Stop);
+ case GDK_Search:
+ return DispatchCommandEvent(nsGkAtoms::Search);
+ case GDK_Favorites:
+ return DispatchCommandEvent(nsGkAtoms::Bookmarks);
+ case GDK_HomePage:
+ return DispatchCommandEvent(nsGkAtoms::Home);
+ case GDK_Copy:
+ case GDK_F16: // F16, F20, F18, F14 are old keysyms for Copy Cut Paste Undo
+ return DispatchContentCommandEvent(eContentCommandCopy);
+ case GDK_Cut:
+ case GDK_F20:
+ return DispatchContentCommandEvent(eContentCommandCut);
+ case GDK_Paste:
+ case GDK_F18:
+ return DispatchContentCommandEvent(eContentCommandPaste);
+ case GDK_Redo:
+ return DispatchContentCommandEvent(eContentCommandRedo);
+ case GDK_Undo:
+ case GDK_F14:
+ return DispatchContentCommandEvent(eContentCommandUndo);
+ }
+
+ WidgetKeyboardEvent keypressEvent(true, eKeyPress, this);
+ KeymapWrapper::InitKeyEvent(keypressEvent, aEvent);
+
+ // before we dispatch a key, check if it's the context menu key.
+ // If so, send a context menu key event instead.
+ if (is_context_menu_key(keypressEvent)) {
+ WidgetMouseEvent contextMenuEvent(true, eContextMenu, this,
+ WidgetMouseEvent::eReal,
+ WidgetMouseEvent::eContextMenuKey);
+
+ contextMenuEvent.mRefPoint = LayoutDeviceIntPoint(0, 0);
+ contextMenuEvent.AssignEventTime(GetWidgetEventTime(aEvent->time));
+ contextMenuEvent.mClickCount = 1;
+ KeymapWrapper::InitInputEvent(contextMenuEvent, aEvent->state);
+ DispatchInputEvent(&contextMenuEvent);
+ } else {
+ RefPtr<TextEventDispatcher> dispatcher = GetTextEventDispatcher();
+ nsresult rv = dispatcher->BeginNativeInputTransaction();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return TRUE;
+ }
+
+ // If the character code is in the BMP, send the key press event.
+ // Otherwise, send a compositionchange event with the equivalent UTF-16
+ // string.
+ // TODO: Investigate other browser's behavior in this case because
+ // this hack is odd for UI Events.
+ nsEventStatus status = nsEventStatus_eIgnore;
+ if (keypressEvent.mKeyNameIndex != KEY_NAME_INDEX_USE_STRING ||
+ keypressEvent.mKeyValue.Length() == 1) {
+ dispatcher->MaybeDispatchKeypressEvents(keypressEvent,
+ status, aEvent);
+ } else {
+ WidgetEventTime eventTime = GetWidgetEventTime(aEvent->time);
+ dispatcher->CommitComposition(status, &keypressEvent.mKeyValue,
+ &eventTime);
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+nsWindow::OnKeyReleaseEvent(GdkEventKey *aEvent)
+{
+ LOGFOCUS(("OnKeyReleaseEvent [%p]\n", (void *)this));
+
+ if (mIMContext && mIMContext->OnKeyEvent(this, aEvent)) {
+ return TRUE;
+ }
+
+ RefPtr<TextEventDispatcher> dispatcher = GetTextEventDispatcher();
+ nsresult rv = dispatcher->BeginNativeInputTransaction();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ WidgetKeyboardEvent keyupEvent(true, eKeyUp, this);
+ KeymapWrapper::InitKeyEvent(keyupEvent, aEvent);
+ nsEventStatus status = nsEventStatus_eIgnore;
+ dispatcher->DispatchKeyboardEvent(eKeyUp, keyupEvent, status, aEvent);
+
+ return TRUE;
+}
+
+void
+nsWindow::OnScrollEvent(GdkEventScroll *aEvent)
+{
+ // check to see if we should rollup
+ if (CheckForRollup(aEvent->x_root, aEvent->y_root, true, false))
+ return;
+#if GTK_CHECK_VERSION(3,4,0)
+ // check for duplicate legacy scroll event, see GNOME bug 726878
+ if (aEvent->direction != GDK_SCROLL_SMOOTH &&
+ mLastScrollEventTime == aEvent->time)
+ return;
+#endif
+ WidgetWheelEvent wheelEvent(true, eWheel, this);
+ wheelEvent.mDeltaMode = nsIDOMWheelEvent::DOM_DELTA_LINE;
+ switch (aEvent->direction) {
+#if GTK_CHECK_VERSION(3,4,0)
+ case GDK_SCROLL_SMOOTH:
+ {
+ // As of GTK 3.4, all directional scroll events are provided by
+ // the GDK_SCROLL_SMOOTH direction on XInput2 devices.
+ mLastScrollEventTime = aEvent->time;
+ // TODO - use a more appropriate scrolling unit than lines.
+ // Multiply event deltas by 3 to emulate legacy behaviour.
+ wheelEvent.mDeltaX = aEvent->delta_x * 3;
+ wheelEvent.mDeltaY = aEvent->delta_y * 3;
+ wheelEvent.mIsNoLineOrPageDelta = true;
+ // This next step manually unsets smooth scrolling for touch devices
+ // that trigger GDK_SCROLL_SMOOTH. We use the slave device, which
+ // represents the actual input.
+ GdkDevice *device = gdk_event_get_source_device((GdkEvent*)aEvent);
+ GdkInputSource source = gdk_device_get_source(device);
+ if (source == GDK_SOURCE_TOUCHSCREEN ||
+ source == GDK_SOURCE_TOUCHPAD) {
+ wheelEvent.mScrollType = WidgetWheelEvent::SCROLL_ASYNCHRONOUSELY;
+ }
+ break;
+ }
+#endif
+ case GDK_SCROLL_UP:
+ wheelEvent.mDeltaY = wheelEvent.mLineOrPageDeltaY = -3;
+ break;
+ case GDK_SCROLL_DOWN:
+ wheelEvent.mDeltaY = wheelEvent.mLineOrPageDeltaY = 3;
+ break;
+ case GDK_SCROLL_LEFT:
+ wheelEvent.mDeltaX = wheelEvent.mLineOrPageDeltaX = -1;
+ break;
+ case GDK_SCROLL_RIGHT:
+ wheelEvent.mDeltaX = wheelEvent.mLineOrPageDeltaX = 1;
+ break;
+ }
+
+ wheelEvent.mRefPoint = GetRefPoint(this, aEvent);
+
+ KeymapWrapper::InitInputEvent(wheelEvent, aEvent->state);
+
+ wheelEvent.AssignEventTime(GetWidgetEventTime(aEvent->time));
+
+ DispatchInputEvent(&wheelEvent);
+}
+
+void
+nsWindow::OnVisibilityNotifyEvent(GdkEventVisibility *aEvent)
+{
+ LOGDRAW(("Visibility event %i on [%p] %p\n",
+ aEvent->state, this, aEvent->window));
+
+ if (!mGdkWindow)
+ return;
+
+ switch (aEvent->state) {
+ case GDK_VISIBILITY_UNOBSCURED:
+ case GDK_VISIBILITY_PARTIAL:
+ if (mIsFullyObscured && mHasMappedToplevel) {
+ // GDK_EXPOSE events have been ignored, so make sure GDK
+ // doesn't think that the window has already been painted.
+ gdk_window_invalidate_rect(mGdkWindow, nullptr, FALSE);
+ }
+
+ mIsFullyObscured = false;
+
+ // if we have to retry the grab, retry it.
+ EnsureGrabs();
+ break;
+ default: // includes GDK_VISIBILITY_FULLY_OBSCURED
+ mIsFullyObscured = true;
+ break;
+ }
+}
+
+void
+nsWindow::OnWindowStateEvent(GtkWidget *aWidget, GdkEventWindowState *aEvent)
+{
+ LOG(("nsWindow::OnWindowStateEvent [%p] changed %d new_window_state %d\n",
+ (void *)this, aEvent->changed_mask, aEvent->new_window_state));
+
+ if (IS_MOZ_CONTAINER(aWidget)) {
+ // This event is notifying the container widget of changes to the
+ // toplevel window. Just detect changes affecting whether windows are
+ // viewable.
+ //
+ // (A visibility notify event is sent to each window that becomes
+ // viewable when the toplevel is mapped, but we can't rely on that for
+ // setting mHasMappedToplevel because these toplevel window state
+ // events are asynchronous. The windows in the hierarchy now may not
+ // be the same windows as when the toplevel was mapped, so they may
+ // not get VisibilityNotify events.)
+ bool mapped =
+ !(aEvent->new_window_state &
+ (GDK_WINDOW_STATE_ICONIFIED|GDK_WINDOW_STATE_WITHDRAWN));
+ if (mHasMappedToplevel != mapped) {
+ SetHasMappedToplevel(mapped);
+ }
+ return;
+ }
+ // else the widget is a shell widget.
+
+ // We don't care about anything but changes in the maximized/icon/fullscreen
+ // states
+ if ((aEvent->changed_mask
+ & (GDK_WINDOW_STATE_ICONIFIED |
+ GDK_WINDOW_STATE_MAXIMIZED |
+ GDK_WINDOW_STATE_FULLSCREEN)) == 0) {
+ return;
+ }
+
+ if (aEvent->new_window_state & GDK_WINDOW_STATE_ICONIFIED) {
+ LOG(("\tIconified\n"));
+ mSizeState = nsSizeMode_Minimized;
+#ifdef ACCESSIBILITY
+ DispatchMinimizeEventAccessible();
+#endif //ACCESSIBILITY
+ }
+ else if (aEvent->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) {
+ LOG(("\tFullscreen\n"));
+ mSizeState = nsSizeMode_Fullscreen;
+ }
+ else if (aEvent->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) {
+ LOG(("\tMaximized\n"));
+ mSizeState = nsSizeMode_Maximized;
+#ifdef ACCESSIBILITY
+ DispatchMaximizeEventAccessible();
+#endif //ACCESSIBILITY
+ }
+ else {
+ LOG(("\tNormal\n"));
+ mSizeState = nsSizeMode_Normal;
+#ifdef ACCESSIBILITY
+ DispatchRestoreEventAccessible();
+#endif //ACCESSIBILITY
+ }
+
+ if (mWidgetListener) {
+ mWidgetListener->SizeModeChanged(mSizeState);
+ if (aEvent->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
+ mWidgetListener->FullscreenChanged(
+ aEvent->new_window_state & GDK_WINDOW_STATE_FULLSCREEN);
+ }
+ }
+}
+
+void
+nsWindow::ThemeChanged()
+{
+ NotifyThemeChanged();
+
+ if (!mGdkWindow || MOZ_UNLIKELY(mIsDestroyed))
+ return;
+
+ // Dispatch theme change notification to all child windows
+ GList *children =
+ gdk_window_peek_children(mGdkWindow);
+ while (children) {
+ GdkWindow *gdkWin = GDK_WINDOW(children->data);
+
+ nsWindow *win = (nsWindow*) g_object_get_data(G_OBJECT(gdkWin),
+ "nsWindow");
+
+ if (win && win != this) { // guard against infinite recursion
+ RefPtr<nsWindow> kungFuDeathGrip = win;
+ win->ThemeChanged();
+ }
+
+ children = children->next;
+ }
+}
+
+void
+nsWindow::OnDPIChanged()
+{
+ if (mWidgetListener) {
+ nsIPresShell* presShell = mWidgetListener->GetPresShell();
+ if (presShell) {
+ presShell->BackingScaleFactorChanged();
+ // Update menu's font size etc
+ presShell->ThemeChanged();
+ }
+ }
+}
+
+void
+nsWindow::OnCheckResize()
+{
+ mPendingConfigures++;
+}
+
+void
+nsWindow::DispatchDragEvent(EventMessage aMsg, const LayoutDeviceIntPoint& aRefPoint,
+ guint aTime)
+{
+ WidgetDragEvent event(true, aMsg, this);
+
+ if (aMsg == eDragOver) {
+ InitDragEvent(event);
+ }
+
+ event.mRefPoint = aRefPoint;
+ event.AssignEventTime(GetWidgetEventTime(aTime));
+
+ DispatchInputEvent(&event);
+}
+
+void
+nsWindow::OnDragDataReceivedEvent(GtkWidget *aWidget,
+ GdkDragContext *aDragContext,
+ gint aX,
+ gint aY,
+ GtkSelectionData *aSelectionData,
+ guint aInfo,
+ guint aTime,
+ gpointer aData)
+{
+ LOGDRAG(("nsWindow::OnDragDataReceived(%p)\n", (void*)this));
+
+ nsDragService::GetInstance()->
+ TargetDataReceived(aWidget, aDragContext, aX, aY,
+ aSelectionData, aInfo, aTime);
+}
+
+#if GTK_CHECK_VERSION(3,4,0)
+gboolean
+nsWindow::OnTouchEvent(GdkEventTouch* aEvent)
+{
+ if (!mHandleTouchEvent) {
+ return FALSE;
+ }
+
+ EventMessage msg;
+ switch (aEvent->type) {
+ case GDK_TOUCH_BEGIN:
+ msg = eTouchStart;
+ break;
+ case GDK_TOUCH_UPDATE:
+ msg = eTouchMove;
+ break;
+ case GDK_TOUCH_END:
+ msg = eTouchEnd;
+ break;
+ case GDK_TOUCH_CANCEL:
+ msg = eTouchCancel;
+ break;
+ default:
+ return FALSE;
+ }
+
+ LayoutDeviceIntPoint touchPoint = GetRefPoint(this, aEvent);
+
+ int32_t id;
+ RefPtr<dom::Touch> touch;
+ if (mTouches.Remove(aEvent->sequence, getter_AddRefs(touch))) {
+ id = touch->mIdentifier;
+ } else {
+ id = ++gLastTouchID & 0x7FFFFFFF;
+ }
+
+ touch = new dom::Touch(id, touchPoint, LayoutDeviceIntPoint(1, 1),
+ 0.0f, 0.0f);
+
+ WidgetTouchEvent event(true, msg, this);
+ KeymapWrapper::InitInputEvent(event, aEvent->state);
+ event.mTime = aEvent->time;
+
+ if (aEvent->type == GDK_TOUCH_BEGIN || aEvent->type == GDK_TOUCH_UPDATE) {
+ mTouches.Put(aEvent->sequence, touch.forget());
+ // add all touch points to event object
+ for (auto iter = mTouches.Iter(); !iter.Done(); iter.Next()) {
+ event.mTouches.AppendElement(new dom::Touch(*iter.UserData()));
+ }
+ } else if (aEvent->type == GDK_TOUCH_END ||
+ aEvent->type == GDK_TOUCH_CANCEL) {
+ *event.mTouches.AppendElement() = touch.forget();
+ }
+
+ DispatchInputEvent(&event);
+ return TRUE;
+}
+#endif
+
+static void
+GetBrandName(nsXPIDLString& brandName)
+{
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ do_GetService(NS_STRINGBUNDLE_CONTRACTID);
+
+ nsCOMPtr<nsIStringBundle> bundle;
+ if (bundleService)
+ bundleService->CreateBundle(
+ "chrome://branding/locale/brand.properties",
+ getter_AddRefs(bundle));
+
+ if (bundle)
+ bundle->GetStringFromName(
+ u"brandShortName",
+ getter_Copies(brandName));
+
+ if (brandName.IsEmpty())
+ brandName.AssignLiteral(u"Mozilla");
+}
+
+static GdkWindow *
+CreateGdkWindow(GdkWindow *parent, GtkWidget *widget)
+{
+ GdkWindowAttr attributes;
+ gint attributes_mask = GDK_WA_VISUAL;
+
+ attributes.event_mask = kEvents;
+
+ attributes.width = 1;
+ attributes.height = 1;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.visual = gtk_widget_get_visual(widget);
+ attributes.window_type = GDK_WINDOW_CHILD;
+
+#if (MOZ_WIDGET_GTK == 2)
+ attributes_mask |= GDK_WA_COLORMAP;
+ attributes.colormap = gtk_widget_get_colormap(widget);
+#endif
+
+ GdkWindow *window = gdk_window_new(parent, &attributes, attributes_mask);
+ gdk_window_set_user_data(window, widget);
+
+// GTK3 TODO?
+#if (MOZ_WIDGET_GTK == 2)
+ /* set the default pixmap to None so that you don't end up with the
+ gtk default which is BlackPixel. */
+ gdk_window_set_back_pixmap(window, nullptr, FALSE);
+#endif
+
+ return window;
+}
+
+nsresult
+nsWindow::Create(nsIWidget* aParent,
+ nsNativeWidget aNativeParent,
+ const LayoutDeviceIntRect& aRect,
+ nsWidgetInitData* aInitData)
+{
+ // only set the base parent if we're going to be a dialog or a
+ // toplevel
+ nsIWidget *baseParent = aInitData &&
+ (aInitData->mWindowType == eWindowType_dialog ||
+ aInitData->mWindowType == eWindowType_toplevel ||
+ aInitData->mWindowType == eWindowType_invisible) ?
+ nullptr : aParent;
+
+#ifdef ACCESSIBILITY
+ // Send a DBus message to check whether a11y is enabled
+ a11y::PreInit();
+#endif
+
+ // Ensure that the toolkit is created.
+ nsGTKToolkit::GetToolkit();
+
+ // initialize all the common bits of this class
+ BaseCreate(baseParent, aInitData);
+
+ // Do we need to listen for resizes?
+ bool listenForResizes = false;;
+ if (aNativeParent || (aInitData && aInitData->mListenForResizes))
+ listenForResizes = true;
+
+ // and do our common creation
+ CommonCreate(aParent, listenForResizes);
+
+ // save our bounds
+ mBounds = aRect;
+ ConstrainSize(&mBounds.width, &mBounds.height);
+
+ // figure out our parent window
+ GtkWidget *parentMozContainer = nullptr;
+ GtkContainer *parentGtkContainer = nullptr;
+ GdkWindow *parentGdkWindow = nullptr;
+ GtkWindow *topLevelParent = nullptr;
+ nsWindow *parentnsWindow = nullptr;
+ GtkWidget *eventWidget = nullptr;
+ bool shellHasCSD = false;
+
+ if (aParent) {
+ parentnsWindow = static_cast<nsWindow*>(aParent);
+ parentGdkWindow = parentnsWindow->mGdkWindow;
+ } else if (aNativeParent && GDK_IS_WINDOW(aNativeParent)) {
+ parentGdkWindow = GDK_WINDOW(aNativeParent);
+ parentnsWindow = get_window_for_gdk_window(parentGdkWindow);
+ if (!parentnsWindow)
+ return NS_ERROR_FAILURE;
+
+ } else if (aNativeParent && GTK_IS_CONTAINER(aNativeParent)) {
+ parentGtkContainer = GTK_CONTAINER(aNativeParent);
+ }
+
+ if (parentGdkWindow) {
+ // get the widget for the window - it should be a moz container
+ parentMozContainer = parentnsWindow->GetMozContainerWidget();
+ if (!parentMozContainer)
+ return NS_ERROR_FAILURE;
+
+ // get the toplevel window just in case someone needs to use it
+ // for setting transients or whatever.
+ topLevelParent =
+ GTK_WINDOW(gtk_widget_get_toplevel(parentMozContainer));
+ }
+
+ // ok, create our windows
+ switch (mWindowType) {
+ case eWindowType_dialog:
+ case eWindowType_popup:
+ case eWindowType_toplevel:
+ case eWindowType_invisible: {
+ mIsTopLevel = true;
+
+ // Popups that are not noautohide are only temporary. The are used
+ // for menus and the like and disappear when another window is used.
+ // For most popups, use the standard GtkWindowType GTK_WINDOW_POPUP,
+ // which will use a Window with the override-redirect attribute
+ // (for temporary windows).
+ // For long-lived windows, their stacking order is managed by the
+ // window manager, as indicated by GTK_WINDOW_TOPLEVEL ...
+ GtkWindowType type =
+ mWindowType != eWindowType_popup || aInitData->mNoAutoHide ?
+ GTK_WINDOW_TOPLEVEL : GTK_WINDOW_POPUP;
+ mShell = gtk_window_new(type);
+
+ // We only move a general managed toplevel window if someone has
+ // actually placed the window somewhere. If no placement has taken
+ // place, we just let the window manager Do The Right Thing.
+ NativeResize();
+
+ if (mWindowType == eWindowType_dialog) {
+ SetDefaultIcon();
+ gtk_window_set_wmclass(GTK_WINDOW(mShell), "Dialog",
+ gdk_get_program_class());
+ gtk_window_set_type_hint(GTK_WINDOW(mShell),
+ GDK_WINDOW_TYPE_HINT_DIALOG);
+ gtk_window_set_transient_for(GTK_WINDOW(mShell),
+ topLevelParent);
+ }
+ else if (mWindowType == eWindowType_popup) {
+ // With popup windows, we want to control their position, so don't
+ // wait for the window manager to place them (which wouldn't
+ // happen with override-redirect windows anyway).
+ NativeMove();
+
+ gtk_window_set_wmclass(GTK_WINDOW(mShell), "Popup",
+ gdk_get_program_class());
+
+ if (aInitData->mSupportTranslucency) {
+ // We need to select an ARGB visual here instead of in
+ // SetTransparencyMode() because it has to be done before the
+ // widget is realized. An ARGB visual is only useful if we
+ // are on a compositing window manager.
+ GdkScreen *screen = gtk_widget_get_screen(mShell);
+ if (gdk_screen_is_composited(screen)) {
+#if (MOZ_WIDGET_GTK == 2)
+ GdkColormap *colormap =
+ gdk_screen_get_rgba_colormap(screen);
+ gtk_widget_set_colormap(mShell, colormap);
+#else
+ GdkVisual *visual = gdk_screen_get_rgba_visual(screen);
+ gtk_widget_set_visual(mShell, visual);
+#endif
+ }
+ }
+ if (aInitData->mNoAutoHide) {
+ // ... but the window manager does not decorate this window,
+ // nor provide a separate taskbar icon.
+ if (mBorderStyle == eBorderStyle_default) {
+ gtk_window_set_decorated(GTK_WINDOW(mShell), FALSE);
+ }
+ else {
+ bool decorate = mBorderStyle & eBorderStyle_title;
+ gtk_window_set_decorated(GTK_WINDOW(mShell), decorate);
+ if (decorate) {
+ gtk_window_set_deletable(GTK_WINDOW(mShell), mBorderStyle & eBorderStyle_close);
+ }
+ }
+ gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mShell), TRUE);
+ // Element focus is managed by the parent window so the
+ // WM_HINTS input field is set to False to tell the window
+ // manager not to set input focus to this window ...
+ gtk_window_set_accept_focus(GTK_WINDOW(mShell), FALSE);
+#ifdef MOZ_X11
+ // ... but when the window manager offers focus through
+ // WM_TAKE_FOCUS, focus is requested on the parent window.
+ gtk_widget_realize(mShell);
+ gdk_window_add_filter(gtk_widget_get_window(mShell),
+ popup_take_focus_filter, nullptr);
+#endif
+ }
+
+ GdkWindowTypeHint gtkTypeHint;
+ if (aInitData->mIsDragPopup) {
+ gtkTypeHint = GDK_WINDOW_TYPE_HINT_DND;
+ mIsDragPopup = true;
+ }
+ else {
+ switch (aInitData->mPopupHint) {
+ case ePopupTypeMenu:
+ gtkTypeHint = GDK_WINDOW_TYPE_HINT_POPUP_MENU;
+ break;
+ case ePopupTypeTooltip:
+ gtkTypeHint = GDK_WINDOW_TYPE_HINT_TOOLTIP;
+ break;
+ default:
+ gtkTypeHint = GDK_WINDOW_TYPE_HINT_UTILITY;
+ break;
+ }
+ }
+ gtk_window_set_type_hint(GTK_WINDOW(mShell), gtkTypeHint);
+
+ if (topLevelParent) {
+ gtk_window_set_transient_for(GTK_WINDOW(mShell),
+ topLevelParent);
+ }
+ }
+ else { // must be eWindowType_toplevel
+ SetDefaultIcon();
+ gtk_window_set_wmclass(GTK_WINDOW(mShell), "Toplevel",
+ gdk_get_program_class());
+
+ // each toplevel window gets its own window group
+ GtkWindowGroup *group = gtk_window_group_new();
+ gtk_window_group_add_window(group, GTK_WINDOW(mShell));
+ g_object_unref(group);
+ }
+
+ // Create a container to hold child windows and child GtkWidgets.
+ GtkWidget *container = moz_container_new();
+ mContainer = MOZ_CONTAINER(container);
+
+#if (MOZ_WIDGET_GTK == 3)
+ // "csd" style is set when widget is realized so we need to call
+ // it explicitly now.
+ gtk_widget_realize(mShell);
+
+ // We can't draw directly to top-level window when client side
+ // decorations are enabled. We use container with GdkWindow instead.
+ GtkStyleContext* style = gtk_widget_get_style_context(mShell);
+ shellHasCSD = gtk_style_context_has_class(style, "csd");
+#endif
+ if (!shellHasCSD) {
+ // Use mShell's window for drawing and events.
+ gtk_widget_set_has_window(container, FALSE);
+ // Prevent GtkWindow from painting a background to flicker.
+ gtk_widget_set_app_paintable(mShell, TRUE);
+ }
+ // Set up event widget
+ eventWidget = shellHasCSD ? container : mShell;
+ gtk_widget_add_events(eventWidget, kEvents);
+
+ gtk_container_add(GTK_CONTAINER(mShell), container);
+ gtk_widget_realize(container);
+
+ // make sure this is the focus widget in the container
+ gtk_widget_show(container);
+ gtk_widget_grab_focus(container);
+
+ // the drawing window
+ mGdkWindow = gtk_widget_get_window(eventWidget);
+
+ if (mWindowType == eWindowType_popup) {
+ // gdk does not automatically set the cursor for "temporary"
+ // windows, which are what gtk uses for popups.
+
+ mCursor = eCursor_wait; // force SetCursor to actually set the
+ // cursor, even though our internal state
+ // indicates that we already have the
+ // standard cursor.
+ SetCursor(eCursor_standard);
+
+ if (aInitData->mNoAutoHide) {
+ gint wmd = ConvertBorderStyles(mBorderStyle);
+ if (wmd != -1)
+ gdk_window_set_decorations(mGdkWindow, (GdkWMDecoration) wmd);
+ }
+
+ // If the popup ignores mouse events, set an empty input shape.
+ if (aInitData->mMouseTransparent) {
+#if (MOZ_WIDGET_GTK == 2)
+ GdkRectangle rect = { 0, 0, 0, 0 };
+ GdkRegion *region = gdk_region_rectangle(&rect);
+
+ gdk_window_input_shape_combine_region(mGdkWindow, region, 0, 0);
+ gdk_region_destroy(region);
+#else
+ cairo_rectangle_int_t rect = { 0, 0, 0, 0 };
+ cairo_region_t *region = cairo_region_create_rectangle(&rect);
+
+ gdk_window_input_shape_combine_region(mGdkWindow, region, 0, 0);
+ cairo_region_destroy(region);
+#endif
+ }
+ }
+ }
+ break;
+ case eWindowType_plugin:
+ case eWindowType_plugin_ipc_chrome:
+ case eWindowType_plugin_ipc_content:
+ case eWindowType_child: {
+ if (parentMozContainer) {
+ mGdkWindow = CreateGdkWindow(parentGdkWindow, parentMozContainer);
+ mHasMappedToplevel = parentnsWindow->mHasMappedToplevel;
+ }
+ else if (parentGtkContainer) {
+ // This MozContainer has its own window for drawing and receives
+ // events because there is no mShell widget (corresponding to this
+ // nsWindow).
+ GtkWidget *container = moz_container_new();
+ mContainer = MOZ_CONTAINER(container);
+ eventWidget = container;
+ gtk_widget_add_events(eventWidget, kEvents);
+ gtk_container_add(parentGtkContainer, container);
+ gtk_widget_realize(container);
+
+ mGdkWindow = gtk_widget_get_window(container);
+ }
+ else {
+ NS_WARNING("Warning: tried to create a new child widget with no parent!");
+ return NS_ERROR_FAILURE;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ // label the drawing window with this object so we can find our way home
+ g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", this);
+
+ if (mContainer)
+ g_object_set_data(G_OBJECT(mContainer), "nsWindow", this);
+
+ if (mShell)
+ g_object_set_data(G_OBJECT(mShell), "nsWindow", this);
+
+ // attach listeners for events
+ if (mShell) {
+ g_signal_connect(mShell, "configure_event",
+ G_CALLBACK(configure_event_cb), nullptr);
+ g_signal_connect(mShell, "delete_event",
+ G_CALLBACK(delete_event_cb), nullptr);
+ g_signal_connect(mShell, "window_state_event",
+ G_CALLBACK(window_state_event_cb), nullptr);
+ g_signal_connect(mShell, "check-resize",
+ G_CALLBACK(check_resize_cb), nullptr);
+
+ GtkSettings* default_settings = gtk_settings_get_default();
+ g_signal_connect_after(default_settings,
+ "notify::gtk-theme-name",
+ G_CALLBACK(theme_changed_cb), this);
+ g_signal_connect_after(default_settings,
+ "notify::gtk-font-name",
+ G_CALLBACK(theme_changed_cb), this);
+ }
+
+ if (mContainer) {
+ // Widget signals
+ g_signal_connect(mContainer, "unrealize",
+ G_CALLBACK(container_unrealize_cb), nullptr);
+ g_signal_connect_after(mContainer, "size_allocate",
+ G_CALLBACK(size_allocate_cb), nullptr);
+ g_signal_connect(mContainer, "hierarchy-changed",
+ G_CALLBACK(hierarchy_changed_cb), nullptr);
+#if (MOZ_WIDGET_GTK == 3)
+ g_signal_connect(mContainer, "notify::scale-factor",
+ G_CALLBACK(scale_changed_cb), nullptr);
+#endif
+ // Initialize mHasMappedToplevel.
+ hierarchy_changed_cb(GTK_WIDGET(mContainer), nullptr);
+ // Expose, focus, key, and drag events are sent even to GTK_NO_WINDOW
+ // widgets.
+#if (MOZ_WIDGET_GTK == 2)
+ g_signal_connect(mContainer, "expose_event",
+ G_CALLBACK(expose_event_cb), nullptr);
+#else
+ g_signal_connect(G_OBJECT(mContainer), "draw",
+ G_CALLBACK(expose_event_cb), nullptr);
+#endif
+ g_signal_connect(mContainer, "focus_in_event",
+ G_CALLBACK(focus_in_event_cb), nullptr);
+ g_signal_connect(mContainer, "focus_out_event",
+ G_CALLBACK(focus_out_event_cb), nullptr);
+ g_signal_connect(mContainer, "key_press_event",
+ G_CALLBACK(key_press_event_cb), nullptr);
+ g_signal_connect(mContainer, "key_release_event",
+ G_CALLBACK(key_release_event_cb), nullptr);
+
+ gtk_drag_dest_set((GtkWidget *)mContainer,
+ (GtkDestDefaults)0,
+ nullptr,
+ 0,
+ (GdkDragAction)0);
+
+ g_signal_connect(mContainer, "drag_motion",
+ G_CALLBACK(drag_motion_event_cb), nullptr);
+ g_signal_connect(mContainer, "drag_leave",
+ G_CALLBACK(drag_leave_event_cb), nullptr);
+ g_signal_connect(mContainer, "drag_drop",
+ G_CALLBACK(drag_drop_event_cb), nullptr);
+ g_signal_connect(mContainer, "drag_data_received",
+ G_CALLBACK(drag_data_received_event_cb), nullptr);
+
+ GtkWidget *widgets[] = { GTK_WIDGET(mContainer),
+ !shellHasCSD ? mShell : nullptr };
+ for (size_t i = 0; i < ArrayLength(widgets) && widgets[i]; ++i) {
+ // Visibility events are sent to the owning widget of the relevant
+ // window but do not propagate to parent widgets so connect on
+ // mShell (if it exists) as well as mContainer.
+ g_signal_connect(widgets[i], "visibility-notify-event",
+ G_CALLBACK(visibility_notify_event_cb), nullptr);
+ // Similarly double buffering is controlled by the window's owning
+ // widget. Disable double buffering for painting directly to the
+ // X Window.
+ gtk_widget_set_double_buffered(widgets[i], FALSE);
+ }
+
+ // We create input contexts for all containers, except for
+ // toplevel popup windows
+ if (mWindowType != eWindowType_popup) {
+ mIMContext = new IMContextWrapper(this);
+ }
+ } else if (!mIMContext) {
+ nsWindow *container = GetContainerWindow();
+ if (container) {
+ mIMContext = container->mIMContext;
+ }
+ }
+
+ if (eventWidget) {
+#if (MOZ_WIDGET_GTK == 2)
+ // Don't let GTK mess with the shapes of our GdkWindows
+ GTK_PRIVATE_SET_FLAG(eventWidget, GTK_HAS_SHAPE_MASK);
+#endif
+
+ // These events are sent to the owning widget of the relevant window
+ // and propagate up to the first widget that handles the events, so we
+ // need only connect on mShell, if it exists, to catch events on its
+ // window and windows of mContainer.
+ g_signal_connect(eventWidget, "enter-notify-event",
+ G_CALLBACK(enter_notify_event_cb), nullptr);
+ g_signal_connect(eventWidget, "leave-notify-event",
+ G_CALLBACK(leave_notify_event_cb), nullptr);
+ g_signal_connect(eventWidget, "motion-notify-event",
+ G_CALLBACK(motion_notify_event_cb), nullptr);
+ g_signal_connect(eventWidget, "button-press-event",
+ G_CALLBACK(button_press_event_cb), nullptr);
+ g_signal_connect(eventWidget, "button-release-event",
+ G_CALLBACK(button_release_event_cb), nullptr);
+ g_signal_connect(eventWidget, "property-notify-event",
+ G_CALLBACK(property_notify_event_cb), nullptr);
+ g_signal_connect(eventWidget, "scroll-event",
+ G_CALLBACK(scroll_event_cb), nullptr);
+#if GTK_CHECK_VERSION(3,4,0)
+ g_signal_connect(eventWidget, "touch-event",
+ G_CALLBACK(touch_event_cb), nullptr);
+#endif
+ }
+
+ LOG(("nsWindow [%p]\n", (void *)this));
+ if (mShell) {
+ LOG(("\tmShell %p mContainer %p mGdkWindow %p 0x%lx\n",
+ mShell, mContainer, mGdkWindow,
+ gdk_x11_window_get_xid(mGdkWindow)));
+ } else if (mContainer) {
+ LOG(("\tmContainer %p mGdkWindow %p\n", mContainer, mGdkWindow));
+ }
+ else if (mGdkWindow) {
+ LOG(("\tmGdkWindow %p parent %p\n",
+ mGdkWindow, gdk_window_get_parent(mGdkWindow)));
+ }
+
+ // resize so that everything is set to the right dimensions
+ if (!mIsTopLevel)
+ Resize(mBounds.x, mBounds.y, mBounds.width, mBounds.height, false);
+
+#ifdef MOZ_X11
+ if (mIsX11Display && mGdkWindow) {
+ mXDisplay = GDK_WINDOW_XDISPLAY(mGdkWindow);
+ mXWindow = gdk_x11_window_get_xid(mGdkWindow);
+
+ GdkVisual* gdkVisual = gdk_window_get_visual(mGdkWindow);
+ mXVisual = gdk_x11_visual_get_xvisual(gdkVisual);
+ mXDepth = gdk_visual_get_depth(gdkVisual);
+
+ mSurfaceProvider.Initialize(mXDisplay, mXWindow, mXVisual, mXDepth);
+ }
+#endif
+
+ return NS_OK;
+}
+
+void
+nsWindow::SetWindowClass(const nsAString &xulWinType)
+{
+ if (!mShell)
+ return;
+
+ const char *res_class = gdk_get_program_class();
+ if (!res_class)
+ return;
+
+ char *res_name = ToNewCString(xulWinType);
+ if (!res_name)
+ return;
+
+ const char *role = nullptr;
+
+ // Parse res_name into a name and role. Characters other than
+ // [A-Za-z0-9_-] are converted to '_'. Anything after the first
+ // colon is assigned to role; if there's no colon, assign the
+ // whole thing to both role and res_name.
+ for (char *c = res_name; *c; c++) {
+ if (':' == *c) {
+ *c = 0;
+ role = c + 1;
+ }
+ else if (!isascii(*c) || (!isalnum(*c) && ('_' != *c) && ('-' != *c)))
+ *c = '_';
+ }
+ res_name[0] = toupper(res_name[0]);
+ if (!role) role = res_name;
+
+ gdk_window_set_role(mGdkWindow, role);
+
+#ifdef MOZ_X11
+ if (mIsX11Display) {
+ XClassHint *class_hint = XAllocClassHint();
+ if (!class_hint) {
+ free(res_name);
+ return;
+ }
+ class_hint->res_name = res_name;
+ class_hint->res_class = const_cast<char*>(res_class);
+
+ // Can't use gtk_window_set_wmclass() for this; it prints
+ // a warning & refuses to make the change.
+ GdkDisplay *display = gdk_display_get_default();
+ XSetClassHint(GDK_DISPLAY_XDISPLAY(display),
+ gdk_x11_window_get_xid(mGdkWindow),
+ class_hint);
+ XFree(class_hint);
+ }
+#endif /* MOZ_X11 */
+
+ free(res_name);
+}
+
+void
+nsWindow::NativeResize()
+{
+ if (!AreBoundsSane()) {
+ // If someone has set this so that the needs show flag is false
+ // and it needs to be hidden, update the flag and hide the
+ // window. This flag will be cleared the next time someone
+ // hides the window or shows it. It also prevents us from
+ // calling NativeShow(false) excessively on the window which
+ // causes unneeded X traffic.
+ if (!mNeedsShow && mIsShown) {
+ mNeedsShow = true;
+ NativeShow(false);
+ }
+ return;
+ }
+
+ GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mBounds.Size());
+
+ LOG(("nsWindow::NativeResize [%p] %d %d\n", (void *)this,
+ size.width, size.height));
+
+ if (mIsTopLevel) {
+ gtk_window_resize(GTK_WINDOW(mShell), size.width, size.height);
+ }
+ else if (mContainer) {
+ GtkWidget *widget = GTK_WIDGET(mContainer);
+ GtkAllocation allocation, prev_allocation;
+ gtk_widget_get_allocation(widget, &prev_allocation);
+ allocation.x = prev_allocation.x;
+ allocation.y = prev_allocation.y;
+ allocation.width = size.width;
+ allocation.height = size.height;
+ gtk_widget_size_allocate(widget, &allocation);
+ }
+ else if (mGdkWindow) {
+ gdk_window_resize(mGdkWindow, size.width, size.height);
+ }
+
+#ifdef MOZ_X11
+ // Notify the X11CompositorWidget of a ClientSizeChange
+ // This is different than OnSizeAllocate to catch initial sizing
+ if (mCompositorWidgetDelegate) {
+ mCompositorWidgetDelegate->NotifyClientSizeChanged(GetClientSize());
+ }
+#endif
+
+ // Does it need to be shown because bounds were previously insane?
+ if (mNeedsShow && mIsShown) {
+ NativeShow(true);
+ }
+}
+
+void
+nsWindow::NativeMoveResize()
+{
+ if (!AreBoundsSane()) {
+ // If someone has set this so that the needs show flag is false
+ // and it needs to be hidden, update the flag and hide the
+ // window. This flag will be cleared the next time someone
+ // hides the window or shows it. It also prevents us from
+ // calling NativeShow(false) excessively on the window which
+ // causes unneeded X traffic.
+ if (!mNeedsShow && mIsShown) {
+ mNeedsShow = true;
+ NativeShow(false);
+ }
+ NativeMove();
+ }
+
+ GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mBounds.Size());
+ GdkPoint topLeft = DevicePixelsToGdkPointRoundDown(mBounds.TopLeft());
+
+ LOG(("nsWindow::NativeMoveResize [%p] %d %d %d %d\n", (void *)this,
+ topLeft.x, topLeft.y, size.width, size.height));
+
+ if (mIsTopLevel) {
+ // x and y give the position of the window manager frame top-left.
+ gtk_window_move(GTK_WINDOW(mShell), topLeft.x, topLeft.y);
+ // This sets the client window size.
+ gtk_window_resize(GTK_WINDOW(mShell), size.width, size.height);
+ }
+ else if (mContainer) {
+ GtkAllocation allocation;
+ allocation.x = topLeft.x;
+ allocation.y = topLeft.y;
+ allocation.width = size.width;
+ allocation.height = size.height;
+ gtk_widget_size_allocate(GTK_WIDGET(mContainer), &allocation);
+ }
+ else if (mGdkWindow) {
+ gdk_window_move_resize(mGdkWindow,
+ topLeft.x, topLeft.y, size.width, size.height);
+ }
+
+#ifdef MOZ_X11
+ // Notify the X11CompositorWidget of a ClientSizeChange
+ // This is different than OnSizeAllocate to catch initial sizing
+ if (mCompositorWidgetDelegate) {
+ mCompositorWidgetDelegate->NotifyClientSizeChanged(GetClientSize());
+ }
+#endif
+
+ // Does it need to be shown because bounds were previously insane?
+ if (mNeedsShow && mIsShown) {
+ NativeShow(true);
+ }
+}
+
+void
+nsWindow::NativeShow(bool aAction)
+{
+ if (aAction) {
+ // unset our flag now that our window has been shown
+ mNeedsShow = false;
+
+ if (mIsTopLevel) {
+ // Set up usertime/startupID metadata for the created window.
+ if (mWindowType != eWindowType_invisible) {
+ SetUserTimeAndStartupIDForActivatedWindow(mShell);
+ }
+
+ gtk_widget_show(mShell);
+ }
+ else if (mContainer) {
+ gtk_widget_show(GTK_WIDGET(mContainer));
+ }
+ else if (mGdkWindow) {
+ gdk_window_show_unraised(mGdkWindow);
+ }
+ }
+ else {
+ if (mIsTopLevel) {
+ // Workaround window freezes on GTK versions before 3.21.2 by
+ // ensuring that configure events get dispatched to windows before
+ // they are unmapped. See bug 1225044.
+ if (gtk_check_version(3, 21, 2) != nullptr && mPendingConfigures > 0) {
+ GtkAllocation allocation;
+ gtk_widget_get_allocation(GTK_WIDGET(mShell), &allocation);
+
+ GdkEventConfigure event;
+ PodZero(&event);
+ event.type = GDK_CONFIGURE;
+ event.window = mGdkWindow;
+ event.send_event = TRUE;
+ event.x = allocation.x;
+ event.y = allocation.y;
+ event.width = allocation.width;
+ event.height = allocation.height;
+
+ auto shellClass = GTK_WIDGET_GET_CLASS(mShell);
+ for (unsigned int i = 0; i < mPendingConfigures; i++) {
+ Unused << shellClass->configure_event(mShell, &event);
+ }
+ mPendingConfigures = 0;
+ }
+
+ gtk_widget_hide(mShell);
+
+ ClearTransparencyBitmap(); // Release some resources
+ }
+ else if (mContainer) {
+ gtk_widget_hide(GTK_WIDGET(mContainer));
+ }
+ else if (mGdkWindow) {
+ gdk_window_hide(mGdkWindow);
+ }
+ }
+}
+
+void
+nsWindow::SetHasMappedToplevel(bool aState)
+{
+ // Even when aState == mHasMappedToplevel (as when this method is called
+ // from Show()), child windows need to have their state checked, so don't
+ // return early.
+ bool oldState = mHasMappedToplevel;
+ mHasMappedToplevel = aState;
+
+ // mHasMappedToplevel is not updated for children of windows that are
+ // hidden; GDK knows not to send expose events for these windows. The
+ // state is recorded on the hidden window itself, but, for child trees of
+ // hidden windows, their state essentially becomes disconnected from their
+ // hidden parent. When the hidden parent gets shown, the child trees are
+ // reconnected, and the state of the window being shown can be easily
+ // propagated.
+ if (!mIsShown || !mGdkWindow)
+ return;
+
+ if (aState && !oldState && !mIsFullyObscured) {
+ // GDK_EXPOSE events have been ignored but the window is now visible,
+ // so make sure GDK doesn't think that the window has already been
+ // painted.
+ gdk_window_invalidate_rect(mGdkWindow, nullptr, FALSE);
+
+ // Check that a grab didn't fail due to the window not being
+ // viewable.
+ EnsureGrabs();
+ }
+
+ for (GList *children = gdk_window_peek_children(mGdkWindow);
+ children;
+ children = children->next) {
+ GdkWindow *gdkWin = GDK_WINDOW(children->data);
+ nsWindow *child = get_window_for_gdk_window(gdkWin);
+
+ if (child && child->mHasMappedToplevel != aState) {
+ child->SetHasMappedToplevel(aState);
+ }
+ }
+}
+
+LayoutDeviceIntSize
+nsWindow::GetSafeWindowSize(LayoutDeviceIntSize aSize)
+{
+ // The X protocol uses CARD32 for window sizes, but the server (1.11.3)
+ // reads it as CARD16. Sizes of pixmaps, used for drawing, are (unsigned)
+ // CARD16 in the protocol, but the server's ProcCreatePixmap returns
+ // BadAlloc if dimensions cannot be represented by signed shorts.
+ LayoutDeviceIntSize result = aSize;
+ const int32_t kInt16Max = 32767;
+ if (result.width > kInt16Max) {
+ result.width = kInt16Max;
+ }
+ if (result.height > kInt16Max) {
+ result.height = kInt16Max;
+ }
+ return result;
+}
+
+void
+nsWindow::EnsureGrabs(void)
+{
+ if (mRetryPointerGrab)
+ GrabPointer(sRetryGrabTime);
+}
+
+void
+nsWindow::CleanLayerManagerRecursive(void) {
+ if (mLayerManager) {
+ mLayerManager->Destroy();
+ mLayerManager = nullptr;
+ }
+
+ DestroyCompositor();
+
+ GList* children = gdk_window_peek_children(mGdkWindow);
+ for (GList* list = children; list; list = list->next) {
+ nsWindow* window = get_window_for_gdk_window(GDK_WINDOW(list->data));
+ if (window) {
+ window->CleanLayerManagerRecursive();
+ }
+ }
+}
+
+void
+nsWindow::SetTransparencyMode(nsTransparencyMode aMode)
+{
+ if (!mShell) {
+ // Pass the request to the toplevel window
+ GtkWidget *topWidget = GetToplevelWidget();
+ if (!topWidget)
+ return;
+
+ nsWindow *topWindow = get_window_for_gtk_widget(topWidget);
+ if (!topWindow)
+ return;
+
+ topWindow->SetTransparencyMode(aMode);
+ return;
+ }
+ bool isTransparent = aMode == eTransparencyTransparent;
+
+ if (mIsTransparent == isTransparent)
+ return;
+
+ if (!isTransparent) {
+ ClearTransparencyBitmap();
+ } // else the new default alpha values are "all 1", so we don't
+ // need to change anything yet
+
+ mIsTransparent = isTransparent;
+
+ // Need to clean our LayerManager up while still alive because
+ // we don't want to use layers acceleration on shaped windows
+ CleanLayerManagerRecursive();
+}
+
+nsTransparencyMode
+nsWindow::GetTransparencyMode()
+{
+ if (!mShell) {
+ // Pass the request to the toplevel window
+ GtkWidget *topWidget = GetToplevelWidget();
+ if (!topWidget) {
+ return eTransparencyOpaque;
+ }
+
+ nsWindow *topWindow = get_window_for_gtk_widget(topWidget);
+ if (!topWindow) {
+ return eTransparencyOpaque;
+ }
+
+ return topWindow->GetTransparencyMode();
+ }
+
+ return mIsTransparent ? eTransparencyTransparent : eTransparencyOpaque;
+}
+
+nsresult
+nsWindow::ConfigureChildren(const nsTArray<Configuration>& aConfigurations)
+{
+ // If this is a remotely updated widget we receive clipping, position, and
+ // size information from a source other than our owner. Don't let our parent
+ // update this information.
+ if (mWindowType == eWindowType_plugin_ipc_chrome) {
+ return NS_OK;
+ }
+
+ for (uint32_t i = 0; i < aConfigurations.Length(); ++i) {
+ const Configuration& configuration = aConfigurations[i];
+ nsWindow* w = static_cast<nsWindow*>(configuration.mChild.get());
+ NS_ASSERTION(w->GetParent() == this,
+ "Configured widget is not a child");
+ w->SetWindowClipRegion(configuration.mClipRegion, true);
+ if (w->mBounds.Size() != configuration.mBounds.Size()) {
+ w->Resize(configuration.mBounds.x, configuration.mBounds.y,
+ configuration.mBounds.width, configuration.mBounds.height,
+ true);
+ } else if (w->mBounds.TopLeft() != configuration.mBounds.TopLeft()) {
+ w->Move(configuration.mBounds.x, configuration.mBounds.y);
+ }
+ w->SetWindowClipRegion(configuration.mClipRegion, false);
+ }
+ return NS_OK;
+}
+
+nsresult
+nsWindow::SetWindowClipRegion(const nsTArray<LayoutDeviceIntRect>& aRects,
+ bool aIntersectWithExisting)
+{
+ const nsTArray<LayoutDeviceIntRect>* newRects = &aRects;
+
+ AutoTArray<LayoutDeviceIntRect,1> intersectRects;
+ if (aIntersectWithExisting) {
+ AutoTArray<LayoutDeviceIntRect,1> existingRects;
+ GetWindowClipRegion(&existingRects);
+
+ LayoutDeviceIntRegion existingRegion = RegionFromArray(existingRects);
+ LayoutDeviceIntRegion newRegion = RegionFromArray(aRects);
+ LayoutDeviceIntRegion intersectRegion;
+ intersectRegion.And(newRegion, existingRegion);
+
+ // If mClipRects is null we haven't set a clip rect yet, so we
+ // need to set the clip even if it is equal.
+ if (mClipRects && intersectRegion.IsEqual(existingRegion)) {
+ return NS_OK;
+ }
+
+ if (!intersectRegion.IsEqual(newRegion)) {
+ ArrayFromRegion(intersectRegion, intersectRects);
+ newRects = &intersectRects;
+ }
+ }
+
+ if (IsWindowClipRegionEqual(*newRects))
+ return NS_OK;
+
+ StoreWindowClipRegion(*newRects);
+
+ if (!mGdkWindow)
+ return NS_OK;
+
+#if (MOZ_WIDGET_GTK == 2)
+ GdkRegion *region = gdk_region_new(); // aborts on OOM
+ for (uint32_t i = 0; i < newRects->Length(); ++i) {
+ const LayoutDeviceIntRect& r = newRects->ElementAt(i);
+ GdkRectangle rect = { r.x, r.y, r.width, r.height };
+ gdk_region_union_with_rect(region, &rect);
+ }
+
+ gdk_window_shape_combine_region(mGdkWindow, region, 0, 0);
+ gdk_region_destroy(region);
+#else
+ cairo_region_t *region = cairo_region_create();
+ for (uint32_t i = 0; i < newRects->Length(); ++i) {
+ const LayoutDeviceIntRect& r = newRects->ElementAt(i);
+ cairo_rectangle_int_t rect = { r.x, r.y, r.width, r.height };
+ cairo_region_union_rectangle(region, &rect);
+ }
+
+ gdk_window_shape_combine_region(mGdkWindow, region, 0, 0);
+ cairo_region_destroy(region);
+#endif
+
+ return NS_OK;
+}
+
+void
+nsWindow::ResizeTransparencyBitmap()
+{
+ if (!mTransparencyBitmap)
+ return;
+
+ if (mBounds.width == mTransparencyBitmapWidth &&
+ mBounds.height == mTransparencyBitmapHeight)
+ return;
+
+ int32_t newRowBytes = GetBitmapStride(mBounds.width);
+ int32_t newSize = newRowBytes * mBounds.height;
+ gchar* newBits = new gchar[newSize];
+ // fill new mask with "transparent", first
+ memset(newBits, 0, newSize);
+
+ // Now copy the intersection of the old and new areas into the new mask
+ int32_t copyWidth = std::min(mBounds.width, mTransparencyBitmapWidth);
+ int32_t copyHeight = std::min(mBounds.height, mTransparencyBitmapHeight);
+ int32_t oldRowBytes = GetBitmapStride(mTransparencyBitmapWidth);
+ int32_t copyBytes = GetBitmapStride(copyWidth);
+
+ int32_t i;
+ gchar* fromPtr = mTransparencyBitmap;
+ gchar* toPtr = newBits;
+ for (i = 0; i < copyHeight; i++) {
+ memcpy(toPtr, fromPtr, copyBytes);
+ fromPtr += oldRowBytes;
+ toPtr += newRowBytes;
+ }
+
+ delete[] mTransparencyBitmap;
+ mTransparencyBitmap = newBits;
+ mTransparencyBitmapWidth = mBounds.width;
+ mTransparencyBitmapHeight = mBounds.height;
+}
+
+static bool
+ChangedMaskBits(gchar* aMaskBits, int32_t aMaskWidth, int32_t aMaskHeight,
+ const nsIntRect& aRect, uint8_t* aAlphas, int32_t aStride)
+{
+ int32_t x, y, xMax = aRect.XMost(), yMax = aRect.YMost();
+ int32_t maskBytesPerRow = GetBitmapStride(aMaskWidth);
+ for (y = aRect.y; y < yMax; y++) {
+ gchar* maskBytes = aMaskBits + y*maskBytesPerRow;
+ uint8_t* alphas = aAlphas;
+ for (x = aRect.x; x < xMax; x++) {
+ bool newBit = *alphas > 0x7f;
+ alphas++;
+
+ gchar maskByte = maskBytes[x >> 3];
+ bool maskBit = (maskByte & (1 << (x & 7))) != 0;
+
+ if (maskBit != newBit) {
+ return true;
+ }
+ }
+ aAlphas += aStride;
+ }
+
+ return false;
+}
+
+static
+void UpdateMaskBits(gchar* aMaskBits, int32_t aMaskWidth, int32_t aMaskHeight,
+ const nsIntRect& aRect, uint8_t* aAlphas, int32_t aStride)
+{
+ int32_t x, y, xMax = aRect.XMost(), yMax = aRect.YMost();
+ int32_t maskBytesPerRow = GetBitmapStride(aMaskWidth);
+ for (y = aRect.y; y < yMax; y++) {
+ gchar* maskBytes = aMaskBits + y*maskBytesPerRow;
+ uint8_t* alphas = aAlphas;
+ for (x = aRect.x; x < xMax; x++) {
+ bool newBit = *alphas > 0x7f;
+ alphas++;
+
+ gchar mask = 1 << (x & 7);
+ gchar maskByte = maskBytes[x >> 3];
+ // Note: '-newBit' turns 0 into 00...00 and 1 into 11...11
+ maskBytes[x >> 3] = (maskByte & ~mask) | (-newBit & mask);
+ }
+ aAlphas += aStride;
+ }
+}
+
+void
+nsWindow::ApplyTransparencyBitmap()
+{
+#ifdef MOZ_X11
+ // We use X11 calls where possible, because GDK handles expose events
+ // for shaped windows in a way that's incompatible with us (Bug 635903).
+ // It doesn't occur when the shapes are set through X.
+ Display* xDisplay = GDK_WINDOW_XDISPLAY(mGdkWindow);
+ Window xDrawable = GDK_WINDOW_XID(mGdkWindow);
+ Pixmap maskPixmap = XCreateBitmapFromData(xDisplay,
+ xDrawable,
+ mTransparencyBitmap,
+ mTransparencyBitmapWidth,
+ mTransparencyBitmapHeight);
+ XShapeCombineMask(xDisplay, xDrawable,
+ ShapeBounding, 0, 0,
+ maskPixmap, ShapeSet);
+ XFreePixmap(xDisplay, maskPixmap);
+#else
+#if (MOZ_WIDGET_GTK == 2)
+ gtk_widget_reset_shapes(mShell);
+ GdkBitmap* maskBitmap = gdk_bitmap_create_from_data(mGdkWindow,
+ mTransparencyBitmap,
+ mTransparencyBitmapWidth, mTransparencyBitmapHeight);
+ if (!maskBitmap)
+ return;
+
+ gtk_widget_shape_combine_mask(mShell, maskBitmap, 0, 0);
+ g_object_unref(maskBitmap);
+#else
+ cairo_surface_t *maskBitmap;
+ maskBitmap = cairo_image_surface_create_for_data((unsigned char*)mTransparencyBitmap,
+ CAIRO_FORMAT_A1,
+ mTransparencyBitmapWidth,
+ mTransparencyBitmapHeight,
+ GetBitmapStride(mTransparencyBitmapWidth));
+ if (!maskBitmap)
+ return;
+
+ cairo_region_t * maskRegion = gdk_cairo_region_create_from_surface(maskBitmap);
+ gtk_widget_shape_combine_region(mShell, maskRegion);
+ cairo_region_destroy(maskRegion);
+ cairo_surface_destroy(maskBitmap);
+#endif // MOZ_WIDGET_GTK == 2
+#endif // MOZ_X11
+}
+
+void
+nsWindow::ClearTransparencyBitmap()
+{
+ if (!mTransparencyBitmap)
+ return;
+
+ delete[] mTransparencyBitmap;
+ mTransparencyBitmap = nullptr;
+ mTransparencyBitmapWidth = 0;
+ mTransparencyBitmapHeight = 0;
+
+ if (!mShell)
+ return;
+
+#ifdef MOZ_X11
+ if (!mGdkWindow)
+ return;
+
+ Display* xDisplay = GDK_WINDOW_XDISPLAY(mGdkWindow);
+ Window xWindow = gdk_x11_window_get_xid(mGdkWindow);
+
+ XShapeCombineMask(xDisplay, xWindow, ShapeBounding, 0, 0, X11None, ShapeSet);
+#endif
+}
+
+nsresult
+nsWindow::UpdateTranslucentWindowAlphaInternal(const nsIntRect& aRect,
+ uint8_t* aAlphas, int32_t aStride)
+{
+ if (!mShell) {
+ // Pass the request to the toplevel window
+ GtkWidget *topWidget = GetToplevelWidget();
+ if (!topWidget)
+ return NS_ERROR_FAILURE;
+
+ nsWindow *topWindow = get_window_for_gtk_widget(topWidget);
+ if (!topWindow)
+ return NS_ERROR_FAILURE;
+
+ return topWindow->UpdateTranslucentWindowAlphaInternal(aRect, aAlphas, aStride);
+ }
+
+ NS_ASSERTION(mIsTransparent, "Window is not transparent");
+
+ if (mTransparencyBitmap == nullptr) {
+ int32_t size = GetBitmapStride(mBounds.width)*mBounds.height;
+ mTransparencyBitmap = new gchar[size];
+ memset(mTransparencyBitmap, 255, size);
+ mTransparencyBitmapWidth = mBounds.width;
+ mTransparencyBitmapHeight = mBounds.height;
+ } else {
+ ResizeTransparencyBitmap();
+ }
+
+ nsIntRect rect;
+ rect.IntersectRect(aRect, nsIntRect(0, 0, mBounds.width, mBounds.height));
+
+ if (!ChangedMaskBits(mTransparencyBitmap, mBounds.width, mBounds.height,
+ rect, aAlphas, aStride))
+ // skip the expensive stuff if the mask bits haven't changed; hopefully
+ // this is the common case
+ return NS_OK;
+
+ UpdateMaskBits(mTransparencyBitmap, mBounds.width, mBounds.height,
+ rect, aAlphas, aStride);
+
+ if (!mNeedsShow) {
+ ApplyTransparencyBitmap();
+ }
+ return NS_OK;
+}
+
+void
+nsWindow::GrabPointer(guint32 aTime)
+{
+ LOG(("GrabPointer time=0x%08x retry=%d\n",
+ (unsigned int)aTime, mRetryPointerGrab));
+
+ mRetryPointerGrab = false;
+ sRetryGrabTime = aTime;
+
+ // If the window isn't visible, just set the flag to retry the
+ // grab. When this window becomes visible, the grab will be
+ // retried.
+ if (!mHasMappedToplevel || mIsFullyObscured) {
+ LOG(("GrabPointer: window not visible\n"));
+ mRetryPointerGrab = true;
+ return;
+ }
+
+ if (!mGdkWindow)
+ return;
+
+ gint retval;
+ retval = gdk_pointer_grab(mGdkWindow, TRUE,
+ (GdkEventMask)(GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK |
+ GDK_POINTER_MOTION_MASK),
+ (GdkWindow *)nullptr, nullptr, aTime);
+
+ if (retval == GDK_GRAB_NOT_VIEWABLE) {
+ LOG(("GrabPointer: window not viewable; will retry\n"));
+ mRetryPointerGrab = true;
+ } else if (retval != GDK_GRAB_SUCCESS) {
+ LOG(("GrabPointer: pointer grab failed: %i\n", retval));
+ // A failed grab indicates that another app has grabbed the pointer.
+ // Check for rollup now, because, without the grab, we likely won't
+ // get subsequent button press events. Do this with an event so that
+ // popups don't rollup while potentially adjusting the grab for
+ // this popup.
+ nsCOMPtr<nsIRunnable> event =
+ NewRunnableMethod(this, &nsWindow::CheckForRollupDuringGrab);
+ NS_DispatchToCurrentThread(event.forget());
+ }
+}
+
+void
+nsWindow::ReleaseGrabs(void)
+{
+ LOG(("ReleaseGrabs\n"));
+
+ mRetryPointerGrab = false;
+ gdk_pointer_ungrab(GDK_CURRENT_TIME);
+}
+
+GtkWidget *
+nsWindow::GetToplevelWidget()
+{
+ if (mShell) {
+ return mShell;
+ }
+
+ GtkWidget *widget = GetMozContainerWidget();
+ if (!widget)
+ return nullptr;
+
+ return gtk_widget_get_toplevel(widget);
+}
+
+GtkWidget *
+nsWindow::GetMozContainerWidget()
+{
+ if (!mGdkWindow)
+ return nullptr;
+
+ if (mContainer)
+ return GTK_WIDGET(mContainer);
+
+ GtkWidget *owningWidget =
+ get_gtk_widget_for_gdk_window(mGdkWindow);
+ return owningWidget;
+}
+
+nsWindow *
+nsWindow::GetContainerWindow()
+{
+ GtkWidget *owningWidget = GetMozContainerWidget();
+ if (!owningWidget)
+ return nullptr;
+
+ nsWindow *window = get_window_for_gtk_widget(owningWidget);
+ NS_ASSERTION(window, "No nsWindow for container widget");
+ return window;
+}
+
+void
+nsWindow::SetUrgencyHint(GtkWidget *top_window, bool state)
+{
+ if (!top_window)
+ return;
+
+ gdk_window_set_urgency_hint(gtk_widget_get_window(top_window), state);
+}
+
+void *
+nsWindow::SetupPluginPort(void)
+{
+ if (!mGdkWindow)
+ return nullptr;
+
+ if (gdk_window_is_destroyed(mGdkWindow) == TRUE)
+ return nullptr;
+
+ Window window = gdk_x11_window_get_xid(mGdkWindow);
+
+ // we have to flush the X queue here so that any plugins that
+ // might be running on separate X connections will be able to use
+ // this window in case it was just created
+#ifdef MOZ_X11
+ XWindowAttributes xattrs;
+ Display *display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
+ XGetWindowAttributes(display, window, &xattrs);
+ XSelectInput (display, window,
+ xattrs.your_event_mask |
+ SubstructureNotifyMask);
+
+ gdk_window_add_filter(mGdkWindow, plugin_window_filter_func, this);
+
+ XSync(display, False);
+#endif /* MOZ_X11 */
+
+ return (void *)window;
+}
+
+void
+nsWindow::SetDefaultIcon(void)
+{
+ SetIcon(NS_LITERAL_STRING("default"));
+}
+
+void
+nsWindow::SetPluginType(PluginType aPluginType)
+{
+ mPluginType = aPluginType;
+}
+
+#ifdef MOZ_X11
+void
+nsWindow::SetNonXEmbedPluginFocus()
+{
+ if (gPluginFocusWindow == this || mPluginType!=PluginType_NONXEMBED) {
+ return;
+ }
+
+ if (gPluginFocusWindow) {
+ RefPtr<nsWindow> kungFuDeathGrip = gPluginFocusWindow;
+ gPluginFocusWindow->LoseNonXEmbedPluginFocus();
+ }
+
+ LOGFOCUS(("nsWindow::SetNonXEmbedPluginFocus\n"));
+
+ Window curFocusWindow;
+ int focusState;
+
+ GdkDisplay *gdkDisplay = gdk_window_get_display(mGdkWindow);
+ XGetInputFocus(gdk_x11_display_get_xdisplay(gdkDisplay),
+ &curFocusWindow,
+ &focusState);
+
+ LOGFOCUS(("\t curFocusWindow=%p\n", curFocusWindow));
+
+ GdkWindow* toplevel = gdk_window_get_toplevel(mGdkWindow);
+#if (MOZ_WIDGET_GTK == 2)
+ GdkWindow *gdkfocuswin = gdk_window_lookup(curFocusWindow);
+#else
+ GdkWindow *gdkfocuswin = gdk_x11_window_lookup_for_display(gdkDisplay,
+ curFocusWindow);
+#endif
+
+ // lookup with the focus proxy window is supposed to get the
+ // same GdkWindow as toplevel. If the current focused window
+ // is not the focus proxy, we return without any change.
+ if (gdkfocuswin != toplevel) {
+ return;
+ }
+
+ // switch the focus from the focus proxy to the plugin window
+ mOldFocusWindow = curFocusWindow;
+ XRaiseWindow(GDK_WINDOW_XDISPLAY(mGdkWindow),
+ gdk_x11_window_get_xid(mGdkWindow));
+ gdk_error_trap_push();
+ XSetInputFocus(GDK_WINDOW_XDISPLAY(mGdkWindow),
+ gdk_x11_window_get_xid(mGdkWindow),
+ RevertToNone,
+ CurrentTime);
+ gdk_flush();
+#if (MOZ_WIDGET_GTK == 3)
+ gdk_error_trap_pop_ignored();
+#else
+ gdk_error_trap_pop();
+#endif
+ gPluginFocusWindow = this;
+ gdk_window_add_filter(nullptr, plugin_client_message_filter, this);
+
+ LOGFOCUS(("nsWindow::SetNonXEmbedPluginFocus oldfocus=%p new=%p\n",
+ mOldFocusWindow, gdk_x11_window_get_xid(mGdkWindow)));
+}
+
+void
+nsWindow::LoseNonXEmbedPluginFocus()
+{
+ LOGFOCUS(("nsWindow::LoseNonXEmbedPluginFocus\n"));
+
+ // This method is only for the nsWindow which contains a
+ // Non-XEmbed plugin, for example, JAVA plugin.
+ if (gPluginFocusWindow != this || mPluginType!=PluginType_NONXEMBED) {
+ return;
+ }
+
+ Window curFocusWindow;
+ int focusState;
+
+ XGetInputFocus(GDK_WINDOW_XDISPLAY(mGdkWindow),
+ &curFocusWindow,
+ &focusState);
+
+ // we only switch focus between plugin window and focus proxy. If the
+ // current focused window is not the plugin window, just removing the
+ // event filter that blocks the WM_TAKE_FOCUS is enough. WM and gtk2
+ // will take care of the focus later.
+ if (!curFocusWindow ||
+ curFocusWindow == gdk_x11_window_get_xid(mGdkWindow)) {
+
+ gdk_error_trap_push();
+ XRaiseWindow(GDK_WINDOW_XDISPLAY(mGdkWindow),
+ mOldFocusWindow);
+ XSetInputFocus(GDK_WINDOW_XDISPLAY(mGdkWindow),
+ mOldFocusWindow,
+ RevertToParent,
+ CurrentTime);
+ gdk_flush();
+#if (MOZ_WIDGET_GTK == 3)
+ gdk_error_trap_pop_ignored();
+#else
+ gdk_error_trap_pop();
+#endif
+ }
+ gPluginFocusWindow = nullptr;
+ mOldFocusWindow = 0;
+ gdk_window_remove_filter(nullptr, plugin_client_message_filter, this);
+
+ LOGFOCUS(("nsWindow::LoseNonXEmbedPluginFocus end\n"));
+}
+#endif /* MOZ_X11 */
+
+gint
+nsWindow::ConvertBorderStyles(nsBorderStyle aStyle)
+{
+ gint w = 0;
+
+ if (aStyle == eBorderStyle_default)
+ return -1;
+
+ // note that we don't handle eBorderStyle_close yet
+ if (aStyle & eBorderStyle_all)
+ w |= GDK_DECOR_ALL;
+ if (aStyle & eBorderStyle_border)
+ w |= GDK_DECOR_BORDER;
+ if (aStyle & eBorderStyle_resizeh)
+ w |= GDK_DECOR_RESIZEH;
+ if (aStyle & eBorderStyle_title)
+ w |= GDK_DECOR_TITLE;
+ if (aStyle & eBorderStyle_menu)
+ w |= GDK_DECOR_MENU;
+ if (aStyle & eBorderStyle_minimize)
+ w |= GDK_DECOR_MINIMIZE;
+ if (aStyle & eBorderStyle_maximize)
+ w |= GDK_DECOR_MAXIMIZE;
+
+ return w;
+}
+
+class FullscreenTransitionWindow final : public nsISupports
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ explicit FullscreenTransitionWindow(GtkWidget* aWidget);
+
+ GtkWidget* mWindow;
+
+private:
+ ~FullscreenTransitionWindow();
+};
+
+NS_IMPL_ISUPPORTS0(FullscreenTransitionWindow)
+
+FullscreenTransitionWindow::FullscreenTransitionWindow(GtkWidget* aWidget)
+{
+ mWindow = gtk_window_new(GTK_WINDOW_POPUP);
+ GtkWindow* gtkWin = GTK_WINDOW(mWindow);
+
+ gtk_window_set_type_hint(gtkWin, GDK_WINDOW_TYPE_HINT_SPLASHSCREEN);
+ gtk_window_set_transient_for(gtkWin, GTK_WINDOW(aWidget));
+ gtk_window_set_decorated(gtkWin, false);
+
+ GdkWindow* gdkWin = gtk_widget_get_window(aWidget);
+ GdkScreen* screen = gtk_widget_get_screen(aWidget);
+ gint monitorNum = gdk_screen_get_monitor_at_window(screen, gdkWin);
+ GdkRectangle monitorRect;
+ gdk_screen_get_monitor_geometry(screen, monitorNum, &monitorRect);
+ gtk_window_set_screen(gtkWin, screen);
+ gtk_window_move(gtkWin, monitorRect.x, monitorRect.y);
+ gtk_window_resize(gtkWin, monitorRect.width, monitorRect.height);
+
+ GdkColor bgColor;
+ bgColor.red = bgColor.green = bgColor.blue = 0;
+ gtk_widget_modify_bg(mWindow, GTK_STATE_NORMAL, &bgColor);
+
+ gtk_window_set_opacity(gtkWin, 0.0);
+ gtk_widget_show(mWindow);
+}
+
+FullscreenTransitionWindow::~FullscreenTransitionWindow()
+{
+ gtk_widget_destroy(mWindow);
+}
+
+class FullscreenTransitionData
+{
+public:
+ FullscreenTransitionData(nsIWidget::FullscreenTransitionStage aStage,
+ uint16_t aDuration, nsIRunnable* aCallback,
+ FullscreenTransitionWindow* aWindow)
+ : mStage(aStage)
+ , mStartTime(TimeStamp::Now())
+ , mDuration(TimeDuration::FromMilliseconds(aDuration))
+ , mCallback(aCallback)
+ , mWindow(aWindow) { }
+
+ static const guint sInterval = 1000 / 30; // 30fps
+ static gboolean TimeoutCallback(gpointer aData);
+
+private:
+ nsIWidget::FullscreenTransitionStage mStage;
+ TimeStamp mStartTime;
+ TimeDuration mDuration;
+ nsCOMPtr<nsIRunnable> mCallback;
+ RefPtr<FullscreenTransitionWindow> mWindow;
+};
+
+/* static */ gboolean
+FullscreenTransitionData::TimeoutCallback(gpointer aData)
+{
+ bool finishing = false;
+ auto data = static_cast<FullscreenTransitionData*>(aData);
+ gdouble opacity = (TimeStamp::Now() - data->mStartTime) / data->mDuration;
+ if (opacity >= 1.0) {
+ opacity = 1.0;
+ finishing = true;
+ }
+ if (data->mStage == nsIWidget::eAfterFullscreenToggle) {
+ opacity = 1.0 - opacity;
+ }
+ gtk_window_set_opacity(GTK_WINDOW(data->mWindow->mWindow), opacity);
+
+ if (!finishing) {
+ return TRUE;
+ }
+ NS_DispatchToMainThread(data->mCallback.forget());
+ delete data;
+ return FALSE;
+}
+
+/* virtual */ bool
+nsWindow::PrepareForFullscreenTransition(nsISupports** aData)
+{
+ GdkScreen* screen = gtk_widget_get_screen(mShell);
+ if (!gdk_screen_is_composited(screen)) {
+ return false;
+ }
+ *aData = do_AddRef(new FullscreenTransitionWindow(mShell)).take();
+ return true;
+}
+
+/* virtual */ void
+nsWindow::PerformFullscreenTransition(FullscreenTransitionStage aStage,
+ uint16_t aDuration, nsISupports* aData,
+ nsIRunnable* aCallback)
+{
+ auto data = static_cast<FullscreenTransitionWindow*>(aData);
+ // This will be released at the end of the last timeout callback for it.
+ auto transitionData = new FullscreenTransitionData(aStage, aDuration,
+ aCallback, data);
+ g_timeout_add_full(G_PRIORITY_HIGH,
+ FullscreenTransitionData::sInterval,
+ FullscreenTransitionData::TimeoutCallback,
+ transitionData, nullptr);
+}
+
+static bool
+IsFullscreenSupported(GtkWidget* aShell)
+{
+#ifdef MOZ_X11
+ GdkScreen* screen = gtk_widget_get_screen(aShell);
+ GdkAtom atom = gdk_atom_intern("_NET_WM_STATE_FULLSCREEN", FALSE);
+ if (!gdk_x11_screen_supports_net_wm_hint(screen, atom)) {
+ return false;
+ }
+#endif
+ return true;
+}
+
+nsresult
+nsWindow::MakeFullScreen(bool aFullScreen, nsIScreen* aTargetScreen)
+{
+ LOG(("nsWindow::MakeFullScreen [%p] aFullScreen %d\n",
+ (void *)this, aFullScreen));
+
+ if (!IsFullscreenSupported(mShell)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ if (aFullScreen) {
+ if (mSizeMode != nsSizeMode_Fullscreen)
+ mLastSizeMode = mSizeMode;
+
+ mSizeMode = nsSizeMode_Fullscreen;
+ gtk_window_fullscreen(GTK_WINDOW(mShell));
+ }
+ else {
+ mSizeMode = mLastSizeMode;
+ gtk_window_unfullscreen(GTK_WINDOW(mShell));
+ }
+
+ NS_ASSERTION(mLastSizeMode != nsSizeMode_Fullscreen,
+ "mLastSizeMode should never be fullscreen");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::HideWindowChrome(bool aShouldHide)
+{
+ if (!mShell) {
+ // Pass the request to the toplevel window
+ GtkWidget *topWidget = GetToplevelWidget();
+ if (!topWidget)
+ return NS_ERROR_FAILURE;
+
+ nsWindow *topWindow = get_window_for_gtk_widget(topWidget);
+ if (!topWindow)
+ return NS_ERROR_FAILURE;
+
+ return topWindow->HideWindowChrome(aShouldHide);
+ }
+
+ // Sawfish, metacity, and presumably other window managers get
+ // confused if we change the window decorations while the window
+ // is visible.
+ bool wasVisible = false;
+ if (gdk_window_is_visible(mGdkWindow)) {
+ gdk_window_hide(mGdkWindow);
+ wasVisible = true;
+ }
+
+ gint wmd;
+ if (aShouldHide)
+ wmd = 0;
+ else
+ wmd = ConvertBorderStyles(mBorderStyle);
+
+ if (wmd != -1)
+ gdk_window_set_decorations(mGdkWindow, (GdkWMDecoration) wmd);
+
+ if (wasVisible)
+ gdk_window_show(mGdkWindow);
+
+ // For some window managers, adding or removing window decorations
+ // requires unmapping and remapping our toplevel window. Go ahead
+ // and flush the queue here so that we don't end up with a BadWindow
+ // error later when this happens (when the persistence timer fires
+ // and GetWindowPos is called)
+#ifdef MOZ_X11
+ XSync(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()) , False);
+#else
+ gdk_flush ();
+#endif /* MOZ_X11 */
+
+ return NS_OK;
+}
+
+bool
+nsWindow::CheckForRollup(gdouble aMouseX, gdouble aMouseY,
+ bool aIsWheel, bool aAlwaysRollup)
+{
+ nsIRollupListener* rollupListener = GetActiveRollupListener();
+ nsCOMPtr<nsIWidget> rollupWidget;
+ if (rollupListener) {
+ rollupWidget = rollupListener->GetRollupWidget();
+ }
+ if (!rollupWidget) {
+ nsBaseWidget::gRollupListener = nullptr;
+ return false;
+ }
+
+ bool retVal = false;
+ GdkWindow *currentPopup =
+ (GdkWindow *)rollupWidget->GetNativeData(NS_NATIVE_WINDOW);
+ if (aAlwaysRollup || !is_mouse_in_window(currentPopup, aMouseX, aMouseY)) {
+ bool rollup = true;
+ if (aIsWheel) {
+ rollup = rollupListener->ShouldRollupOnMouseWheelEvent();
+ retVal = rollupListener->ShouldConsumeOnMouseWheelEvent();
+ }
+ // if we're dealing with menus, we probably have submenus and
+ // we don't want to rollup if the click is in a parent menu of
+ // the current submenu
+ uint32_t popupsToRollup = UINT32_MAX;
+ if (!aAlwaysRollup) {
+ AutoTArray<nsIWidget*, 5> widgetChain;
+ uint32_t sameTypeCount = rollupListener->GetSubmenuWidgetChain(&widgetChain);
+ for (uint32_t i=0; i<widgetChain.Length(); ++i) {
+ nsIWidget* widget = widgetChain[i];
+ GdkWindow* currWindow =
+ (GdkWindow*) widget->GetNativeData(NS_NATIVE_WINDOW);
+ if (is_mouse_in_window(currWindow, aMouseX, aMouseY)) {
+ // don't roll up if the mouse event occurred within a
+ // menu of the same type. If the mouse event occurred
+ // in a menu higher than that, roll up, but pass the
+ // number of popups to Rollup so that only those of the
+ // same type close up.
+ if (i < sameTypeCount) {
+ rollup = false;
+ }
+ else {
+ popupsToRollup = sameTypeCount;
+ }
+ break;
+ }
+ } // foreach parent menu widget
+ } // if rollup listener knows about menus
+
+ // if we've determined that we should still rollup, do it.
+ bool usePoint = !aIsWheel && !aAlwaysRollup;
+ IntPoint point = IntPoint::Truncate(aMouseX, aMouseY);
+ if (rollup && rollupListener->Rollup(popupsToRollup, true, usePoint ? &point : nullptr, nullptr)) {
+ retVal = true;
+ }
+ }
+ return retVal;
+}
+
+/* static */
+bool
+nsWindow::DragInProgress(void)
+{
+ nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
+
+ if (!dragService)
+ return false;
+
+ nsCOMPtr<nsIDragSession> currentDragSession;
+ dragService->GetCurrentSession(getter_AddRefs(currentDragSession));
+
+ return currentDragSession != nullptr;
+}
+
+static bool
+is_mouse_in_window (GdkWindow* aWindow, gdouble aMouseX, gdouble aMouseY)
+{
+ gint x = 0;
+ gint y = 0;
+ gint w, h;
+
+ gint offsetX = 0;
+ gint offsetY = 0;
+
+ GdkWindow *window = aWindow;
+
+ while (window) {
+ gint tmpX = 0;
+ gint tmpY = 0;
+
+ gdk_window_get_position(window, &tmpX, &tmpY);
+ GtkWidget *widget = get_gtk_widget_for_gdk_window(window);
+
+ // if this is a window, compute x and y given its origin and our
+ // offset
+ if (GTK_IS_WINDOW(widget)) {
+ x = tmpX + offsetX;
+ y = tmpY + offsetY;
+ break;
+ }
+
+ offsetX += tmpX;
+ offsetY += tmpY;
+ window = gdk_window_get_parent(window);
+ }
+
+#if (MOZ_WIDGET_GTK == 2)
+ gdk_drawable_get_size(aWindow, &w, &h);
+#else
+ w = gdk_window_get_width(aWindow);
+ h = gdk_window_get_height(aWindow);
+#endif
+
+ if (aMouseX > x && aMouseX < x + w &&
+ aMouseY > y && aMouseY < y + h)
+ return true;
+
+ return false;
+}
+
+static nsWindow *
+get_window_for_gtk_widget(GtkWidget *widget)
+{
+ gpointer user_data = g_object_get_data(G_OBJECT(widget), "nsWindow");
+
+ return static_cast<nsWindow *>(user_data);
+}
+
+static nsWindow *
+get_window_for_gdk_window(GdkWindow *window)
+{
+ gpointer user_data = g_object_get_data(G_OBJECT(window), "nsWindow");
+
+ return static_cast<nsWindow *>(user_data);
+}
+
+static GtkWidget *
+get_gtk_widget_for_gdk_window(GdkWindow *window)
+{
+ gpointer user_data = nullptr;
+ gdk_window_get_user_data(window, &user_data);
+
+ return GTK_WIDGET(user_data);
+}
+
+static GdkCursor *
+get_gtk_cursor(nsCursor aCursor)
+{
+ GdkCursor *gdkcursor = nullptr;
+ uint8_t newType = 0xff;
+
+ if ((gdkcursor = gCursorCache[aCursor])) {
+ return gdkcursor;
+ }
+
+ GdkDisplay *defaultDisplay = gdk_display_get_default();
+
+ // The strategy here is to use standard GDK cursors, and, if not available,
+ // load by standard name with gdk_cursor_new_from_name.
+ // Spec is here: http://www.freedesktop.org/wiki/Specifications/cursor-spec/
+ switch (aCursor) {
+ case eCursor_standard:
+ gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_LEFT_PTR);
+ break;
+ case eCursor_wait:
+ gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_WATCH);
+ break;
+ case eCursor_select:
+ gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_XTERM);
+ break;
+ case eCursor_hyperlink:
+ gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_HAND2);
+ break;
+ case eCursor_n_resize:
+ gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_TOP_SIDE);
+ break;
+ case eCursor_s_resize:
+ gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_BOTTOM_SIDE);
+ break;
+ case eCursor_w_resize:
+ gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_LEFT_SIDE);
+ break;
+ case eCursor_e_resize:
+ gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_RIGHT_SIDE);
+ break;
+ case eCursor_nw_resize:
+ gdkcursor = gdk_cursor_new_for_display(defaultDisplay,
+ GDK_TOP_LEFT_CORNER);
+ break;
+ case eCursor_se_resize:
+ gdkcursor = gdk_cursor_new_for_display(defaultDisplay,
+ GDK_BOTTOM_RIGHT_CORNER);
+ break;
+ case eCursor_ne_resize:
+ gdkcursor = gdk_cursor_new_for_display(defaultDisplay,
+ GDK_TOP_RIGHT_CORNER);
+ break;
+ case eCursor_sw_resize:
+ gdkcursor = gdk_cursor_new_for_display(defaultDisplay,
+ GDK_BOTTOM_LEFT_CORNER);
+ break;
+ case eCursor_crosshair:
+ gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_CROSSHAIR);
+ break;
+ case eCursor_move:
+ gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_FLEUR);
+ break;
+ case eCursor_help:
+ gdkcursor = gdk_cursor_new_for_display(defaultDisplay,
+ GDK_QUESTION_ARROW);
+ break;
+ case eCursor_copy: // CSS3
+ gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "copy");
+ if (!gdkcursor)
+ newType = MOZ_CURSOR_COPY;
+ break;
+ case eCursor_alias:
+ gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "alias");
+ if (!gdkcursor)
+ newType = MOZ_CURSOR_ALIAS;
+ break;
+ case eCursor_context_menu:
+ gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "context-menu");
+ if (!gdkcursor)
+ newType = MOZ_CURSOR_CONTEXT_MENU;
+ break;
+ case eCursor_cell:
+ gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_PLUS);
+ break;
+ // Those two aren’t standardized. Trying both KDE’s and GNOME’s names
+ case eCursor_grab:
+ gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "openhand");
+ if (!gdkcursor)
+ newType = MOZ_CURSOR_HAND_GRAB;
+ break;
+ case eCursor_grabbing:
+ gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "closedhand");
+ if (!gdkcursor)
+ gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "grabbing");
+ if (!gdkcursor)
+ newType = MOZ_CURSOR_HAND_GRABBING;
+ break;
+ case eCursor_spinning:
+ gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "progress");
+ if (!gdkcursor)
+ newType = MOZ_CURSOR_SPINNING;
+ break;
+ case eCursor_zoom_in:
+ newType = MOZ_CURSOR_ZOOM_IN;
+ break;
+ case eCursor_zoom_out:
+ newType = MOZ_CURSOR_ZOOM_OUT;
+ break;
+ case eCursor_not_allowed:
+ gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "not-allowed");
+ if (!gdkcursor) // nonstandard, yet common
+ gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "crossed_circle");
+ if (!gdkcursor)
+ newType = MOZ_CURSOR_NOT_ALLOWED;
+ break;
+ case eCursor_no_drop:
+ gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "no-drop");
+ if (!gdkcursor) // this nonstandard sequence makes it work on KDE and GNOME
+ gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "forbidden");
+ if (!gdkcursor)
+ gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "circle");
+ if (!gdkcursor)
+ newType = MOZ_CURSOR_NOT_ALLOWED;
+ break;
+ case eCursor_vertical_text:
+ newType = MOZ_CURSOR_VERTICAL_TEXT;
+ break;
+ case eCursor_all_scroll:
+ gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_FLEUR);
+ break;
+ case eCursor_nesw_resize:
+ gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "size_bdiag");
+ if (!gdkcursor)
+ newType = MOZ_CURSOR_NESW_RESIZE;
+ break;
+ case eCursor_nwse_resize:
+ gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "size_fdiag");
+ if (!gdkcursor)
+ newType = MOZ_CURSOR_NWSE_RESIZE;
+ break;
+ case eCursor_ns_resize:
+ gdkcursor = gdk_cursor_new_for_display(defaultDisplay,
+ GDK_SB_V_DOUBLE_ARROW);
+ break;
+ case eCursor_ew_resize:
+ gdkcursor = gdk_cursor_new_for_display(defaultDisplay,
+ GDK_SB_H_DOUBLE_ARROW);
+ break;
+ // Here, two better fitting cursors exist in some cursor themes. Try those first
+ case eCursor_row_resize:
+ gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "split_v");
+ if (!gdkcursor)
+ gdkcursor = gdk_cursor_new_for_display(defaultDisplay,
+ GDK_SB_V_DOUBLE_ARROW);
+ break;
+ case eCursor_col_resize:
+ gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "split_h");
+ if (!gdkcursor)
+ gdkcursor = gdk_cursor_new_for_display(defaultDisplay,
+ GDK_SB_H_DOUBLE_ARROW);
+ break;
+ case eCursor_none:
+ newType = MOZ_CURSOR_NONE;
+ break;
+ default:
+ NS_ASSERTION(aCursor, "Invalid cursor type");
+ gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_LEFT_PTR);
+ break;
+ }
+
+ // If by now we don't have a xcursor, this means we have to make a custom
+ // one. First, we try creating a named cursor based on the hash of our
+ // custom bitmap, as libXcursor has some magic to convert bitmapped cursors
+ // to themed cursors
+ if (newType != 0xFF && GtkCursors[newType].hash) {
+ gdkcursor = gdk_cursor_new_from_name(defaultDisplay, GtkCursors[newType].hash);
+ }
+
+ // If we still don't have a xcursor, we now really create a bitmap cursor
+ if (newType != 0xff && !gdkcursor) {
+ GdkPixbuf * cursor_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32);
+ if (!cursor_pixbuf)
+ return nullptr;
+
+ guchar *data = gdk_pixbuf_get_pixels(cursor_pixbuf);
+
+ // Read data from GtkCursors and compose RGBA surface from 1bit bitmap and mask
+ // GtkCursors bits and mask are 32x32 monochrome bitmaps (1 bit for each pixel)
+ // so it's 128 byte array (4 bytes for are one bitmap row and there are 32 rows here).
+ const unsigned char *bits = GtkCursors[newType].bits;
+ const unsigned char *mask_bits = GtkCursors[newType].mask_bits;
+
+ for (int i = 0; i < 128; i++) {
+ char bit = *bits++;
+ char mask = *mask_bits++;
+ for (int j = 0; j < 8; j++) {
+ unsigned char pix = ~(((bit >> j) & 0x01) * 0xff);
+ *data++ = pix;
+ *data++ = pix;
+ *data++ = pix;
+ *data++ = (((mask >> j) & 0x01) * 0xff);
+ }
+ }
+
+ gdkcursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), cursor_pixbuf,
+ GtkCursors[newType].hot_x,
+ GtkCursors[newType].hot_y);
+
+ g_object_unref(cursor_pixbuf);
+ }
+
+ gCursorCache[aCursor] = gdkcursor;
+
+ return gdkcursor;
+}
+
+// gtk callbacks
+
+#if (MOZ_WIDGET_GTK == 2)
+static gboolean
+expose_event_cb(GtkWidget *widget, GdkEventExpose *event)
+{
+ RefPtr<nsWindow> window = get_window_for_gdk_window(event->window);
+ if (!window)
+ return FALSE;
+
+ window->OnExposeEvent(event);
+ return FALSE;
+}
+#else
+void
+draw_window_of_widget(GtkWidget *widget, GdkWindow *aWindow, cairo_t *cr)
+{
+ if (gtk_cairo_should_draw_window(cr, aWindow)) {
+ RefPtr<nsWindow> window = get_window_for_gdk_window(aWindow);
+ if (!window) {
+ NS_WARNING("Cannot get nsWindow from GtkWidget");
+ }
+ else {
+ cairo_save(cr);
+ gtk_cairo_transform_to_window(cr, widget, aWindow);
+ // TODO - window->OnExposeEvent() can destroy this or other windows,
+ // do we need to handle it somehow?
+ window->OnExposeEvent(cr);
+ cairo_restore(cr);
+ }
+ }
+
+ GList *children = gdk_window_get_children(aWindow);
+ GList *child = children;
+ while (child) {
+ GdkWindow *window = GDK_WINDOW(child->data);
+ gpointer windowWidget;
+ gdk_window_get_user_data(window, &windowWidget);
+ if (windowWidget == widget) {
+ draw_window_of_widget(widget, window, cr);
+ }
+ child = g_list_next(child);
+ }
+ g_list_free(children);
+}
+
+/* static */
+gboolean
+expose_event_cb(GtkWidget *widget, cairo_t *cr)
+{
+ draw_window_of_widget(widget, gtk_widget_get_window(widget), cr);
+
+ // A strong reference is already held during "draw" signal emission,
+ // but GTK+ 3.4 wants the object to live a little longer than that
+ // (bug 1225970).
+ g_object_ref(widget);
+ g_idle_add(
+ [](gpointer data) -> gboolean {
+ g_object_unref(data);
+ return G_SOURCE_REMOVE;
+ },
+ widget);
+
+ return FALSE;
+}
+#endif //MOZ_WIDGET_GTK == 2
+
+static gboolean
+configure_event_cb(GtkWidget *widget,
+ GdkEventConfigure *event)
+{
+ RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
+ if (!window)
+ return FALSE;
+
+ return window->OnConfigureEvent(widget, event);
+}
+
+static void
+container_unrealize_cb (GtkWidget *widget)
+{
+ RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
+ if (!window)
+ return;
+
+ window->OnContainerUnrealize();
+}
+
+static void
+size_allocate_cb (GtkWidget *widget, GtkAllocation *allocation)
+{
+ RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
+ if (!window)
+ return;
+
+ window->OnSizeAllocate(allocation);
+}
+
+static gboolean
+delete_event_cb(GtkWidget *widget, GdkEventAny *event)
+{
+ RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
+ if (!window)
+ return FALSE;
+
+ window->OnDeleteEvent();
+
+ return TRUE;
+}
+
+static gboolean
+enter_notify_event_cb(GtkWidget *widget,
+ GdkEventCrossing *event)
+{
+ RefPtr<nsWindow> window = get_window_for_gdk_window(event->window);
+ if (!window)
+ return TRUE;
+
+ window->OnEnterNotifyEvent(event);
+
+ return TRUE;
+}
+
+static gboolean
+leave_notify_event_cb(GtkWidget *widget,
+ GdkEventCrossing *event)
+{
+ if (is_parent_grab_leave(event)) {
+ return TRUE;
+ }
+
+ // bug 369599: Suppress LeaveNotify events caused by pointer grabs to
+ // avoid generating spurious mouse exit events.
+ gint x = gint(event->x_root);
+ gint y = gint(event->y_root);
+ GdkDisplay* display = gtk_widget_get_display(widget);
+ GdkWindow* winAtPt = gdk_display_get_window_at_pointer(display, &x, &y);
+ if (winAtPt == event->window) {
+ return TRUE;
+ }
+
+ RefPtr<nsWindow> window = get_window_for_gdk_window(event->window);
+ if (!window)
+ return TRUE;
+
+ window->OnLeaveNotifyEvent(event);
+
+ return TRUE;
+}
+
+static nsWindow*
+GetFirstNSWindowForGDKWindow(GdkWindow *aGdkWindow)
+{
+ nsWindow* window;
+ while (!(window = get_window_for_gdk_window(aGdkWindow))) {
+ // The event has bubbled to the moz_container widget as passed into each caller's *widget parameter,
+ // but its corresponding nsWindow is an ancestor of the window that we need. Instead, look at
+ // event->window and find the first ancestor nsWindow of it because event->window may be in a plugin.
+ aGdkWindow = gdk_window_get_parent(aGdkWindow);
+ if (!aGdkWindow) {
+ window = nullptr;
+ break;
+ }
+ }
+ return window;
+}
+
+static gboolean
+motion_notify_event_cb(GtkWidget *widget, GdkEventMotion *event)
+{
+ UpdateLastInputEventTime(event);
+
+ nsWindow *window = GetFirstNSWindowForGDKWindow(event->window);
+ if (!window)
+ return FALSE;
+
+ window->OnMotionNotifyEvent(event);
+
+ return TRUE;
+}
+
+static gboolean
+button_press_event_cb(GtkWidget *widget, GdkEventButton *event)
+{
+ UpdateLastInputEventTime(event);
+
+ nsWindow *window = GetFirstNSWindowForGDKWindow(event->window);
+ if (!window)
+ return FALSE;
+
+ window->OnButtonPressEvent(event);
+
+ return TRUE;
+}
+
+static gboolean
+button_release_event_cb(GtkWidget *widget, GdkEventButton *event)
+{
+ UpdateLastInputEventTime(event);
+
+ nsWindow *window = GetFirstNSWindowForGDKWindow(event->window);
+ if (!window)
+ return FALSE;
+
+ window->OnButtonReleaseEvent(event);
+
+ return TRUE;
+}
+
+static gboolean
+focus_in_event_cb(GtkWidget *widget, GdkEventFocus *event)
+{
+ RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
+ if (!window)
+ return FALSE;
+
+ window->OnContainerFocusInEvent(event);
+
+ return FALSE;
+}
+
+static gboolean
+focus_out_event_cb(GtkWidget *widget, GdkEventFocus *event)
+{
+ RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
+ if (!window)
+ return FALSE;
+
+ window->OnContainerFocusOutEvent(event);
+
+ return FALSE;
+}
+
+#ifdef MOZ_X11
+// For long-lived popup windows that don't really take focus themselves but
+// may have elements that accept keyboard input when the parent window is
+// active, focus is handled specially. These windows include noautohide
+// panels. (This special handling is not necessary for temporary popups where
+// the keyboard is grabbed.)
+//
+// Mousing over or clicking on these windows should not cause them to steal
+// focus from their parent windows, so, the input field of WM_HINTS is set to
+// False to request that the window manager not set the input focus to this
+// window. http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7
+//
+// However, these windows can still receive WM_TAKE_FOCUS messages from the
+// window manager, so they can still detect when the user has indicated that
+// they wish to direct keyboard input at these windows. When the window
+// manager offers focus to these windows (after a mouse over or click, for
+// example), a request to make the parent window active is issued. When the
+// parent window becomes active, keyboard events will be received.
+
+static GdkFilterReturn
+popup_take_focus_filter(GdkXEvent *gdk_xevent,
+ GdkEvent *event,
+ gpointer data)
+{
+ XEvent* xevent = static_cast<XEvent*>(gdk_xevent);
+ if (xevent->type != ClientMessage)
+ return GDK_FILTER_CONTINUE;
+
+ XClientMessageEvent& xclient = xevent->xclient;
+ if (xclient.message_type != gdk_x11_get_xatom_by_name("WM_PROTOCOLS"))
+ return GDK_FILTER_CONTINUE;
+
+ Atom atom = xclient.data.l[0];
+ if (atom != gdk_x11_get_xatom_by_name("WM_TAKE_FOCUS"))
+ return GDK_FILTER_CONTINUE;
+
+ guint32 timestamp = xclient.data.l[1];
+
+ GtkWidget* widget = get_gtk_widget_for_gdk_window(event->any.window);
+ if (!widget)
+ return GDK_FILTER_CONTINUE;
+
+ GtkWindow* parent = gtk_window_get_transient_for(GTK_WINDOW(widget));
+ if (!parent)
+ return GDK_FILTER_CONTINUE;
+
+ if (gtk_window_is_active(parent))
+ return GDK_FILTER_REMOVE; // leave input focus on the parent
+
+ GdkWindow* parent_window = gtk_widget_get_window(GTK_WIDGET(parent));
+ if (!parent_window)
+ return GDK_FILTER_CONTINUE;
+
+ // In case the parent has not been deconified.
+ gdk_window_show_unraised(parent_window);
+
+ // Request focus on the parent window.
+ // Use gdk_window_focus rather than gtk_window_present to avoid
+ // raising the parent window.
+ gdk_window_focus(parent_window, timestamp);
+ return GDK_FILTER_REMOVE;
+}
+
+static GdkFilterReturn
+plugin_window_filter_func(GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
+{
+ GdkWindow *plugin_window;
+ XEvent *xevent;
+ Window xeventWindow;
+
+ RefPtr<nsWindow> nswindow = (nsWindow*)data;
+ GdkFilterReturn return_val;
+
+ xevent = (XEvent *)gdk_xevent;
+ return_val = GDK_FILTER_CONTINUE;
+
+ switch (xevent->type)
+ {
+ case CreateNotify:
+ case ReparentNotify:
+ if (xevent->type==CreateNotify) {
+ xeventWindow = xevent->xcreatewindow.window;
+ }
+ else {
+ if (xevent->xreparent.event != xevent->xreparent.parent)
+ break;
+ xeventWindow = xevent->xreparent.window;
+ }
+#if (MOZ_WIDGET_GTK == 2)
+ plugin_window = gdk_window_lookup(xeventWindow);
+#else
+ plugin_window = gdk_x11_window_lookup_for_display(
+ gdk_x11_lookup_xdisplay(xevent->xcreatewindow.display), xeventWindow);
+#endif
+ if (plugin_window) {
+ GtkWidget *widget =
+ get_gtk_widget_for_gdk_window(plugin_window);
+
+// TODO GTK3
+#if (MOZ_WIDGET_GTK == 2)
+ if (GTK_IS_XTBIN(widget)) {
+ nswindow->SetPluginType(nsWindow::PluginType_NONXEMBED);
+ break;
+ }
+ else
+#endif
+ if(GTK_IS_SOCKET(widget)) {
+ if (!g_object_get_data(G_OBJECT(widget), "enable-xt-focus")) {
+ nswindow->SetPluginType(nsWindow::PluginType_XEMBED);
+ break;
+ }
+ }
+ }
+ nswindow->SetPluginType(nsWindow::PluginType_NONXEMBED);
+ return_val = GDK_FILTER_REMOVE;
+ break;
+ case EnterNotify:
+ nswindow->SetNonXEmbedPluginFocus();
+ break;
+ case DestroyNotify:
+ gdk_window_remove_filter
+ ((GdkWindow*)(nswindow->GetNativeData(NS_NATIVE_WINDOW)),
+ plugin_window_filter_func,
+ nswindow);
+ // Currently we consider all plugins are non-xembed and calls
+ // LoseNonXEmbedPluginFocus without any checking.
+ nswindow->LoseNonXEmbedPluginFocus();
+ break;
+ default:
+ break;
+ }
+ return return_val;
+}
+
+static GdkFilterReturn
+plugin_client_message_filter(GdkXEvent *gdk_xevent,
+ GdkEvent *event,
+ gpointer data)
+{
+ XEvent *xevent;
+ xevent = (XEvent *)gdk_xevent;
+
+ GdkFilterReturn return_val;
+ return_val = GDK_FILTER_CONTINUE;
+
+ if (!gPluginFocusWindow || xevent->type!=ClientMessage) {
+ return return_val;
+ }
+
+ // When WM sends out WM_TAKE_FOCUS, gtk2 will use XSetInputFocus
+ // to set the focus to the focus proxy. To prevent this happen
+ // while the focus is on the plugin, we filter the WM_TAKE_FOCUS
+ // out.
+ if (gdk_x11_get_xatom_by_name("WM_PROTOCOLS")
+ != xevent->xclient.message_type) {
+ return return_val;
+ }
+
+ if ((Atom) xevent->xclient.data.l[0] ==
+ gdk_x11_get_xatom_by_name("WM_TAKE_FOCUS")) {
+ // block it from gtk2.0 focus proxy
+ return_val = GDK_FILTER_REMOVE;
+ }
+
+ return return_val;
+}
+#endif /* MOZ_X11 */
+
+static gboolean
+key_press_event_cb(GtkWidget *widget, GdkEventKey *event)
+{
+ LOG(("key_press_event_cb\n"));
+
+ UpdateLastInputEventTime(event);
+
+ // find the window with focus and dispatch this event to that widget
+ nsWindow *window = get_window_for_gtk_widget(widget);
+ if (!window)
+ return FALSE;
+
+ RefPtr<nsWindow> focusWindow = gFocusWindow ? gFocusWindow : window;
+
+#ifdef MOZ_X11
+ // Keyboard repeat can cause key press events to queue up when there are
+ // slow event handlers (bug 301029). Throttle these events by removing
+ // consecutive pending duplicate KeyPress events to the same window.
+ // We use the event time of the last one.
+ // Note: GDK calls XkbSetDetectableAutorepeat so that KeyRelease events
+ // are generated only when the key is physically released.
+#define NS_GDKEVENT_MATCH_MASK 0x1FFF /* GDK_SHIFT_MASK .. GDK_BUTTON5_MASK */
+ GdkDisplay* gdkDisplay = gtk_widget_get_display(widget);
+ if (GDK_IS_X11_DISPLAY(gdkDisplay)) {
+ Display* dpy = GDK_DISPLAY_XDISPLAY(gdkDisplay);
+ while (XPending(dpy)) {
+ XEvent next_event;
+ XPeekEvent(dpy, &next_event);
+ GdkWindow* nextGdkWindow =
+ gdk_x11_window_lookup_for_display(gdkDisplay, next_event.xany.window);
+ if (nextGdkWindow != event->window ||
+ next_event.type != KeyPress ||
+ next_event.xkey.keycode != event->hardware_keycode ||
+ next_event.xkey.state != (event->state & NS_GDKEVENT_MATCH_MASK)) {
+ break;
+ }
+ XNextEvent(dpy, &next_event);
+ event->time = next_event.xkey.time;
+ }
+ }
+#endif
+
+ return focusWindow->OnKeyPressEvent(event);
+}
+
+static gboolean
+key_release_event_cb(GtkWidget *widget, GdkEventKey *event)
+{
+ LOG(("key_release_event_cb\n"));
+
+ UpdateLastInputEventTime(event);
+
+ // find the window with focus and dispatch this event to that widget
+ nsWindow *window = get_window_for_gtk_widget(widget);
+ if (!window)
+ return FALSE;
+
+ RefPtr<nsWindow> focusWindow = gFocusWindow ? gFocusWindow : window;
+
+ return focusWindow->OnKeyReleaseEvent(event);
+}
+
+static gboolean
+property_notify_event_cb(GtkWidget* aWidget, GdkEventProperty* aEvent)
+{
+ RefPtr<nsWindow> window = get_window_for_gdk_window(aEvent->window);
+ if (!window)
+ return FALSE;
+
+ return window->OnPropertyNotifyEvent(aWidget, aEvent);
+}
+
+static gboolean
+scroll_event_cb(GtkWidget *widget, GdkEventScroll *event)
+{
+ nsWindow *window = GetFirstNSWindowForGDKWindow(event->window);
+ if (!window)
+ return FALSE;
+
+ window->OnScrollEvent(event);
+
+ return TRUE;
+}
+
+static gboolean
+visibility_notify_event_cb (GtkWidget *widget, GdkEventVisibility *event)
+{
+ RefPtr<nsWindow> window = get_window_for_gdk_window(event->window);
+ if (!window)
+ return FALSE;
+
+ window->OnVisibilityNotifyEvent(event);
+
+ return TRUE;
+}
+
+static void
+hierarchy_changed_cb (GtkWidget *widget,
+ GtkWidget *previous_toplevel)
+{
+ GtkWidget *toplevel = gtk_widget_get_toplevel(widget);
+ GdkWindowState old_window_state = GDK_WINDOW_STATE_WITHDRAWN;
+ GdkEventWindowState event;
+
+ event.new_window_state = GDK_WINDOW_STATE_WITHDRAWN;
+
+ if (GTK_IS_WINDOW(previous_toplevel)) {
+ g_signal_handlers_disconnect_by_func(previous_toplevel,
+ FuncToGpointer(window_state_event_cb),
+ widget);
+ GdkWindow *win = gtk_widget_get_window(previous_toplevel);
+ if (win) {
+ old_window_state = gdk_window_get_state(win);
+ }
+ }
+
+ if (GTK_IS_WINDOW(toplevel)) {
+ g_signal_connect_swapped(toplevel, "window-state-event",
+ G_CALLBACK(window_state_event_cb), widget);
+ GdkWindow *win = gtk_widget_get_window(toplevel);
+ if (win) {
+ event.new_window_state = gdk_window_get_state(win);
+ }
+ }
+
+ event.changed_mask = static_cast<GdkWindowState>
+ (old_window_state ^ event.new_window_state);
+
+ if (event.changed_mask) {
+ event.type = GDK_WINDOW_STATE;
+ event.window = nullptr;
+ event.send_event = TRUE;
+ window_state_event_cb(widget, &event);
+ }
+}
+
+static gboolean
+window_state_event_cb (GtkWidget *widget, GdkEventWindowState *event)
+{
+ RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
+ if (!window)
+ return FALSE;
+
+ window->OnWindowStateEvent(widget, event);
+
+ return FALSE;
+}
+
+static void
+theme_changed_cb (GtkSettings *settings, GParamSpec *pspec, nsWindow *data)
+{
+ RefPtr<nsWindow> window = data;
+ window->ThemeChanged();
+}
+
+static void
+check_resize_cb (GtkContainer* container, gpointer user_data)
+{
+ RefPtr<nsWindow> window = get_window_for_gtk_widget(GTK_WIDGET(container));
+ if (!window) {
+ return;
+ }
+ window->OnCheckResize();
+}
+
+#if (MOZ_WIDGET_GTK == 3)
+static void
+scale_changed_cb (GtkWidget* widget, GParamSpec* aPSpec, gpointer aPointer)
+{
+ RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
+ if (!window) {
+ return;
+ }
+ window->OnDPIChanged();
+
+ // configure_event is already fired before scale-factor signal,
+ // but size-allocate isn't fired by changing scale
+ GtkAllocation allocation;
+ gtk_widget_get_allocation(widget, &allocation);
+ window->OnSizeAllocate(&allocation);
+}
+#endif
+
+#if GTK_CHECK_VERSION(3,4,0)
+static gboolean
+touch_event_cb(GtkWidget* aWidget, GdkEventTouch* aEvent)
+{
+ UpdateLastInputEventTime(aEvent);
+
+ nsWindow* window = GetFirstNSWindowForGDKWindow(aEvent->window);
+ if (!window) {
+ return FALSE;
+ }
+
+ return window->OnTouchEvent(aEvent);
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// These are all of our drag and drop operations
+
+void
+nsWindow::InitDragEvent(WidgetDragEvent &aEvent)
+{
+ // set the keyboard modifiers
+ guint modifierState = KeymapWrapper::GetCurrentModifierState();
+ KeymapWrapper::InitInputEvent(aEvent, modifierState);
+}
+
+static gboolean
+drag_motion_event_cb(GtkWidget *aWidget,
+ GdkDragContext *aDragContext,
+ gint aX,
+ gint aY,
+ guint aTime,
+ gpointer aData)
+{
+ RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
+ if (!window)
+ return FALSE;
+
+ // figure out which internal widget this drag motion actually happened on
+ nscoord retx = 0;
+ nscoord rety = 0;
+
+ GdkWindow *innerWindow =
+ get_inner_gdk_window(gtk_widget_get_window(aWidget), aX, aY,
+ &retx, &rety);
+ RefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow);
+
+ if (!innerMostWindow) {
+ innerMostWindow = window;
+ }
+
+ LOGDRAG(("nsWindow drag-motion signal for %p\n", (void*)innerMostWindow));
+
+ LayoutDeviceIntPoint point = window->GdkPointToDevicePixels({ retx, rety });
+
+ return nsDragService::GetInstance()->
+ ScheduleMotionEvent(innerMostWindow, aDragContext,
+ point, aTime);
+}
+
+static void
+drag_leave_event_cb(GtkWidget *aWidget,
+ GdkDragContext *aDragContext,
+ guint aTime,
+ gpointer aData)
+{
+ RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
+ if (!window)
+ return;
+
+ nsDragService *dragService = nsDragService::GetInstance();
+
+ nsWindow *mostRecentDragWindow = dragService->GetMostRecentDestWindow();
+ if (!mostRecentDragWindow) {
+ // This can happen when the target will not accept a drop. A GTK drag
+ // source sends the leave message to the destination before the
+ // drag-failed signal on the source widget, but the leave message goes
+ // via the X server, and so doesn't get processed at least until the
+ // event loop runs again.
+ return;
+ }
+
+ GtkWidget *mozContainer = mostRecentDragWindow->GetMozContainerWidget();
+ if (aWidget != mozContainer)
+ {
+ // When the drag moves between widgets, GTK can send leave signal for
+ // the old widget after the motion or drop signal for the new widget.
+ // We'll send the leave event when the motion or drop event is run.
+ return;
+ }
+
+ LOGDRAG(("nsWindow drag-leave signal for %p\n",
+ (void*)mostRecentDragWindow));
+
+ dragService->ScheduleLeaveEvent();
+}
+
+
+static gboolean
+drag_drop_event_cb(GtkWidget *aWidget,
+ GdkDragContext *aDragContext,
+ gint aX,
+ gint aY,
+ guint aTime,
+ gpointer aData)
+{
+ RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
+ if (!window)
+ return FALSE;
+
+ // figure out which internal widget this drag motion actually happened on
+ nscoord retx = 0;
+ nscoord rety = 0;
+
+ GdkWindow *innerWindow =
+ get_inner_gdk_window(gtk_widget_get_window(aWidget), aX, aY,
+ &retx, &rety);
+ RefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow);
+
+ if (!innerMostWindow) {
+ innerMostWindow = window;
+ }
+
+ LOGDRAG(("nsWindow drag-drop signal for %p\n", (void*)innerMostWindow));
+
+ LayoutDeviceIntPoint point = window->GdkPointToDevicePixels({ retx, rety });
+
+ return nsDragService::GetInstance()->
+ ScheduleDropEvent(innerMostWindow, aDragContext,
+ point, aTime);
+}
+
+static void
+drag_data_received_event_cb(GtkWidget *aWidget,
+ GdkDragContext *aDragContext,
+ gint aX,
+ gint aY,
+ GtkSelectionData *aSelectionData,
+ guint aInfo,
+ guint aTime,
+ gpointer aData)
+{
+ RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
+ if (!window)
+ return;
+
+ window->OnDragDataReceivedEvent(aWidget,
+ aDragContext,
+ aX, aY,
+ aSelectionData,
+ aInfo, aTime, aData);
+}
+
+static nsresult
+initialize_prefs(void)
+{
+ gRaiseWindows =
+ Preferences::GetBool("mozilla.widget.raise-on-setfocus", true);
+
+ return NS_OK;
+}
+
+static GdkWindow *
+get_inner_gdk_window (GdkWindow *aWindow,
+ gint x, gint y,
+ gint *retx, gint *rety)
+{
+ gint cx, cy, cw, ch;
+ GList *children = gdk_window_peek_children(aWindow);
+ for (GList *child = g_list_last(children);
+ child;
+ child = g_list_previous(child)) {
+ GdkWindow *childWindow = (GdkWindow *) child->data;
+ if (get_window_for_gdk_window(childWindow)) {
+#if (MOZ_WIDGET_GTK == 2)
+ gdk_window_get_geometry(childWindow, &cx, &cy, &cw, &ch, nullptr);
+#else
+ gdk_window_get_geometry(childWindow, &cx, &cy, &cw, &ch);
+#endif
+ if ((cx < x) && (x < (cx + cw)) &&
+ (cy < y) && (y < (cy + ch)) &&
+ gdk_window_is_visible(childWindow)) {
+ return get_inner_gdk_window(childWindow,
+ x - cx, y - cy,
+ retx, rety);
+ }
+ }
+ }
+ *retx = x;
+ *rety = y;
+ return aWindow;
+}
+
+static inline bool
+is_context_menu_key(const WidgetKeyboardEvent& aKeyEvent)
+{
+ return ((aKeyEvent.mKeyCode == NS_VK_F10 && aKeyEvent.IsShift() &&
+ !aKeyEvent.IsControl() && !aKeyEvent.IsMeta() &&
+ !aKeyEvent.IsAlt()) ||
+ (aKeyEvent.mKeyCode == NS_VK_CONTEXT_MENU && !aKeyEvent.IsShift() &&
+ !aKeyEvent.IsControl() && !aKeyEvent.IsMeta() &&
+ !aKeyEvent.IsAlt()));
+}
+
+static int
+is_parent_ungrab_enter(GdkEventCrossing *aEvent)
+{
+ return (GDK_CROSSING_UNGRAB == aEvent->mode) &&
+ ((GDK_NOTIFY_ANCESTOR == aEvent->detail) ||
+ (GDK_NOTIFY_VIRTUAL == aEvent->detail));
+
+}
+
+static int
+is_parent_grab_leave(GdkEventCrossing *aEvent)
+{
+ return (GDK_CROSSING_GRAB == aEvent->mode) &&
+ ((GDK_NOTIFY_ANCESTOR == aEvent->detail) ||
+ (GDK_NOTIFY_VIRTUAL == aEvent->detail));
+}
+
+#ifdef ACCESSIBILITY
+void
+nsWindow::CreateRootAccessible()
+{
+ if (mIsTopLevel && !mRootAccessible) {
+ LOG(("nsWindow:: Create Toplevel Accessibility\n"));
+ mRootAccessible = GetRootAccessible();
+ }
+}
+
+void
+nsWindow::DispatchEventToRootAccessible(uint32_t aEventType)
+{
+ if (!a11y::ShouldA11yBeEnabled()) {
+ return;
+ }
+
+ nsAccessibilityService* accService = GetOrCreateAccService();
+ if (!accService) {
+ return;
+ }
+
+ // Get the root document accessible and fire event to it.
+ a11y::Accessible* acc = GetRootAccessible();
+ if (acc) {
+ accService->FireAccessibleEvent(aEventType, acc);
+ }
+}
+
+void
+nsWindow::DispatchActivateEventAccessible(void)
+{
+ DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_ACTIVATE);
+}
+
+void
+nsWindow::DispatchDeactivateEventAccessible(void)
+{
+ DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_DEACTIVATE);
+}
+
+void
+nsWindow::DispatchMaximizeEventAccessible(void)
+{
+ DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_MAXIMIZE);
+}
+
+void
+nsWindow::DispatchMinimizeEventAccessible(void)
+{
+ DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_MINIMIZE);
+}
+
+void
+nsWindow::DispatchRestoreEventAccessible(void)
+{
+ DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_RESTORE);
+}
+
+#endif /* #ifdef ACCESSIBILITY */
+
+// nsChildWindow class
+
+nsChildWindow::nsChildWindow()
+{
+}
+
+nsChildWindow::~nsChildWindow()
+{
+}
+
+NS_IMETHODIMP_(void)
+nsWindow::SetInputContext(const InputContext& aContext,
+ const InputContextAction& aAction)
+{
+ if (!mIMContext) {
+ return;
+ }
+ mIMContext->SetInputContext(this, &aContext, &aAction);
+}
+
+NS_IMETHODIMP_(InputContext)
+nsWindow::GetInputContext()
+{
+ InputContext context;
+ if (!mIMContext) {
+ context.mIMEState.mEnabled = IMEState::DISABLED;
+ context.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED;
+ } else {
+ context = mIMContext->GetInputContext();
+ }
+ return context;
+}
+
+nsIMEUpdatePreference
+nsWindow::GetIMEUpdatePreference()
+{
+ if (!mIMContext) {
+ return nsIMEUpdatePreference();
+ }
+ return mIMContext->GetIMEUpdatePreference();
+}
+
+NS_IMETHODIMP_(TextEventDispatcherListener*)
+nsWindow::GetNativeTextEventDispatcherListener()
+{
+ if (NS_WARN_IF(!mIMContext)) {
+ return nullptr;
+ }
+ return mIMContext;
+}
+
+bool
+nsWindow::ExecuteNativeKeyBindingRemapped(NativeKeyBindingsType aType,
+ const WidgetKeyboardEvent& aEvent,
+ DoCommandCallback aCallback,
+ void* aCallbackData,
+ uint32_t aGeckoKeyCode,
+ uint32_t aNativeKeyCode)
+{
+ WidgetKeyboardEvent modifiedEvent(aEvent);
+ modifiedEvent.mKeyCode = aGeckoKeyCode;
+ static_cast<GdkEventKey*>(modifiedEvent.mNativeKeyEvent)->keyval =
+ aNativeKeyCode;
+
+ NativeKeyBindings* keyBindings = NativeKeyBindings::GetInstance(aType);
+ return keyBindings->Execute(modifiedEvent, aCallback, aCallbackData);
+}
+
+NS_IMETHODIMP_(bool)
+nsWindow::ExecuteNativeKeyBinding(NativeKeyBindingsType aType,
+ const WidgetKeyboardEvent& aEvent,
+ DoCommandCallback aCallback,
+ void* aCallbackData)
+{
+ if (aEvent.mKeyCode >= NS_VK_LEFT && aEvent.mKeyCode <= NS_VK_DOWN) {
+
+ // Check if we're targeting content with vertical writing mode,
+ // and if so remap the arrow keys.
+ WidgetQueryContentEvent query(true, eQuerySelectedText, this);
+ nsEventStatus status;
+ DispatchEvent(&query, status);
+
+ if (query.mSucceeded && query.mReply.mWritingMode.IsVertical()) {
+ uint32_t geckoCode = 0;
+ uint32_t gdkCode = 0;
+ switch (aEvent.mKeyCode) {
+ case NS_VK_LEFT:
+ if (query.mReply.mWritingMode.IsVerticalLR()) {
+ geckoCode = NS_VK_UP;
+ gdkCode = GDK_Up;
+ } else {
+ geckoCode = NS_VK_DOWN;
+ gdkCode = GDK_Down;
+ }
+ break;
+
+ case NS_VK_RIGHT:
+ if (query.mReply.mWritingMode.IsVerticalLR()) {
+ geckoCode = NS_VK_DOWN;
+ gdkCode = GDK_Down;
+ } else {
+ geckoCode = NS_VK_UP;
+ gdkCode = GDK_Up;
+ }
+ break;
+
+ case NS_VK_UP:
+ geckoCode = NS_VK_LEFT;
+ gdkCode = GDK_Left;
+ break;
+
+ case NS_VK_DOWN:
+ geckoCode = NS_VK_RIGHT;
+ gdkCode = GDK_Right;
+ break;
+ }
+
+ return ExecuteNativeKeyBindingRemapped(aType, aEvent, aCallback,
+ aCallbackData,
+ geckoCode, gdkCode);
+ }
+ }
+
+ NativeKeyBindings* keyBindings = NativeKeyBindings::GetInstance(aType);
+ return keyBindings->Execute(aEvent, aCallback, aCallbackData);
+}
+
+#if defined(MOZ_X11) && (MOZ_WIDGET_GTK == 2)
+/* static */ already_AddRefed<DrawTarget>
+nsWindow::GetDrawTargetForGdkDrawable(GdkDrawable* aDrawable,
+ const IntSize& aSize)
+{
+ GdkVisual* visual = gdk_drawable_get_visual(aDrawable);
+ Screen* xScreen =
+ gdk_x11_screen_get_xscreen(gdk_drawable_get_screen(aDrawable));
+ Display* xDisplay = DisplayOfScreen(xScreen);
+ Drawable xDrawable = gdk_x11_drawable_get_xid(aDrawable);
+
+ RefPtr<gfxASurface> surface;
+
+ if (visual) {
+ Visual* xVisual = gdk_x11_visual_get_xvisual(visual);
+
+ surface = new gfxXlibSurface(xDisplay, xDrawable, xVisual, aSize);
+ } else {
+ // no visual? we must be using an xrender format. Find a format
+ // for this depth.
+ XRenderPictFormat *pf = nullptr;
+ switch (gdk_drawable_get_depth(aDrawable)) {
+ case 32:
+ pf = XRenderFindStandardFormat(xDisplay, PictStandardARGB32);
+ break;
+ case 24:
+ pf = XRenderFindStandardFormat(xDisplay, PictStandardRGB24);
+ break;
+ default:
+ NS_ERROR("Don't know how to handle the given depth!");
+ break;
+ }
+
+ surface = new gfxXlibSurface(xScreen, xDrawable, pf, aSize);
+ }
+
+ RefPtr<DrawTarget> dt =
+ gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surface, aSize);
+
+ if (!dt || !dt->IsValid()) {
+ return nullptr;
+ }
+
+ return dt.forget();
+}
+#endif
+
+already_AddRefed<DrawTarget>
+nsWindow::StartRemoteDrawingInRegion(LayoutDeviceIntRegion& aInvalidRegion, BufferMode* aBufferMode)
+{
+ return mSurfaceProvider.StartRemoteDrawingInRegion(aInvalidRegion, aBufferMode);
+}
+
+void
+nsWindow::EndRemoteDrawingInRegion(DrawTarget* aDrawTarget,
+ LayoutDeviceIntRegion& aInvalidRegion)
+{
+ mSurfaceProvider.EndRemoteDrawingInRegion(aDrawTarget, aInvalidRegion);
+}
+
+// Code shared begin BeginMoveDrag and BeginResizeDrag
+bool
+nsWindow::GetDragInfo(WidgetMouseEvent* aMouseEvent,
+ GdkWindow** aWindow, gint* aButton,
+ gint* aRootX, gint* aRootY)
+{
+ if (aMouseEvent->button != WidgetMouseEvent::eLeftButton) {
+ // we can only begin a move drag with the left mouse button
+ return false;
+ }
+ *aButton = 1;
+
+ // get the gdk window for this widget
+ GdkWindow* gdk_window = mGdkWindow;
+ if (!gdk_window) {
+ return false;
+ }
+#ifdef DEBUG
+ // GDK_IS_WINDOW(...) expands to a statement-expression, and
+ // statement-expressions are not allowed in template-argument lists. So we
+ // have to make the MOZ_ASSERT condition indirect.
+ if (!GDK_IS_WINDOW(gdk_window)) {
+ MOZ_ASSERT(false, "must really be window");
+ }
+#endif
+
+ // find the top-level window
+ gdk_window = gdk_window_get_toplevel(gdk_window);
+ MOZ_ASSERT(gdk_window,
+ "gdk_window_get_toplevel should not return null");
+ *aWindow = gdk_window;
+
+ if (!aMouseEvent->mWidget) {
+ return false;
+ }
+
+ // FIXME: It would be nice to have the widget position at the time
+ // of the event, but it's relatively unlikely that the widget has
+ // moved since the mousedown. (On the other hand, it's quite likely
+ // that the mouse has moved, which is why we use the mouse position
+ // from the event.)
+ LayoutDeviceIntPoint offset = aMouseEvent->mWidget->WidgetToScreenOffset();
+ *aRootX = aMouseEvent->mRefPoint.x + offset.x;
+ *aRootY = aMouseEvent->mRefPoint.y + offset.y;
+
+ return true;
+}
+
+NS_IMETHODIMP
+nsWindow::BeginMoveDrag(WidgetMouseEvent* aEvent)
+{
+ MOZ_ASSERT(aEvent, "must have event");
+ MOZ_ASSERT(aEvent->mClass == eMouseEventClass,
+ "event must have correct struct type");
+
+ GdkWindow *gdk_window;
+ gint button, screenX, screenY;
+ if (!GetDragInfo(aEvent, &gdk_window, &button, &screenX, &screenY)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // tell the window manager to start the move
+ screenX = DevicePixelsToGdkCoordRoundDown(screenX);
+ screenY = DevicePixelsToGdkCoordRoundDown(screenY);
+ gdk_window_begin_move_drag(gdk_window, button, screenX, screenY,
+ aEvent->mTime);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::BeginResizeDrag(WidgetGUIEvent* aEvent,
+ int32_t aHorizontal,
+ int32_t aVertical)
+{
+ NS_ENSURE_ARG_POINTER(aEvent);
+
+ if (aEvent->mClass != eMouseEventClass) {
+ // you can only begin a resize drag with a mouse event
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ GdkWindow *gdk_window;
+ gint button, screenX, screenY;
+ if (!GetDragInfo(aEvent->AsMouseEvent(), &gdk_window, &button,
+ &screenX, &screenY)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // work out what GdkWindowEdge we're talking about
+ GdkWindowEdge window_edge;
+ if (aVertical < 0) {
+ if (aHorizontal < 0) {
+ window_edge = GDK_WINDOW_EDGE_NORTH_WEST;
+ } else if (aHorizontal == 0) {
+ window_edge = GDK_WINDOW_EDGE_NORTH;
+ } else {
+ window_edge = GDK_WINDOW_EDGE_NORTH_EAST;
+ }
+ } else if (aVertical == 0) {
+ if (aHorizontal < 0) {
+ window_edge = GDK_WINDOW_EDGE_WEST;
+ } else if (aHorizontal == 0) {
+ return NS_ERROR_INVALID_ARG;
+ } else {
+ window_edge = GDK_WINDOW_EDGE_EAST;
+ }
+ } else {
+ if (aHorizontal < 0) {
+ window_edge = GDK_WINDOW_EDGE_SOUTH_WEST;
+ } else if (aHorizontal == 0) {
+ window_edge = GDK_WINDOW_EDGE_SOUTH;
+ } else {
+ window_edge = GDK_WINDOW_EDGE_SOUTH_EAST;
+ }
+ }
+
+ // tell the window manager to start the resize
+ gdk_window_begin_resize_drag(gdk_window, window_edge, button,
+ screenX, screenY, aEvent->mTime);
+
+ return NS_OK;
+}
+
+nsIWidget::LayerManager*
+nsWindow::GetLayerManager(PLayerTransactionChild* aShadowManager,
+ LayersBackend aBackendHint,
+ LayerManagerPersistence aPersistence)
+{
+ if (mIsDestroyed) {
+ // Prevent external code from triggering the re-creation of the LayerManager/Compositor
+ // during shutdown. Just return what we currently have, which is most likely null.
+ return mLayerManager;
+ }
+ if (!mLayerManager && eTransparencyTransparent == GetTransparencyMode()) {
+ mLayerManager = CreateBasicLayerManager();
+ }
+
+ return nsBaseWidget::GetLayerManager(aShadowManager, aBackendHint, aPersistence);
+}
+
+void
+nsWindow::ClearCachedResources()
+{
+ if (mLayerManager &&
+ mLayerManager->GetBackendType() == mozilla::layers::LayersBackend::LAYERS_BASIC) {
+ mLayerManager->ClearCachedResources();
+ }
+
+ GList* children = gdk_window_peek_children(mGdkWindow);
+ for (GList* list = children; list; list = list->next) {
+ nsWindow* window = get_window_for_gdk_window(GDK_WINDOW(list->data));
+ if (window) {
+ window->ClearCachedResources();
+ }
+ }
+}
+
+gint
+nsWindow::GdkScaleFactor()
+{
+#if (MOZ_WIDGET_GTK >= 3)
+ // Available as of GTK 3.10+
+ static auto sGdkWindowGetScaleFactorPtr = (gint (*)(GdkWindow*))
+ dlsym(RTLD_DEFAULT, "gdk_window_get_scale_factor");
+ if (sGdkWindowGetScaleFactorPtr && mGdkWindow)
+ return (*sGdkWindowGetScaleFactorPtr)(mGdkWindow);
+#endif
+ return nsScreenGtk::GetGtkMonitorScaleFactor();
+}
+
+
+gint
+nsWindow::DevicePixelsToGdkCoordRoundUp(int pixels) {
+ gint scale = GdkScaleFactor();
+ return (pixels + scale - 1) / scale;
+}
+
+gint
+nsWindow::DevicePixelsToGdkCoordRoundDown(int pixels) {
+ gint scale = GdkScaleFactor();
+ return pixels / scale;
+}
+
+GdkPoint
+nsWindow::DevicePixelsToGdkPointRoundDown(LayoutDeviceIntPoint point) {
+ gint scale = GdkScaleFactor();
+ return { point.x / scale, point.y / scale };
+}
+
+GdkRectangle
+nsWindow::DevicePixelsToGdkRectRoundOut(LayoutDeviceIntRect rect) {
+ gint scale = GdkScaleFactor();
+ int x = rect.x / scale;
+ int y = rect.y / scale;
+ int right = (rect.x + rect.width + scale - 1) / scale;
+ int bottom = (rect.y + rect.height + scale - 1) / scale;
+ return { x, y, right - x, bottom - y };
+}
+
+GdkRectangle
+nsWindow::DevicePixelsToGdkSizeRoundUp(LayoutDeviceIntSize pixelSize) {
+ gint scale = GdkScaleFactor();
+ gint width = (pixelSize.width + scale - 1) / scale;
+ gint height = (pixelSize.height + scale - 1) / scale;
+ return { 0, 0, width, height };
+}
+
+int
+nsWindow::GdkCoordToDevicePixels(gint coord) {
+ return coord * GdkScaleFactor();
+}
+
+LayoutDeviceIntPoint
+nsWindow::GdkEventCoordsToDevicePixels(gdouble x, gdouble y)
+{
+ gint scale = GdkScaleFactor();
+ return LayoutDeviceIntPoint::Round(x * scale, y * scale);
+}
+
+LayoutDeviceIntPoint
+nsWindow::GdkPointToDevicePixels(GdkPoint point) {
+ gint scale = GdkScaleFactor();
+ return LayoutDeviceIntPoint(point.x * scale,
+ point.y * scale);
+}
+
+LayoutDeviceIntRect
+nsWindow::GdkRectToDevicePixels(GdkRectangle rect) {
+ gint scale = GdkScaleFactor();
+ return LayoutDeviceIntRect(rect.x * scale,
+ rect.y * scale,
+ rect.width * scale,
+ rect.height * scale);
+}
+
+nsresult
+nsWindow::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
+ uint32_t aNativeMessage,
+ uint32_t aModifierFlags,
+ nsIObserver* aObserver)
+{
+ AutoObserverNotifier notifier(aObserver, "mouseevent");
+
+ if (!mGdkWindow) {
+ return NS_OK;
+ }
+
+ GdkDisplay* display = gdk_window_get_display(mGdkWindow);
+
+ // When a button-press/release event is requested, create it here and put it in the
+ // event queue. This will not emit a motion event - this needs to be done
+ // explicitly *before* requesting a button-press/release. You will also need to wait
+ // for the motion event to be dispatched before requesting a button-press/release
+ // event to maintain the desired event order.
+ if (aNativeMessage == GDK_BUTTON_PRESS || aNativeMessage == GDK_BUTTON_RELEASE) {
+ GdkEvent event;
+ memset(&event, 0, sizeof(GdkEvent));
+ event.type = (GdkEventType)aNativeMessage;
+ event.button.button = 1;
+ event.button.window = mGdkWindow;
+ event.button.time = GDK_CURRENT_TIME;
+
+#if (MOZ_WIDGET_GTK == 3)
+ // Get device for event source
+ GdkDeviceManager *device_manager = gdk_display_get_device_manager(display);
+ event.button.device = gdk_device_manager_get_client_pointer(device_manager);
+#endif
+
+ event.button.x_root = DevicePixelsToGdkCoordRoundDown(aPoint.x);
+ event.button.y_root = DevicePixelsToGdkCoordRoundDown(aPoint.y);
+
+ LayoutDeviceIntPoint pointInWindow = aPoint - WidgetToScreenOffset();
+ event.button.x = DevicePixelsToGdkCoordRoundDown(pointInWindow.x);
+ event.button.y = DevicePixelsToGdkCoordRoundDown(pointInWindow.y);
+
+ gdk_event_put(&event);
+ } else {
+ // We don't support specific events other than button-press/release. In all
+ // other cases we'll synthesize a motion event that will be emitted by
+ // gdk_display_warp_pointer().
+ GdkScreen* screen = gdk_window_get_screen(mGdkWindow);
+ GdkPoint point = DevicePixelsToGdkPointRoundDown(aPoint);
+ gdk_display_warp_pointer(display, screen, point.x, point.y);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsWindow::SynthesizeNativeMouseScrollEvent(mozilla::LayoutDeviceIntPoint aPoint,
+ uint32_t aNativeMessage,
+ double aDeltaX,
+ double aDeltaY,
+ double aDeltaZ,
+ uint32_t aModifierFlags,
+ uint32_t aAdditionalFlags,
+ nsIObserver* aObserver)
+{
+ AutoObserverNotifier notifier(aObserver, "mousescrollevent");
+
+ if (!mGdkWindow) {
+ return NS_OK;
+ }
+
+ GdkEvent event;
+ memset(&event, 0, sizeof(GdkEvent));
+ event.type = GDK_SCROLL;
+ event.scroll.window = mGdkWindow;
+ event.scroll.time = GDK_CURRENT_TIME;
+#if (MOZ_WIDGET_GTK == 3)
+ // Get device for event source
+ GdkDisplay* display = gdk_window_get_display(mGdkWindow);
+ GdkDeviceManager *device_manager = gdk_display_get_device_manager(display);
+ event.scroll.device = gdk_device_manager_get_client_pointer(device_manager);
+#endif
+ event.scroll.x_root = DevicePixelsToGdkCoordRoundDown(aPoint.x);
+ event.scroll.y_root = DevicePixelsToGdkCoordRoundDown(aPoint.y);
+
+ LayoutDeviceIntPoint pointInWindow = aPoint - WidgetToScreenOffset();
+ event.scroll.x = DevicePixelsToGdkCoordRoundDown(pointInWindow.x);
+ event.scroll.y = DevicePixelsToGdkCoordRoundDown(pointInWindow.y);
+
+ // The delta values are backwards on Linux compared to Windows and Cocoa,
+ // hence the negation.
+#if GTK_CHECK_VERSION(3,4,0)
+ // TODO: is this correct? I don't have GTK 3.4+ so I can't check
+ event.scroll.direction = GDK_SCROLL_SMOOTH;
+ event.scroll.delta_x = -aDeltaX;
+ event.scroll.delta_y = -aDeltaY;
+#else
+ if (aDeltaX < 0) {
+ event.scroll.direction = GDK_SCROLL_RIGHT;
+ } else if (aDeltaX > 0) {
+ event.scroll.direction = GDK_SCROLL_LEFT;
+ } else if (aDeltaY < 0) {
+ event.scroll.direction = GDK_SCROLL_DOWN;
+ } else if (aDeltaY > 0) {
+ event.scroll.direction = GDK_SCROLL_UP;
+ } else {
+ return NS_OK;
+ }
+#endif
+
+ gdk_event_put(&event);
+
+ return NS_OK;
+}
+
+#if GTK_CHECK_VERSION(3,4,0)
+nsresult
+nsWindow::SynthesizeNativeTouchPoint(uint32_t aPointerId,
+ TouchPointerState aPointerState,
+ LayoutDeviceIntPoint aPoint,
+ double aPointerPressure,
+ uint32_t aPointerOrientation,
+ nsIObserver* aObserver)
+{
+ AutoObserverNotifier notifier(aObserver, "touchpoint");
+
+ if (!mGdkWindow) {
+ return NS_OK;
+ }
+
+ GdkEvent event;
+ memset(&event, 0, sizeof(GdkEvent));
+
+ static std::map<uint32_t, GdkEventSequence*> sKnownPointers;
+
+ auto result = sKnownPointers.find(aPointerId);
+ switch (aPointerState) {
+ case TOUCH_CONTACT:
+ if (result == sKnownPointers.end()) {
+ // GdkEventSequence isn't a thing we can instantiate, and never gets
+ // dereferenced in the gtk code. It's an opaque pointer, the only
+ // requirement is that it be distinct from other instances of
+ // GdkEventSequence*.
+ event.touch.sequence = (GdkEventSequence*)((uintptr_t)aPointerId);
+ sKnownPointers[aPointerId] = event.touch.sequence;
+ event.type = GDK_TOUCH_BEGIN;
+ } else {
+ event.touch.sequence = result->second;
+ event.type = GDK_TOUCH_UPDATE;
+ }
+ break;
+ case TOUCH_REMOVE:
+ event.type = GDK_TOUCH_END;
+ if (result == sKnownPointers.end()) {
+ NS_WARNING("Tried to synthesize touch-end for unknown pointer!");
+ return NS_ERROR_UNEXPECTED;
+ }
+ event.touch.sequence = result->second;
+ sKnownPointers.erase(result);
+ break;
+ case TOUCH_CANCEL:
+ event.type = GDK_TOUCH_CANCEL;
+ if (result == sKnownPointers.end()) {
+ NS_WARNING("Tried to synthesize touch-cancel for unknown pointer!");
+ return NS_ERROR_UNEXPECTED;
+ }
+ event.touch.sequence = result->second;
+ sKnownPointers.erase(result);
+ break;
+ case TOUCH_HOVER:
+ default:
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ event.touch.window = mGdkWindow;
+ event.touch.time = GDK_CURRENT_TIME;
+
+ GdkDisplay* display = gdk_window_get_display(mGdkWindow);
+ GdkDeviceManager* device_manager = gdk_display_get_device_manager(display);
+ event.touch.device = gdk_device_manager_get_client_pointer(device_manager);
+
+ event.touch.x_root = DevicePixelsToGdkCoordRoundDown(aPoint.x);
+ event.touch.y_root = DevicePixelsToGdkCoordRoundDown(aPoint.y);
+
+ LayoutDeviceIntPoint pointInWindow = aPoint - WidgetToScreenOffset();
+ event.touch.x = DevicePixelsToGdkCoordRoundDown(pointInWindow.x);
+ event.touch.y = DevicePixelsToGdkCoordRoundDown(pointInWindow.y);
+
+ gdk_event_put(&event);
+
+ return NS_OK;
+}
+#endif
+
+int32_t
+nsWindow::RoundsWidgetCoordinatesTo()
+{
+ return GdkScaleFactor();
+}
+
+void nsWindow::GetCompositorWidgetInitData(mozilla::widget::CompositorWidgetInitData* aInitData)
+{
+ #ifdef MOZ_X11
+ *aInitData = mozilla::widget::CompositorWidgetInitData(
+ mXWindow,
+ nsCString(XDisplayString(mXDisplay)),
+ GetClientSize());
+ #endif
+}