widget/gtk/nsWindow.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* vim:expandtab:shiftwidth=4:tabstop=4:
     3  */
     4 /* This Source Code Form is subject to the terms of the Mozilla Public
     5  * License, v. 2.0. If a copy of the MPL was not distributed with this
     6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     8 #include "mozilla/ArrayUtils.h"
     9 #include "mozilla/MiscEvents.h"
    10 #include "mozilla/MouseEvents.h"
    11 #include "mozilla/TextEvents.h"
    12 #include <algorithm>
    14 #include "prlink.h"
    15 #include "nsGTKToolkit.h"
    16 #include "nsIRollupListener.h"
    17 #include "nsIDOMNode.h"
    19 #include "nsWidgetsCID.h"
    20 #include "nsDragService.h"
    21 #include "nsIWidgetListener.h"
    23 #include "nsGtkKeyUtils.h"
    24 #include "nsGtkCursors.h"
    26 #include <gtk/gtk.h>
    27 #if (MOZ_WIDGET_GTK == 3)
    28 #include <gtk/gtkx.h>
    29 #endif
    30 #ifdef MOZ_X11
    31 #include <gdk/gdkx.h>
    32 #include <X11/Xatom.h>
    33 #include <X11/extensions/XShm.h>
    34 #include <X11/extensions/shape.h>
    35 #if (MOZ_WIDGET_GTK == 3)
    36 #include <gdk/gdkkeysyms-compat.h>
    37 #endif
    39 #ifdef AIX
    40 #include <X11/keysym.h>
    41 #else
    42 #include <X11/XF86keysym.h>
    43 #endif
    45 #if (MOZ_WIDGET_GTK == 2)
    46 #include "gtk2xtbin.h"
    47 #endif
    48 #endif /* MOZ_X11 */
    49 #include <gdk/gdkkeysyms.h>
    50 #if (MOZ_WIDGET_GTK == 2)
    51 #include <gtk/gtkprivate.h>
    52 #endif
    54 #include "nsGkAtoms.h"
    56 #ifdef MOZ_ENABLE_STARTUP_NOTIFICATION
    57 #define SN_API_NOT_YET_FROZEN
    58 #include <startup-notification-1.0/libsn/sn.h>
    59 #endif
    61 #include "mozilla/Likely.h"
    62 #include "mozilla/Preferences.h"
    63 #include "nsIPrefService.h"
    64 #include "nsIGConfService.h"
    65 #include "nsIServiceManager.h"
    66 #include "nsIStringBundle.h"
    67 #include "nsGfxCIID.h"
    68 #include "nsGtkUtils.h"
    69 #include "nsIObserverService.h"
    70 #include "mozilla/layers/LayersTypes.h"
    71 #include "nsIIdleServiceInternal.h"
    72 #include "nsIPropertyBag2.h"
    73 #include "GLContext.h"
    74 #include "gfx2DGlue.h"
    76 #ifdef ACCESSIBILITY
    77 #include "mozilla/a11y/Accessible.h"
    78 #include "mozilla/a11y/Platform.h"
    79 #include "nsAccessibilityService.h"
    81 using namespace mozilla;
    82 using namespace mozilla::widget;
    83 #endif
    85 /* For SetIcon */
    86 #include "nsAppDirectoryServiceDefs.h"
    87 #include "nsXPIDLString.h"
    88 #include "nsIFile.h"
    90 /* SetCursor(imgIContainer*) */
    91 #include <gdk/gdk.h>
    92 #include <wchar.h>
    93 #include "imgIContainer.h"
    94 #include "nsGfxCIID.h"
    95 #include "nsImageToPixbuf.h"
    96 #include "nsIInterfaceRequestorUtils.h"
    97 #include "nsAutoPtr.h"
    98 #include "ClientLayerManager.h"
   100 extern "C" {
   101 #define PIXMAN_DONT_DEFINE_STDINT
   102 #include "pixman.h"
   103 }
   104 #include "gfxPlatformGtk.h"
   105 #include "gfxContext.h"
   106 #include "gfxImageSurface.h"
   107 #include "gfxUtils.h"
   108 #include "Layers.h"
   109 #include "GLContextProvider.h"
   110 #include "mozilla/gfx/2D.h"
   111 #include "mozilla/layers/CompositorParent.h"
   113 #ifdef MOZ_X11
   114 #include "gfxXlibSurface.h"
   115 #include "cairo-xlib.h"
   116 #endif
   118 #include "nsShmImage.h"
   120 #include "nsIDOMWheelEvent.h"
   122 #include "NativeKeyBindings.h"
   123 #include "nsWindow.h"
   125 using namespace mozilla;
   126 using namespace mozilla::gfx;
   127 using namespace mozilla::widget;
   128 using namespace mozilla::layers;
   129 using mozilla::gl::GLContext;
   131 // Don't put more than this many rects in the dirty region, just fluff
   132 // out to the bounding-box if there are more
   133 #define MAX_RECTS_IN_REGION 100
   135 const gint kEvents = GDK_EXPOSURE_MASK | GDK_STRUCTURE_MASK |
   136                      GDK_VISIBILITY_NOTIFY_MASK |
   137                      GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
   138                      GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
   139                      GDK_SCROLL_MASK |
   140                      GDK_POINTER_MOTION_MASK;
   142 /* utility functions */
   143 static bool       is_mouse_in_window(GdkWindow* aWindow,
   144                                      gdouble aMouseX, gdouble aMouseY);
   145 static nsWindow  *get_window_for_gtk_widget(GtkWidget *widget);
   146 static nsWindow  *get_window_for_gdk_window(GdkWindow *window);
   147 static GtkWidget *get_gtk_widget_for_gdk_window(GdkWindow *window);
   148 static GdkCursor *get_gtk_cursor(nsCursor aCursor);
   150 static GdkWindow *get_inner_gdk_window (GdkWindow *aWindow,
   151                                         gint x, gint y,
   152                                         gint *retx, gint *rety);
   154 static inline bool is_context_menu_key(const WidgetKeyboardEvent& inKeyEvent);
   156 static int    is_parent_ungrab_enter(GdkEventCrossing *aEvent);
   157 static int    is_parent_grab_leave(GdkEventCrossing *aEvent);
   159 static void GetBrandName(nsXPIDLString& brandName);
   161 /* callbacks from widgets */
   162 #if (MOZ_WIDGET_GTK == 2)
   163 static gboolean expose_event_cb           (GtkWidget *widget,
   164                                            GdkEventExpose *event);
   165 #else
   166 static gboolean expose_event_cb           (GtkWidget *widget,
   167                                            cairo_t *rect);
   168 #endif
   169 static gboolean configure_event_cb        (GtkWidget *widget,
   170                                            GdkEventConfigure *event);
   171 static void     container_unrealize_cb    (GtkWidget *widget);
   172 static void     size_allocate_cb          (GtkWidget *widget,
   173                                            GtkAllocation *allocation);
   174 static gboolean delete_event_cb           (GtkWidget *widget,
   175                                            GdkEventAny *event);
   176 static gboolean enter_notify_event_cb     (GtkWidget *widget,
   177                                            GdkEventCrossing *event);
   178 static gboolean leave_notify_event_cb     (GtkWidget *widget,
   179                                            GdkEventCrossing *event);
   180 static gboolean motion_notify_event_cb    (GtkWidget *widget,
   181                                            GdkEventMotion *event);
   182 static gboolean button_press_event_cb     (GtkWidget *widget,
   183                                            GdkEventButton *event);
   184 static gboolean button_release_event_cb   (GtkWidget *widget,
   185                                            GdkEventButton *event);
   186 static gboolean focus_in_event_cb         (GtkWidget *widget,
   187                                            GdkEventFocus *event);
   188 static gboolean focus_out_event_cb        (GtkWidget *widget,
   189                                            GdkEventFocus *event);
   190 static gboolean key_press_event_cb        (GtkWidget *widget,
   191                                            GdkEventKey *event);
   192 static gboolean key_release_event_cb      (GtkWidget *widget,
   193                                            GdkEventKey *event);
   194 static gboolean scroll_event_cb           (GtkWidget *widget,
   195                                            GdkEventScroll *event);
   196 static gboolean visibility_notify_event_cb(GtkWidget *widget,
   197                                            GdkEventVisibility *event);
   198 static void     hierarchy_changed_cb      (GtkWidget *widget,
   199                                            GtkWidget *previous_toplevel);
   200 static gboolean window_state_event_cb     (GtkWidget *widget,
   201                                            GdkEventWindowState *event);
   202 static void     theme_changed_cb          (GtkSettings *settings,
   203                                            GParamSpec *pspec,
   204                                            nsWindow *data);
   205 static nsWindow* GetFirstNSWindowForGDKWindow (GdkWindow *aGdkWindow);
   207 #ifdef __cplusplus
   208 extern "C" {
   209 #endif /* __cplusplus */
   210 #ifdef MOZ_X11
   211 static GdkFilterReturn popup_take_focus_filter (GdkXEvent *gdk_xevent,
   212                                                 GdkEvent *event,
   213                                                 gpointer data);
   214 static GdkFilterReturn plugin_window_filter_func (GdkXEvent *gdk_xevent,
   215                                                   GdkEvent *event,
   216                                                   gpointer data);
   217 static GdkFilterReturn plugin_client_message_filter (GdkXEvent *xevent,
   218                                                      GdkEvent *event,
   219                                                      gpointer data);
   220 #endif /* MOZ_X11 */
   221 #ifdef __cplusplus
   222 }
   223 #endif /* __cplusplus */
   225 static gboolean drag_motion_event_cb      (GtkWidget *aWidget,
   226                                            GdkDragContext *aDragContext,
   227                                            gint aX,
   228                                            gint aY,
   229                                            guint aTime,
   230                                            gpointer aData);
   231 static void     drag_leave_event_cb       (GtkWidget *aWidget,
   232                                            GdkDragContext *aDragContext,
   233                                            guint aTime,
   234                                            gpointer aData);
   235 static gboolean drag_drop_event_cb        (GtkWidget *aWidget,
   236                                            GdkDragContext *aDragContext,
   237                                            gint aX,
   238                                            gint aY,
   239                                            guint aTime,
   240                                            gpointer aData);
   241 static void    drag_data_received_event_cb(GtkWidget *aWidget,
   242                                            GdkDragContext *aDragContext,
   243                                            gint aX,
   244                                            gint aY,
   245                                            GtkSelectionData  *aSelectionData,
   246                                            guint aInfo,
   247                                            guint32 aTime,
   248                                            gpointer aData);
   250 /* initialization static functions */
   251 static nsresult    initialize_prefs        (void);
   253 static guint32 sLastUserInputTime = GDK_CURRENT_TIME;
   254 static guint32 sRetryGrabTime;
   256 static NS_DEFINE_IID(kCDragServiceCID,  NS_DRAGSERVICE_CID);
   258 // The window from which the focus manager asks us to dispatch key events.
   259 static nsWindow         *gFocusWindow          = nullptr;
   260 static bool              gBlockActivateEvent   = false;
   261 static bool              gGlobalsInitialized   = false;
   262 static bool              gRaiseWindows         = true;
   263 static nsWindow         *gPluginFocusWindow    = nullptr;
   266 #define NS_WINDOW_TITLE_MAX_LENGTH 4095
   268 // If after selecting profile window, the startup fail, please refer to
   269 // http://bugzilla.gnome.org/show_bug.cgi?id=88940
   271 // needed for imgIContainer cursors
   272 // GdkDisplay* was added in 2.2
   273 typedef struct _GdkDisplay GdkDisplay;
   275 #define kWindowPositionSlop 20
   277 // cursor cache
   278 static GdkCursor *gCursorCache[eCursorCount];
   280 static GtkWidget *gInvisibleContainer = nullptr;
   282 // Sometimes this actually also includes the state of the modifier keys, but
   283 // only the button state bits are used.
   284 static guint gButtonState;
   286 // nsAutoRef<pixman_region32> uses nsSimpleRef<> to know how to automatically
   287 // destroy regions.
   288 template <>
   289 class nsSimpleRef<pixman_region32> : public pixman_region32 {
   290 protected:
   291     typedef pixman_region32 RawRef;
   293     nsSimpleRef() { data = nullptr; }
   294     nsSimpleRef(const RawRef &aRawRef) : pixman_region32(aRawRef) { }
   296     static void Release(pixman_region32& region) {
   297         pixman_region32_fini(&region);
   298     }
   299     // Whether this needs to be released:
   300     bool HaveResource() const { return data != nullptr; }
   302     pixman_region32& get() { return *this; }
   303 };
   305 static inline int32_t
   306 GetBitmapStride(int32_t width)
   307 {
   308 #if defined(MOZ_X11) || (MOZ_WIDGET_GTK == 2)
   309   return (width+7)/8;
   310 #else
   311   return cairo_format_stride_for_width(CAIRO_FORMAT_A1, width);
   312 #endif
   313 }
   315 static inline bool TimestampIsNewerThan(guint32 a, guint32 b)
   316 {
   317     // Timestamps are just the least significant bits of a monotonically
   318     // increasing function, and so the use of unsigned overflow arithmetic.
   319     return a - b <= G_MAXUINT32/2;
   320 }
   322 static void
   323 UpdateLastInputEventTime(void *aGdkEvent)
   324 {
   325     nsCOMPtr<nsIIdleServiceInternal> idleService =
   326         do_GetService("@mozilla.org/widget/idleservice;1");
   327     if (idleService) {
   328         idleService->ResetIdleTimeOut(0);
   329     }
   331     guint timestamp = gdk_event_get_time(static_cast<GdkEvent*>(aGdkEvent));
   332     if (timestamp == GDK_CURRENT_TIME)
   333         return;
   335     sLastUserInputTime = timestamp;
   336 }
   338 nsWindow::nsWindow()
   339 {
   340     mIsTopLevel       = false;
   341     mIsDestroyed      = false;
   342     mNeedsResize      = false;
   343     mNeedsMove        = false;
   344     mListenForResizes = false;
   345     mIsShown          = false;
   346     mNeedsShow        = false;
   347     mEnabled          = true;
   348     mCreated          = false;
   350     mContainer           = nullptr;
   351     mGdkWindow           = nullptr;
   352     mShell               = nullptr;
   353     mHasMappedToplevel   = false;
   354     mIsFullyObscured     = false;
   355     mRetryPointerGrab    = false;
   356     mWindowType          = eWindowType_child;
   357     mSizeState           = nsSizeMode_Normal;
   358     mLastSizeMode        = nsSizeMode_Normal;
   359     mSizeConstraints.mMaxSize = GetSafeWindowSize(mSizeConstraints.mMaxSize);
   361 #ifdef MOZ_X11
   362     mOldFocusWindow      = 0;
   363 #endif /* MOZ_X11 */
   364     mPluginType          = PluginType_NONE;
   366     if (!gGlobalsInitialized) {
   367         gGlobalsInitialized = true;
   369         // It's OK if either of these fail, but it may not be one day.
   370         initialize_prefs();
   371     }
   373     mLastMotionPressure = 0;
   375 #ifdef ACCESSIBILITY
   376     mRootAccessible  = nullptr;
   377 #endif
   379     mIsTransparent = false;
   380     mTransparencyBitmap = nullptr;
   382     mTransparencyBitmapWidth  = 0;
   383     mTransparencyBitmapHeight = 0;
   384 }
   386 nsWindow::~nsWindow()
   387 {
   388     LOG(("nsWindow::~nsWindow() [%p]\n", (void *)this));
   390     delete[] mTransparencyBitmap;
   391     mTransparencyBitmap = nullptr;
   393     Destroy();
   394 }
   396 /* static */ void
   397 nsWindow::ReleaseGlobals()
   398 {
   399   for (uint32_t i = 0; i < ArrayLength(gCursorCache); ++i) {
   400     if (gCursorCache[i]) {
   401       gdk_cursor_unref(gCursorCache[i]);
   402       gCursorCache[i] = nullptr;
   403     }
   404   }
   405 }
   407 NS_IMPL_ISUPPORTS_INHERITED(nsWindow, nsBaseWidget,
   408                             nsISupportsWeakReference)
   410 void
   411 nsWindow::CommonCreate(nsIWidget *aParent, bool aListenForResizes)
   412 {
   413     mParent = aParent;
   414     mListenForResizes = aListenForResizes;
   415     mCreated = true;
   416 }
   418 void
   419 nsWindow::DispatchActivateEvent(void)
   420 {
   421     NS_ASSERTION(mContainer || mIsDestroyed,
   422                  "DispatchActivateEvent only intended for container windows");
   424 #ifdef ACCESSIBILITY
   425     DispatchActivateEventAccessible();
   426 #endif //ACCESSIBILITY
   428     if (mWidgetListener)
   429       mWidgetListener->WindowActivated();
   430 }
   432 void
   433 nsWindow::DispatchDeactivateEvent(void)
   434 {
   435     if (mWidgetListener)
   436       mWidgetListener->WindowDeactivated();
   438 #ifdef ACCESSIBILITY
   439     DispatchDeactivateEventAccessible();
   440 #endif //ACCESSIBILITY
   441 }
   443 void
   444 nsWindow::DispatchResized(int32_t aWidth, int32_t aHeight)
   445 {
   446     nsIWidgetListener *listeners[] =
   447         { mWidgetListener, mAttachedWidgetListener };
   448     for (size_t i = 0; i < ArrayLength(listeners); ++i) {
   449         if (listeners[i]) {
   450             listeners[i]->WindowResized(this, aWidth, aHeight);
   451         }
   452     }
   453 }
   455 nsresult
   456 nsWindow::DispatchEvent(WidgetGUIEvent* aEvent, nsEventStatus& aStatus)
   457 {
   458 #ifdef DEBUG
   459     debug_DumpEvent(stdout, aEvent->widget, aEvent,
   460                     nsAutoCString("something"), 0);
   461 #endif
   463     aStatus = nsEventStatus_eIgnore;
   464     nsIWidgetListener* listener =
   465         mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener;
   466     if (listener) {
   467       aStatus = listener->HandleEvent(aEvent, mUseAttachedEvents);
   468     }
   470     return NS_OK;
   471 }
   473 void
   474 nsWindow::OnDestroy(void)
   475 {
   476     if (mOnDestroyCalled)
   477         return;
   479     mOnDestroyCalled = true;
   481     // Prevent deletion.
   482     nsCOMPtr<nsIWidget> kungFuDeathGrip = this;
   484     // release references to children, device context, toolkit + app shell
   485     nsBaseWidget::OnDestroy(); 
   487     // Remove association between this object and its parent and siblings.
   488     nsBaseWidget::Destroy();
   489     mParent = nullptr;
   491     NotifyWindowDestroyed();
   492 }
   494 bool
   495 nsWindow::AreBoundsSane(void)
   496 {
   497     if (mBounds.width > 0 && mBounds.height > 0)
   498         return true;
   500     return false;
   501 }
   503 static GtkWidget*
   504 EnsureInvisibleContainer()
   505 {
   506     if (!gInvisibleContainer) {
   507         // GtkWidgets need to be anchored to a GtkWindow to be realized (to
   508         // have a window).  Using GTK_WINDOW_POPUP rather than
   509         // GTK_WINDOW_TOPLEVEL in the hope that POPUP results in less
   510         // initialization and window manager interaction.
   511         GtkWidget* window = gtk_window_new(GTK_WINDOW_POPUP);
   512         gInvisibleContainer = moz_container_new();
   513         gtk_container_add(GTK_CONTAINER(window), gInvisibleContainer);
   514         gtk_widget_realize(gInvisibleContainer);
   516     }
   517     return gInvisibleContainer;
   518 }
   520 static void
   521 CheckDestroyInvisibleContainer()
   522 {
   523     NS_PRECONDITION(gInvisibleContainer, "oh, no");
   525     if (!gdk_window_peek_children(gtk_widget_get_window(gInvisibleContainer))) {
   526         // No children, so not in use.
   527         // Make sure to destroy the GtkWindow also.
   528         gtk_widget_destroy(gtk_widget_get_parent(gInvisibleContainer));
   529         gInvisibleContainer = nullptr;
   530     }
   531 }
   533 // Change the containing GtkWidget on a sub-hierarchy of GdkWindows belonging
   534 // to aOldWidget and rooted at aWindow, and reparent any child GtkWidgets of
   535 // the GdkWindow hierarchy to aNewWidget.
   536 static void
   537 SetWidgetForHierarchy(GdkWindow *aWindow,
   538                       GtkWidget *aOldWidget,
   539                       GtkWidget *aNewWidget)
   540 {
   541     gpointer data;
   542     gdk_window_get_user_data(aWindow, &data);
   544     if (data != aOldWidget) {
   545         if (!GTK_IS_WIDGET(data))
   546             return;
   548         GtkWidget* widget = static_cast<GtkWidget*>(data);
   549         if (gtk_widget_get_parent(widget) != aOldWidget)
   550             return;
   552         // This window belongs to a child widget, which will no longer be a
   553         // child of aOldWidget.
   554         gtk_widget_reparent(widget, aNewWidget);
   556         return;
   557     }
   559     GList *children = gdk_window_get_children(aWindow);
   560     for(GList *list = children; list; list = list->next) {
   561         SetWidgetForHierarchy(GDK_WINDOW(list->data), aOldWidget, aNewWidget);
   562     }
   563     g_list_free(children);
   565     gdk_window_set_user_data(aWindow, aNewWidget);
   566 }
   568 // Walk the list of child windows and call destroy on them.
   569 void
   570 nsWindow::DestroyChildWindows()
   571 {
   572     if (!mGdkWindow)
   573         return;
   575     while (GList *children = gdk_window_peek_children(mGdkWindow)) {
   576         GdkWindow *child = GDK_WINDOW(children->data);
   577         nsWindow *kid = get_window_for_gdk_window(child);
   578         if (kid) {
   579             kid->Destroy();
   580         } else {
   581             // This child is not an nsWindow.
   582             // Destroy the child GtkWidget.
   583             gpointer data;
   584             gdk_window_get_user_data(child, &data);
   585             if (GTK_IS_WIDGET(data)) {
   586                 gtk_widget_destroy(static_cast<GtkWidget*>(data));
   587             }
   588         }
   589     }
   590 }
   592 NS_IMETHODIMP
   593 nsWindow::Destroy(void)
   594 {
   595     if (mIsDestroyed || !mCreated)
   596         return NS_OK;
   598     LOG(("nsWindow::Destroy [%p]\n", (void *)this));
   599     mIsDestroyed = true;
   600     mCreated = false;
   602     /** Need to clean our LayerManager up while still alive */
   603     if (mLayerManager) {
   604         mLayerManager->Destroy();
   605     }
   606     mLayerManager = nullptr;
   608     // It is safe to call DestroyeCompositor several times (here and 
   609     // in the parent class) since it will take effect only once.
   610     // The reason we call it here is because on gtk platforms we need 
   611     // to destroy the compositor before we destroy the gdk window (which
   612     // destroys the the gl context attached to it).
   613     DestroyCompositor();
   615     ClearCachedResources();
   617     g_signal_handlers_disconnect_by_func(gtk_settings_get_default(),
   618                                          FuncToGpointer(theme_changed_cb),
   619                                          this);
   621     nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
   622     if (rollupListener) {
   623         nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget();
   624         if (static_cast<nsIWidget *>(this) == rollupWidget) {
   625             rollupListener->Rollup(0, nullptr, nullptr);
   626         }
   627     }
   629     // dragService will be null after shutdown of the service manager.
   630     nsDragService *dragService = nsDragService::GetInstance();
   631     if (dragService && this == dragService->GetMostRecentDestWindow()) {
   632         dragService->ScheduleLeaveEvent();
   633     }
   635     NativeShow(false);
   637     if (mIMModule) {
   638         mIMModule->OnDestroyWindow(this);
   639     }
   641     // make sure that we remove ourself as the focus window
   642     if (gFocusWindow == this) {
   643         LOGFOCUS(("automatically losing focus...\n"));
   644         gFocusWindow = nullptr;
   645     }
   647 #if (MOZ_WIDGET_GTK == 2) && defined(MOZ_X11)
   648     // make sure that we remove ourself as the plugin focus window
   649     if (gPluginFocusWindow == this) {
   650         gPluginFocusWindow->LoseNonXEmbedPluginFocus();
   651     }
   652 #endif /* MOZ_X11 && MOZ_WIDGET_GTK2 */
   654     // Destroy thebes surface now. Badness can happen if we destroy
   655     // the surface after its X Window.
   656     mThebesSurface = nullptr;
   658     GtkWidget *owningWidget = GetMozContainerWidget();
   659     if (mShell) {
   660         gtk_widget_destroy(mShell);
   661         mShell = nullptr;
   662         mContainer = nullptr;
   663         NS_ABORT_IF_FALSE(!mGdkWindow,
   664                           "mGdkWindow should be NULL when mContainer is destroyed");
   665     }
   666     else if (mContainer) {
   667         gtk_widget_destroy(GTK_WIDGET(mContainer));
   668         mContainer = nullptr;
   669         NS_ABORT_IF_FALSE(!mGdkWindow,
   670                           "mGdkWindow should be NULL when mContainer is destroyed");
   671     }
   672     else if (mGdkWindow) {
   673         // Destroy child windows to ensure that their mThebesSurfaces are
   674         // released and to remove references from GdkWindows back to their
   675         // container widget.  (OnContainerUnrealize() does this when the
   676         // MozContainer widget is destroyed.)
   677         DestroyChildWindows();
   679         gdk_window_set_user_data(mGdkWindow, nullptr);
   680         g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr);
   681         gdk_window_destroy(mGdkWindow);
   682         mGdkWindow = nullptr;
   683     }
   685     if (gInvisibleContainer && owningWidget == gInvisibleContainer) {
   686         CheckDestroyInvisibleContainer();
   687     }
   689 #ifdef ACCESSIBILITY
   690      if (mRootAccessible) {
   691          mRootAccessible = nullptr;
   692      }
   693 #endif
   695     // Save until last because OnDestroy() may cause us to be deleted.
   696     OnDestroy();
   698     return NS_OK;
   699 }
   701 nsIWidget *
   702 nsWindow::GetParent(void)
   703 {
   704     return mParent;
   705 }
   707 float
   708 nsWindow::GetDPI()
   709 {
   710     Display *dpy = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
   711     int defaultScreen = DefaultScreen(dpy);
   712     double heightInches = DisplayHeightMM(dpy, defaultScreen)/MM_PER_INCH_FLOAT;
   713     if (heightInches < 0.25) {
   714         // Something's broken, but we'd better not crash.
   715         return 96.0f;
   716     }
   717     return float(DisplayHeight(dpy, defaultScreen)/heightInches);
   718 }
   720 NS_IMETHODIMP
   721 nsWindow::SetParent(nsIWidget *aNewParent)
   722 {
   723     if (mContainer || !mGdkWindow) {
   724         NS_NOTREACHED("nsWindow::SetParent called illegally");
   725         return NS_ERROR_NOT_IMPLEMENTED;
   726     }
   728     nsCOMPtr<nsIWidget> kungFuDeathGrip = this;
   729     if (mParent) {
   730         mParent->RemoveChild(this);
   731     }
   733     mParent = aNewParent;
   735     GtkWidget* oldContainer = GetMozContainerWidget();
   736     if (!oldContainer) {
   737         // The GdkWindows have been destroyed so there is nothing else to
   738         // reparent.
   739         NS_ABORT_IF_FALSE(gdk_window_is_destroyed(mGdkWindow),
   740                           "live GdkWindow with no widget");
   741         return NS_OK;
   742     }
   744     if (aNewParent) {
   745         aNewParent->AddChild(this);
   746         ReparentNativeWidget(aNewParent);
   747     } else {
   748         // aNewParent is nullptr, but reparent to a hidden window to avoid
   749         // destroying the GdkWindow and its descendants.
   750         // An invisible container widget is needed to hold descendant
   751         // GtkWidgets.
   752         GtkWidget* newContainer = EnsureInvisibleContainer();
   753         GdkWindow* newParentWindow = gtk_widget_get_window(newContainer);
   754         ReparentNativeWidgetInternal(aNewParent, newContainer, newParentWindow,
   755                                      oldContainer);
   756     }
   757     return NS_OK;
   758 }
   760 NS_IMETHODIMP
   761 nsWindow::ReparentNativeWidget(nsIWidget* aNewParent)
   762 {
   763     NS_PRECONDITION(aNewParent, "");
   764     NS_ASSERTION(!mIsDestroyed, "");
   765     NS_ASSERTION(!static_cast<nsWindow*>(aNewParent)->mIsDestroyed, "");
   767     GtkWidget* oldContainer = GetMozContainerWidget();
   768     if (!oldContainer) {
   769         // The GdkWindows have been destroyed so there is nothing else to
   770         // reparent.
   771         NS_ABORT_IF_FALSE(gdk_window_is_destroyed(mGdkWindow),
   772                           "live GdkWindow with no widget");
   773         return NS_OK;
   774     }
   775     NS_ABORT_IF_FALSE(!gdk_window_is_destroyed(mGdkWindow),
   776                       "destroyed GdkWindow with widget");
   778     nsWindow* newParent = static_cast<nsWindow*>(aNewParent);
   779     GdkWindow* newParentWindow = newParent->mGdkWindow;
   780     GtkWidget* newContainer = newParent->GetMozContainerWidget();
   781     GtkWindow* shell = GTK_WINDOW(mShell);
   783     if (shell && gtk_window_get_transient_for(shell)) {
   784       GtkWindow* topLevelParent =
   785           GTK_WINDOW(gtk_widget_get_toplevel(newContainer));
   786       gtk_window_set_transient_for(shell, topLevelParent);
   787     }
   789     ReparentNativeWidgetInternal(aNewParent, newContainer, newParentWindow,
   790                                  oldContainer);
   791     return NS_OK;
   792 }
   794 void
   795 nsWindow::ReparentNativeWidgetInternal(nsIWidget* aNewParent,
   796                                        GtkWidget* aNewContainer,
   797                                        GdkWindow* aNewParentWindow,
   798                                        GtkWidget* aOldContainer)
   799 {
   800     if (!aNewContainer) {
   801         // The new parent GdkWindow has been destroyed.
   802         NS_ABORT_IF_FALSE(!aNewParentWindow ||
   803                           gdk_window_is_destroyed(aNewParentWindow),
   804                           "live GdkWindow with no widget");
   805         Destroy();
   806     } else {
   807         if (aNewContainer != aOldContainer) {
   808             NS_ABORT_IF_FALSE(!gdk_window_is_destroyed(aNewParentWindow),
   809                               "destroyed GdkWindow with widget");
   810             SetWidgetForHierarchy(mGdkWindow, aOldContainer, aNewContainer);
   812             if (aOldContainer == gInvisibleContainer) {
   813                 CheckDestroyInvisibleContainer();
   814             }
   815         }
   817         if (!mIsTopLevel) {
   818             gdk_window_reparent(mGdkWindow, aNewParentWindow, mBounds.x,
   819                                 mBounds.y);
   820         }
   821     }
   823     nsWindow* newParent = static_cast<nsWindow*>(aNewParent);
   824     bool parentHasMappedToplevel =
   825         newParent && newParent->mHasMappedToplevel;
   826     if (mHasMappedToplevel != parentHasMappedToplevel) {
   827         SetHasMappedToplevel(parentHasMappedToplevel);
   828     }
   829 }
   831 NS_IMETHODIMP
   832 nsWindow::SetModal(bool aModal)
   833 {
   834     LOG(("nsWindow::SetModal [%p] %d\n", (void *)this, aModal));
   835     if (mIsDestroyed)
   836         return aModal ? NS_ERROR_NOT_AVAILABLE : NS_OK;
   837     if (!mIsTopLevel || !mShell)
   838         return NS_ERROR_FAILURE;
   839     gtk_window_set_modal(GTK_WINDOW(mShell), aModal ? TRUE : FALSE);
   840     return NS_OK;
   841 }
   843 // nsIWidget method, which means IsShown.
   844 bool
   845 nsWindow::IsVisible() const
   846 {
   847     return mIsShown;
   848 }
   850 NS_IMETHODIMP
   851 nsWindow::ConstrainPosition(bool aAllowSlop, int32_t *aX, int32_t *aY)
   852 {
   853     if (mIsTopLevel && mShell) {
   854         int32_t screenWidth = gdk_screen_width();
   855         int32_t screenHeight = gdk_screen_height();
   856         if (aAllowSlop) {
   857             if (*aX < (kWindowPositionSlop - mBounds.width))
   858                 *aX = kWindowPositionSlop - mBounds.width;
   859             if (*aX > (screenWidth - kWindowPositionSlop))
   860                 *aX = screenWidth - kWindowPositionSlop;
   861             if (*aY < (kWindowPositionSlop - mBounds.height))
   862                 *aY = kWindowPositionSlop - mBounds.height;
   863             if (*aY > (screenHeight - kWindowPositionSlop))
   864                 *aY = screenHeight - kWindowPositionSlop;
   865         } else {
   866             if (*aX < 0)
   867                 *aX = 0;
   868             if (*aX > (screenWidth - mBounds.width))
   869                 *aX = screenWidth - mBounds.width;
   870             if (*aY < 0)
   871                 *aY = 0;
   872             if (*aY > (screenHeight - mBounds.height))
   873                 *aY = screenHeight - mBounds.height;
   874         }
   875     }
   876     return NS_OK;
   877 }
   879 void nsWindow::SetSizeConstraints(const SizeConstraints& aConstraints)
   880 {
   881     mSizeConstraints.mMinSize = GetSafeWindowSize(aConstraints.mMinSize);
   882     mSizeConstraints.mMaxSize = GetSafeWindowSize(aConstraints.mMaxSize);
   884     if (mShell) {
   885         GdkGeometry geometry;
   886         geometry.min_width = mSizeConstraints.mMinSize.width;
   887         geometry.min_height = mSizeConstraints.mMinSize.height;
   888         geometry.max_width = mSizeConstraints.mMaxSize.width;
   889         geometry.max_height = mSizeConstraints.mMaxSize.height;
   891         uint32_t hints = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE;
   892         gtk_window_set_geometry_hints(GTK_WINDOW(mShell), nullptr,
   893                                       &geometry, GdkWindowHints(hints));
   894     }
   895 }
   897 NS_IMETHODIMP
   898 nsWindow::Show(bool aState)
   899 {
   900     if (aState == mIsShown)
   901         return NS_OK;
   903     // Clear our cached resources when the window is hidden.
   904     if (mIsShown && !aState) {
   905         ClearCachedResources();
   906     }
   908     mIsShown = aState;
   910     LOG(("nsWindow::Show [%p] state %d\n", (void *)this, aState));
   912     if (aState) {
   913         // Now that this window is shown, mHasMappedToplevel needs to be
   914         // tracked on viewable descendants.
   915         SetHasMappedToplevel(mHasMappedToplevel);
   916     }
   918     // Ok, someone called show on a window that isn't sized to a sane
   919     // value.  Mark this window as needing to have Show() called on it
   920     // and return.
   921     if ((aState && !AreBoundsSane()) || !mCreated) {
   922         LOG(("\tbounds are insane or window hasn't been created yet\n"));
   923         mNeedsShow = true;
   924         return NS_OK;
   925     }
   927     // If someone is hiding this widget, clear any needing show flag.
   928     if (!aState)
   929         mNeedsShow = false;
   931     // If someone is showing this window and it needs a resize then
   932     // resize the widget.
   933     if (aState) {
   934         if (mNeedsMove) {
   935             NativeResize(mBounds.x, mBounds.y, mBounds.width, mBounds.height,
   936                          false);
   937         } else if (mNeedsResize) {
   938             NativeResize(mBounds.width, mBounds.height, false);
   939         }
   940     }
   942 #ifdef ACCESSIBILITY
   943     if (aState && a11y::ShouldA11yBeEnabled())
   944         CreateRootAccessible();
   945 #endif
   947     NativeShow(aState);
   949     return NS_OK;
   950 }
   952 NS_IMETHODIMP
   953 nsWindow::Resize(double aWidth, double aHeight, bool aRepaint)
   954 {
   955     CSSToLayoutDeviceScale scale = BoundsUseDisplayPixels() ? GetDefaultScale()
   956                                     : CSSToLayoutDeviceScale(1.0);
   957     int32_t width = NSToIntRound(scale.scale * aWidth);
   958     int32_t height = NSToIntRound(scale.scale * aHeight);
   959     ConstrainSize(&width, &height);
   961     // For top-level windows, aWidth and aHeight should possibly be
   962     // interpreted as frame bounds, but NativeResize treats these as window
   963     // bounds (Bug 581866).
   965     mBounds.SizeTo(width, height);
   967     if (!mCreated)
   968         return NS_OK;
   970     // There are several cases here that we need to handle, based on a
   971     // matrix of the visibility of the widget, the sanity of this resize
   972     // and whether or not the widget was previously sane.
   974     // Has this widget been set to visible?
   975     if (mIsShown) {
   976         // Are the bounds sane?
   977         if (AreBoundsSane()) {
   978             // Yep?  Resize the window
   979             //Maybe, the toplevel has moved
   981             // Note that if the widget needs to be positioned because its
   982             // size was previously insane in Resize(x,y,w,h), then we need
   983             // to set the x and y here too, because the widget wasn't
   984             // moved back then
   985             if (mNeedsMove)
   986                 NativeResize(mBounds.x, mBounds.y,
   987                              mBounds.width, mBounds.height, aRepaint);
   988             else
   989                 NativeResize(mBounds.width, mBounds.height, aRepaint);
   991             // Does it need to be shown because it was previously insane?
   992             if (mNeedsShow)
   993                 NativeShow(true);
   994         }
   995         else {
   996             // If someone has set this so that the needs show flag is false
   997             // and it needs to be hidden, update the flag and hide the
   998             // window.  This flag will be cleared the next time someone
   999             // hides the window or shows it.  It also prevents us from
  1000             // calling NativeShow(false) excessively on the window which
  1001             // causes unneeded X traffic.
  1002             if (!mNeedsShow) {
  1003                 mNeedsShow = true;
  1004                 NativeShow(false);
  1008     // If the widget hasn't been shown, mark the widget as needing to be
  1009     // resized before it is shown.
  1010     else {
  1011         if (AreBoundsSane() && mListenForResizes) {
  1012             // For widgets that we listen for resizes for (widgets created
  1013             // with native parents) we apparently _always_ have to resize.  I
  1014             // dunno why, but apparently we're lame like that.
  1015             NativeResize(width, height, aRepaint);
  1017         else {
  1018             mNeedsResize = true;
  1022     NotifyRollupGeometryChange();
  1024     // send a resize notification if this is a toplevel
  1025     if (mIsTopLevel || mListenForResizes) {
  1026         DispatchResized(width, height);
  1029     return NS_OK;
  1032 NS_IMETHODIMP
  1033 nsWindow::Resize(double aX, double aY, double aWidth, double aHeight,
  1034                  bool aRepaint)
  1036     CSSToLayoutDeviceScale scale = BoundsUseDisplayPixels() ? GetDefaultScale()
  1037                                     : CSSToLayoutDeviceScale(1.0);
  1038     int32_t width = NSToIntRound(scale.scale * aWidth);
  1039     int32_t height = NSToIntRound(scale.scale * aHeight);
  1040     ConstrainSize(&width, &height);
  1042     int32_t x = NSToIntRound(scale.scale * aX);
  1043     int32_t y = NSToIntRound(scale.scale * aY);
  1044     mBounds.x = x;
  1045     mBounds.y = y;
  1046     mBounds.SizeTo(width, height);
  1048     mNeedsMove = true;
  1050     if (!mCreated)
  1051         return NS_OK;
  1053     // There are several cases here that we need to handle, based on a
  1054     // matrix of the visibility of the widget, the sanity of this resize
  1055     // and whether or not the widget was previously sane.
  1057     // Has this widget been set to visible?
  1058     if (mIsShown) {
  1059         // Are the bounds sane?
  1060         if (AreBoundsSane()) {
  1061             // Yep?  Resize the window
  1062             NativeResize(x, y, width, height, aRepaint);
  1063             // Does it need to be shown because it was previously insane?
  1064             if (mNeedsShow)
  1065                 NativeShow(true);
  1067         else {
  1068             // If someone has set this so that the needs show flag is false
  1069             // and it needs to be hidden, update the flag and hide the
  1070             // window.  This flag will be cleared the next time someone
  1071             // hides the window or shows it.  It also prevents us from
  1072             // calling NativeShow(false) excessively on the window which
  1073             // causes unneeded X traffic.
  1074             if (!mNeedsShow) {
  1075                 mNeedsShow = true;
  1076                 NativeShow(false);
  1080     // If the widget hasn't been shown, mark the widget as needing to be
  1081     // resized before it is shown
  1082     else {
  1083         if (AreBoundsSane() && mListenForResizes){
  1084             // For widgets that we listen for resizes for (widgets created
  1085             // with native parents) we apparently _always_ have to resize.  I
  1086             // dunno why, but apparently we're lame like that.
  1087             NativeResize(x, y, width, height, aRepaint);
  1089         else {
  1090             mNeedsResize = true;
  1094     NotifyRollupGeometryChange();
  1096     if (mIsTopLevel || mListenForResizes) {
  1097         DispatchResized(width, height);
  1100     return NS_OK;
  1103 NS_IMETHODIMP
  1104 nsWindow::Enable(bool aState)
  1106     mEnabled = aState;
  1108     return NS_OK;
  1111 bool
  1112 nsWindow::IsEnabled() const
  1114     return mEnabled;
  1119 NS_IMETHODIMP
  1120 nsWindow::Move(double aX, double aY)
  1122     LOG(("nsWindow::Move [%p] %f %f\n", (void *)this,
  1123          aX, aY));
  1125     CSSToLayoutDeviceScale scale = BoundsUseDisplayPixels() ? GetDefaultScale()
  1126                                    : CSSToLayoutDeviceScale(1.0);
  1127     int32_t x = NSToIntRound(aX * scale.scale);
  1128     int32_t y = NSToIntRound(aY * scale.scale);
  1130     if (mWindowType == eWindowType_toplevel ||
  1131         mWindowType == eWindowType_dialog) {
  1132         SetSizeMode(nsSizeMode_Normal);
  1135     // Since a popup window's x/y coordinates are in relation to to
  1136     // the parent, the parent might have moved so we always move a
  1137     // popup window.
  1138     if (x == mBounds.x && y == mBounds.y &&
  1139         mWindowType != eWindowType_popup)
  1140         return NS_OK;
  1142     // XXX Should we do some AreBoundsSane check here?
  1144     mBounds.x = x;
  1145     mBounds.y = y;
  1147     if (!mCreated)
  1148         return NS_OK;
  1150     mNeedsMove = false;
  1152     if (mIsTopLevel) {
  1153         gtk_window_move(GTK_WINDOW(mShell), x, y);
  1155     else if (mGdkWindow) {
  1156         gdk_window_move(mGdkWindow, x, y);
  1159     NotifyRollupGeometryChange();
  1160     return NS_OK;
  1163 NS_IMETHODIMP
  1164 nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement  aPlacement,
  1165                       nsIWidget                  *aWidget,
  1166                       bool                        aActivate)
  1168     return NS_ERROR_NOT_IMPLEMENTED;
  1171 void
  1172 nsWindow::SetZIndex(int32_t aZIndex)
  1174     nsIWidget* oldPrev = GetPrevSibling();
  1176     nsBaseWidget::SetZIndex(aZIndex);
  1178     if (GetPrevSibling() == oldPrev) {
  1179         return;
  1182     NS_ASSERTION(!mContainer, "Expected Mozilla child widget");
  1184     // We skip the nsWindows that don't have mGdkWindows.
  1185     // These are probably in the process of being destroyed.
  1187     if (!GetNextSibling()) {
  1188         // We're to be on top.
  1189         if (mGdkWindow)
  1190             gdk_window_raise(mGdkWindow);
  1191     } else {
  1192         // All the siblings before us need to be below our widget.
  1193         for (nsWindow* w = this; w;
  1194              w = static_cast<nsWindow*>(w->GetPrevSibling())) {
  1195             if (w->mGdkWindow)
  1196                 gdk_window_lower(w->mGdkWindow);
  1201 NS_IMETHODIMP
  1202 nsWindow::SetSizeMode(int32_t aMode)
  1204     nsresult rv;
  1206     LOG(("nsWindow::SetSizeMode [%p] %d\n", (void *)this, aMode));
  1208     // Save the requested state.
  1209     rv = nsBaseWidget::SetSizeMode(aMode);
  1211     // return if there's no shell or our current state is the same as
  1212     // the mode we were just set to.
  1213     if (!mShell || mSizeState == mSizeMode) {
  1214         return rv;
  1217     switch (aMode) {
  1218     case nsSizeMode_Maximized:
  1219         gtk_window_maximize(GTK_WINDOW(mShell));
  1220         break;
  1221     case nsSizeMode_Minimized:
  1222         gtk_window_iconify(GTK_WINDOW(mShell));
  1223         break;
  1224     case nsSizeMode_Fullscreen:
  1225         MakeFullScreen(true);
  1226         break;
  1228     default:
  1229         // nsSizeMode_Normal, really.
  1230         if (mSizeState == nsSizeMode_Minimized)
  1231             gtk_window_deiconify(GTK_WINDOW(mShell));
  1232         else if (mSizeState == nsSizeMode_Maximized)
  1233             gtk_window_unmaximize(GTK_WINDOW(mShell));
  1234         break;
  1237     mSizeState = mSizeMode;
  1239     return rv;
  1242 typedef void (* SetUserTimeFunc)(GdkWindow* aWindow, guint32 aTimestamp);
  1244 // This will become obsolete when new GTK APIs are widely supported,
  1245 // as described here: http://bugzilla.gnome.org/show_bug.cgi?id=347375
  1246 static void
  1247 SetUserTimeAndStartupIDForActivatedWindow(GtkWidget* aWindow)
  1249     nsGTKToolkit* GTKToolkit = nsGTKToolkit::GetToolkit();
  1250     if (!GTKToolkit)
  1251         return;
  1253     nsAutoCString desktopStartupID;
  1254     GTKToolkit->GetDesktopStartupID(&desktopStartupID);
  1255     if (desktopStartupID.IsEmpty()) {
  1256         // We don't have the data we need. Fall back to an
  1257         // approximation ... using the timestamp of the remote command
  1258         // being received as a guess for the timestamp of the user event
  1259         // that triggered it.
  1260         uint32_t timestamp = GTKToolkit->GetFocusTimestamp();
  1261         if (timestamp) {
  1262             gdk_window_focus(gtk_widget_get_window(aWindow), timestamp);
  1263             GTKToolkit->SetFocusTimestamp(0);
  1265         return;
  1268 #if defined(MOZ_ENABLE_STARTUP_NOTIFICATION)
  1269     GdkWindow* gdkWindow = gtk_widget_get_window(aWindow);
  1271     GdkScreen* screen = gdk_window_get_screen(gdkWindow);
  1272     SnDisplay* snd =
  1273         sn_display_new(gdk_x11_display_get_xdisplay(gdk_window_get_display(gdkWindow)), 
  1274                        nullptr, nullptr);
  1275     if (!snd)
  1276         return;
  1277     SnLauncheeContext* ctx =
  1278         sn_launchee_context_new(snd, gdk_screen_get_number(screen),
  1279                                 desktopStartupID.get());
  1280     if (!ctx) {
  1281         sn_display_unref(snd);
  1282         return;
  1285     if (sn_launchee_context_get_id_has_timestamp(ctx)) {
  1286         PRLibrary* gtkLibrary;
  1287         SetUserTimeFunc setUserTimeFunc = (SetUserTimeFunc)
  1288             PR_FindFunctionSymbolAndLibrary("gdk_x11_window_set_user_time", &gtkLibrary);
  1289         if (setUserTimeFunc) {
  1290             setUserTimeFunc(gdkWindow, sn_launchee_context_get_timestamp(ctx));
  1291             PR_UnloadLibrary(gtkLibrary);
  1295     sn_launchee_context_setup_window(ctx, gdk_x11_window_get_xid(gdkWindow));
  1296     sn_launchee_context_complete(ctx);
  1298     sn_launchee_context_unref(ctx);
  1299     sn_display_unref(snd);
  1300 #endif
  1302     // If we used the startup ID, that already contains the focus timestamp;
  1303     // we don't want to reuse the timestamp next time we raise the window
  1304     GTKToolkit->SetFocusTimestamp(0);
  1305     GTKToolkit->SetDesktopStartupID(EmptyCString());
  1308 /* static */ guint32
  1309 nsWindow::GetLastUserInputTime()
  1311     // gdk_x11_display_get_user_time tracks button and key presses,
  1312     // DESKTOP_STARTUP_ID used to start the app, drop events from external
  1313     // drags, WM_DELETE_WINDOW delete events, but not usually mouse motion nor
  1314     // button and key releases.  Therefore use the most recent of
  1315     // gdk_x11_display_get_user_time and the last time that we have seen.
  1316     guint32 timestamp =
  1317             gdk_x11_display_get_user_time(gdk_display_get_default());
  1318     if (sLastUserInputTime != GDK_CURRENT_TIME &&
  1319         TimestampIsNewerThan(sLastUserInputTime, timestamp)) {
  1320         return sLastUserInputTime;
  1323     return timestamp;
  1326 NS_IMETHODIMP
  1327 nsWindow::SetFocus(bool aRaise)
  1329     // Make sure that our owning widget has focus.  If it doesn't try to
  1330     // grab it.  Note that we don't set our focus flag in this case.
  1332     LOGFOCUS(("  SetFocus %d [%p]\n", aRaise, (void *)this));
  1334     GtkWidget *owningWidget = GetMozContainerWidget();
  1335     if (!owningWidget)
  1336         return NS_ERROR_FAILURE;
  1338     // Raise the window if someone passed in true and the prefs are
  1339     // set properly.
  1340     GtkWidget *toplevelWidget = gtk_widget_get_toplevel(owningWidget);
  1342     if (gRaiseWindows && aRaise && toplevelWidget &&
  1343         !gtk_widget_has_focus(owningWidget) &&
  1344         !gtk_widget_has_focus(toplevelWidget)) {
  1345         GtkWidget* top_window = GetToplevelWidget();
  1346         if (top_window && (gtk_widget_get_visible(top_window)))
  1348             gdk_window_show_unraised(gtk_widget_get_window(top_window));
  1349             // Unset the urgency hint if possible.
  1350             SetUrgencyHint(top_window, false);
  1354     nsRefPtr<nsWindow> owningWindow = get_window_for_gtk_widget(owningWidget);
  1355     if (!owningWindow)
  1356         return NS_ERROR_FAILURE;
  1358     if (aRaise) {
  1359         // aRaise == true means request toplevel activation.
  1361         // This is asynchronous.
  1362         // If and when the window manager accepts the request, then the focus
  1363         // widget will get a focus-in-event signal.
  1364         if (gRaiseWindows && owningWindow->mIsShown && owningWindow->mShell &&
  1365             !gtk_window_is_active(GTK_WINDOW(owningWindow->mShell))) {
  1367             uint32_t timestamp = GDK_CURRENT_TIME;
  1369             nsGTKToolkit* GTKToolkit = nsGTKToolkit::GetToolkit();
  1370             if (GTKToolkit)
  1371                 timestamp = GTKToolkit->GetFocusTimestamp();
  1373             LOGFOCUS(("  requesting toplevel activation [%p]\n", (void *)this));
  1374             NS_ASSERTION(owningWindow->mWindowType != eWindowType_popup
  1375                          || mParent,
  1376                          "Presenting an override-redirect window");
  1377             gtk_window_present_with_time(GTK_WINDOW(owningWindow->mShell), timestamp);
  1379             if (GTKToolkit)
  1380                 GTKToolkit->SetFocusTimestamp(0);
  1383         return NS_OK;
  1386     // aRaise == false means that keyboard events should be dispatched
  1387     // from this widget.
  1389     // Ensure owningWidget is the focused GtkWidget within its toplevel window.
  1390     //
  1391     // For eWindowType_popup, this GtkWidget may not actually be the one that
  1392     // receives the key events as it may be the parent window that is active.
  1393     if (!gtk_widget_is_focus(owningWidget)) {
  1394         // This is synchronous.  It takes focus from a plugin or from a widget
  1395         // in an embedder.  The focus manager already knows that this window
  1396         // is active so gBlockActivateEvent avoids another (unnecessary)
  1397         // activate notification.
  1398         gBlockActivateEvent = true;
  1399         gtk_widget_grab_focus(owningWidget);
  1400         gBlockActivateEvent = false;
  1403     // If this is the widget that already has focus, return.
  1404     if (gFocusWindow == this) {
  1405         LOGFOCUS(("  already have focus [%p]\n", (void *)this));
  1406         return NS_OK;
  1409     // Set this window to be the focused child window
  1410     gFocusWindow = this;
  1412     if (mIMModule) {
  1413         mIMModule->OnFocusWindow(this);
  1416     LOGFOCUS(("  widget now has focus in SetFocus() [%p]\n",
  1417               (void *)this));
  1419     return NS_OK;
  1422 NS_IMETHODIMP
  1423 nsWindow::GetScreenBounds(nsIntRect &aRect)
  1425     if (mIsTopLevel && mContainer) {
  1426         // use the point including window decorations
  1427         gint x, y;
  1428         gdk_window_get_root_origin(gtk_widget_get_window(GTK_WIDGET(mContainer)), &x, &y);
  1429         aRect.MoveTo(x, y);
  1431     else {
  1432         aRect.MoveTo(WidgetToScreenOffset());
  1434     // mBounds.Size() is the window bounds, not the window-manager frame
  1435     // bounds (bug 581863).  gdk_window_get_frame_extents would give the
  1436     // frame bounds, but mBounds.Size() is returned here for consistency
  1437     // with Resize.
  1438     aRect.SizeTo(mBounds.Size());
  1439     LOG(("GetScreenBounds %d,%d | %dx%d\n",
  1440          aRect.x, aRect.y, aRect.width, aRect.height));
  1441     return NS_OK;
  1444 NS_IMETHODIMP
  1445 nsWindow::GetClientBounds(nsIntRect &aRect)
  1447     // GetBounds returns a rect whose top left represents the top left of the
  1448     // outer bounds, but whose width/height represent the size of the inner
  1449     // bounds (which is messed up).
  1450     GetBounds(aRect);
  1451     aRect.MoveBy(GetClientOffset());
  1453     return NS_OK;
  1456 nsIntPoint
  1457 nsWindow::GetClientOffset()
  1459     if (!mIsTopLevel) {
  1460         return nsIntPoint(0, 0);
  1463     GdkAtom cardinal_atom = gdk_x11_xatom_to_atom(XA_CARDINAL);
  1465     GdkAtom type_returned;
  1466     int format_returned;
  1467     int length_returned;
  1468     long *frame_extents;
  1469     GdkWindow* window;
  1471     if (!mShell || !(window = gtk_widget_get_window(mShell)) ||
  1472         !gdk_property_get(window,
  1473                           gdk_atom_intern ("_NET_FRAME_EXTENTS", FALSE),
  1474                           cardinal_atom,
  1475                           0, // offset
  1476                           4*4, // length
  1477                           FALSE, // delete
  1478                           &type_returned,
  1479                           &format_returned,
  1480                           &length_returned,
  1481                           (guchar **) &frame_extents) ||
  1482         length_returned/sizeof(glong) != 4) {
  1484         return nsIntPoint(0, 0);
  1487     // data returned is in the order left, right, top, bottom
  1488     int32_t left = int32_t(frame_extents[0]);
  1489     int32_t top = int32_t(frame_extents[2]);
  1491     g_free(frame_extents);
  1493     return nsIntPoint(left, top);
  1496 NS_IMETHODIMP
  1497 nsWindow::SetCursor(nsCursor aCursor)
  1499     // if we're not the toplevel window pass up the cursor request to
  1500     // the toplevel window to handle it.
  1501     if (!mContainer && mGdkWindow) {
  1502         nsWindow *window = GetContainerWindow();
  1503         if (!window)
  1504             return NS_ERROR_FAILURE;
  1506         return window->SetCursor(aCursor);
  1509     // Only change cursor if it's actually been changed
  1510     if (aCursor != mCursor) {
  1511         GdkCursor *newCursor = nullptr;
  1513         newCursor = get_gtk_cursor(aCursor);
  1515         if (nullptr != newCursor) {
  1516             mCursor = aCursor;
  1518             if (!mContainer)
  1519                 return NS_OK;
  1521             gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)), newCursor);
  1525     return NS_OK;
  1528 NS_IMETHODIMP
  1529 nsWindow::SetCursor(imgIContainer* aCursor,
  1530                     uint32_t aHotspotX, uint32_t aHotspotY)
  1532     // if we're not the toplevel window pass up the cursor request to
  1533     // the toplevel window to handle it.
  1534     if (!mContainer && mGdkWindow) {
  1535         nsWindow *window = GetContainerWindow();
  1536         if (!window)
  1537             return NS_ERROR_FAILURE;
  1539         return window->SetCursor(aCursor, aHotspotX, aHotspotY);
  1542     mCursor = nsCursor(-1);
  1544     // Get the image's current frame
  1545     GdkPixbuf* pixbuf = nsImageToPixbuf::ImageToPixbuf(aCursor);
  1546     if (!pixbuf)
  1547         return NS_ERROR_NOT_AVAILABLE;
  1549     int width = gdk_pixbuf_get_width(pixbuf);
  1550     int height = gdk_pixbuf_get_height(pixbuf);
  1551     // Reject cursors greater than 128 pixels in some direction, to prevent
  1552     // spoofing.
  1553     // XXX ideally we should rescale. Also, we could modify the API to
  1554     // allow trusted content to set larger cursors.
  1555     if (width > 128 || height > 128) {
  1556         g_object_unref(pixbuf);
  1557         return NS_ERROR_NOT_AVAILABLE;
  1560     // Looks like all cursors need an alpha channel (tested on Gtk 2.4.4). This
  1561     // is of course not documented anywhere...
  1562     // So add one if there isn't one yet
  1563     if (!gdk_pixbuf_get_has_alpha(pixbuf)) {
  1564         GdkPixbuf* alphaBuf = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0);
  1565         g_object_unref(pixbuf);
  1566         if (!alphaBuf) {
  1567             return NS_ERROR_OUT_OF_MEMORY;
  1569         pixbuf = alphaBuf;
  1572     GdkCursor* cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(),
  1573                                                    pixbuf,
  1574                                                    aHotspotX, aHotspotY);
  1575     g_object_unref(pixbuf);
  1576     nsresult rv = NS_ERROR_OUT_OF_MEMORY;
  1577     if (cursor) {
  1578         if (mContainer) {
  1579             gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)), cursor);
  1580             rv = NS_OK;
  1582         gdk_cursor_unref(cursor);
  1585     return rv;
  1588 NS_IMETHODIMP
  1589 nsWindow::Invalidate(const nsIntRect &aRect)
  1591     if (!mGdkWindow)
  1592         return NS_OK;
  1594     GdkRectangle rect;
  1595     rect.x = aRect.x;
  1596     rect.y = aRect.y;
  1597     rect.width = aRect.width;
  1598     rect.height = aRect.height;
  1600     LOGDRAW(("Invalidate (rect) [%p]: %d %d %d %d\n", (void *)this,
  1601              rect.x, rect.y, rect.width, rect.height));
  1603     gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
  1605     return NS_OK;
  1608 void*
  1609 nsWindow::GetNativeData(uint32_t aDataType)
  1611     switch (aDataType) {
  1612     case NS_NATIVE_WINDOW:
  1613     case NS_NATIVE_WIDGET: {
  1614         if (!mGdkWindow)
  1615             return nullptr;
  1617         return mGdkWindow;
  1618         break;
  1621     case NS_NATIVE_PLUGIN_PORT:
  1622         return SetupPluginPort();
  1623         break;
  1625     case NS_NATIVE_DISPLAY:
  1626 #ifdef MOZ_X11
  1627         return GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
  1628 #else
  1629         return nullptr;
  1630 #endif /* MOZ_X11 */
  1631         break;
  1633     case NS_NATIVE_SHELLWIDGET:
  1634         return GetToplevelWidget();
  1636     case NS_NATIVE_SHAREABLE_WINDOW:
  1637         return (void *) GDK_WINDOW_XID(gdk_window_get_toplevel(mGdkWindow));
  1639     default:
  1640         NS_WARNING("nsWindow::GetNativeData called with bad value");
  1641         return nullptr;
  1645 NS_IMETHODIMP
  1646 nsWindow::SetTitle(const nsAString& aTitle)
  1648     if (!mShell)
  1649         return NS_OK;
  1651     // convert the string into utf8 and set the title.
  1652 #define UTF8_FOLLOWBYTE(ch) (((ch) & 0xC0) == 0x80)
  1653     NS_ConvertUTF16toUTF8 titleUTF8(aTitle);
  1654     if (titleUTF8.Length() > NS_WINDOW_TITLE_MAX_LENGTH) {
  1655         // Truncate overlong titles (bug 167315). Make sure we chop after a
  1656         // complete sequence by making sure the next char isn't a follow-byte.
  1657         uint32_t len = NS_WINDOW_TITLE_MAX_LENGTH;
  1658         while(UTF8_FOLLOWBYTE(titleUTF8[len]))
  1659             --len;
  1660         titleUTF8.Truncate(len);
  1662     gtk_window_set_title(GTK_WINDOW(mShell), (const char *)titleUTF8.get());
  1664     return NS_OK;
  1667 NS_IMETHODIMP
  1668 nsWindow::SetIcon(const nsAString& aIconSpec)
  1670     if (!mShell)
  1671         return NS_OK;
  1673     nsAutoCString iconName;
  1675     if (aIconSpec.EqualsLiteral("default")) {
  1676         nsXPIDLString brandName;
  1677         GetBrandName(brandName);
  1678         AppendUTF16toUTF8(brandName, iconName);
  1679         ToLowerCase(iconName);
  1680     } else {
  1681         AppendUTF16toUTF8(aIconSpec, iconName);
  1684     nsCOMPtr<nsIFile> iconFile;
  1685     nsAutoCString path;
  1687     gint *iconSizes =
  1688         gtk_icon_theme_get_icon_sizes(gtk_icon_theme_get_default(),
  1689                                       iconName.get());
  1690     bool foundIcon = (iconSizes[0] != 0);
  1691     g_free(iconSizes);
  1693     if (!foundIcon) {
  1694         // Look for icons with the following suffixes appended to the base name
  1695         // The last two entries (for the old XPM format) will be ignored unless
  1696         // no icons are found using other suffixes. XPM icons are deprecated.
  1698         const char extensions[6][7] = { ".png", "16.png", "32.png", "48.png",
  1699                                     ".xpm", "16.xpm" };
  1701         for (uint32_t i = 0; i < ArrayLength(extensions); i++) {
  1702             // Don't bother looking for XPM versions if we found a PNG.
  1703             if (i == ArrayLength(extensions) - 2 && foundIcon)
  1704                 break;
  1706             nsAutoString extension;
  1707             extension.AppendASCII(extensions[i]);
  1709             ResolveIconName(aIconSpec, extension, getter_AddRefs(iconFile));
  1710             if (iconFile) {
  1711                 iconFile->GetNativePath(path);
  1712                 GdkPixbuf *icon = gdk_pixbuf_new_from_file(path.get(), nullptr);
  1713                 if (icon) {
  1714                     gtk_icon_theme_add_builtin_icon(iconName.get(),
  1715                                                     gdk_pixbuf_get_height(icon),
  1716                                                     icon);
  1717                     g_object_unref(icon);
  1718                     foundIcon = true;
  1724     // leave the default icon intact if no matching icons were found
  1725     if (foundIcon) {
  1726         gtk_window_set_icon_name(GTK_WINDOW(mShell), iconName.get());
  1729     return NS_OK;
  1733 nsIntPoint
  1734 nsWindow::WidgetToScreenOffset()
  1736     gint x = 0, y = 0;
  1738     if (mGdkWindow) {
  1739         gdk_window_get_origin(mGdkWindow, &x, &y);
  1742     return nsIntPoint(x, y);
  1745 NS_IMETHODIMP
  1746 nsWindow::EnableDragDrop(bool aEnable)
  1748     return NS_OK;
  1751 NS_IMETHODIMP
  1752 nsWindow::CaptureMouse(bool aCapture)
  1754     LOG(("CaptureMouse %p\n", (void *)this));
  1756     if (!mGdkWindow)
  1757         return NS_OK;
  1759     if (!mShell)
  1760         return NS_ERROR_FAILURE;
  1762     if (aCapture) {
  1763         gtk_grab_add(mShell);
  1764         GrabPointer(GetLastUserInputTime());
  1766     else {
  1767         ReleaseGrabs();
  1768         gtk_grab_remove(mShell);
  1771     return NS_OK;
  1774 NS_IMETHODIMP
  1775 nsWindow::CaptureRollupEvents(nsIRollupListener *aListener,
  1776                               bool               aDoCapture)
  1778     if (!mGdkWindow)
  1779         return NS_OK;
  1781     if (!mShell)
  1782         return NS_ERROR_FAILURE;
  1784     LOG(("CaptureRollupEvents %p %i\n", this, int(aDoCapture)));
  1786     if (aDoCapture) {
  1787         gRollupListener = aListener;
  1788         // real grab is only done when there is no dragging
  1789         if (!nsWindow::DragInProgress()) {
  1790             // This widget grab ensures that a Gecko GtkWidget receives mouse
  1791             // events even when embedded in non-Gecko-owned GtkWidgets.
  1792             // The grab is placed on the toplevel GtkWindow instead of the
  1793             // MozContainer to avoid double dispatch of keyboard events
  1794             // (bug 707623).
  1795             gtk_grab_add(mShell);
  1796             GrabPointer(GetLastUserInputTime());
  1799     else {
  1800         if (!nsWindow::DragInProgress()) {
  1801             ReleaseGrabs();
  1803         // There may not have been a drag in process when aDoCapture was set,
  1804         // so make sure to remove any added grab.  This is a no-op if the grab
  1805         // was not added to this widget.
  1806         gtk_grab_remove(mShell);
  1807         gRollupListener = nullptr;
  1810     return NS_OK;
  1813 NS_IMETHODIMP
  1814 nsWindow::GetAttention(int32_t aCycleCount)
  1816     LOG(("nsWindow::GetAttention [%p]\n", (void *)this));
  1818     GtkWidget* top_window = GetToplevelWidget();
  1819     GtkWidget* top_focused_window =
  1820         gFocusWindow ? gFocusWindow->GetToplevelWidget() : nullptr;
  1822     // Don't get attention if the window is focused anyway.
  1823     if (top_window && (gtk_widget_get_visible(top_window)) &&
  1824         top_window != top_focused_window) {
  1825         SetUrgencyHint(top_window, true);
  1828     return NS_OK;
  1831 bool
  1832 nsWindow::HasPendingInputEvent()
  1834     // This sucks, but gtk/gdk has no way to answer the question we want while
  1835     // excluding paint events, and there's no X API that will let us peek
  1836     // without blocking or removing.  To prevent event reordering, peek
  1837     // anything except expose events.  Reordering expose and others should be
  1838     // ok, hopefully.
  1839     bool haveEvent;
  1840 #ifdef MOZ_X11
  1841     XEvent ev;
  1842     Display *display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
  1843     haveEvent =
  1844         XCheckMaskEvent(display,
  1845                         KeyPressMask | KeyReleaseMask | ButtonPressMask |
  1846                         ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
  1847                         PointerMotionMask | PointerMotionHintMask |
  1848                         Button1MotionMask | Button2MotionMask |
  1849                         Button3MotionMask | Button4MotionMask |
  1850                         Button5MotionMask | ButtonMotionMask | KeymapStateMask |
  1851                         VisibilityChangeMask | StructureNotifyMask |
  1852                         ResizeRedirectMask | SubstructureNotifyMask |
  1853                         SubstructureRedirectMask | FocusChangeMask |
  1854                         PropertyChangeMask | ColormapChangeMask |
  1855                         OwnerGrabButtonMask, &ev);
  1856     if (haveEvent) {
  1857         XPutBackEvent(display, &ev);
  1859 #else
  1860     haveEvent = false;
  1861 #endif
  1862     return haveEvent;
  1865 #if 0
  1866 #ifdef DEBUG
  1867 // Paint flashing code (disabled for cairo - see below)
  1869 #define CAPS_LOCK_IS_ON \
  1870 (KeymapWrapper::AreModifiersCurrentlyActive(KeymapWrapper::CAPS_LOCK))
  1872 #define WANT_PAINT_FLASHING \
  1873 (debug_WantPaintFlashing() && CAPS_LOCK_IS_ON)
  1875 #ifdef MOZ_X11
  1876 static void
  1877 gdk_window_flash(GdkWindow *    aGdkWindow,
  1878                  unsigned int   aTimes,
  1879                  unsigned int   aInterval,  // Milliseconds
  1880                  GdkRegion *    aRegion)
  1882   gint         x;
  1883   gint         y;
  1884   gint         width;
  1885   gint         height;
  1886   guint        i;
  1887   GdkGC *      gc = 0;
  1888   GdkColor     white;
  1890 #if (MOZ_WIDGET_GTK == 2)
  1891   gdk_window_get_geometry(aGdkWindow,nullptr,nullptr,&width,&height,nullptr);
  1892 #else
  1893   gdk_window_get_geometry(aGdkWindow,nullptr,nullptr,&width,&height);
  1894 #endif
  1896   gdk_window_get_origin (aGdkWindow,
  1897                          &x,
  1898                          &y);
  1900   gc = gdk_gc_new(gdk_get_default_root_window());
  1902   white.pixel = WhitePixel(gdk_display,DefaultScreen(gdk_display));
  1904   gdk_gc_set_foreground(gc,&white);
  1905   gdk_gc_set_function(gc,GDK_XOR);
  1906   gdk_gc_set_subwindow(gc,GDK_INCLUDE_INFERIORS);
  1908   gdk_region_offset(aRegion, x, y);
  1909   gdk_gc_set_clip_region(gc, aRegion);
  1911   /*
  1912    * Need to do this twice so that the XOR effect can replace
  1913    * the original window contents.
  1914    */
  1915   for (i = 0; i < aTimes * 2; i++)
  1917     gdk_draw_rectangle(gdk_get_default_root_window(),
  1918                        gc,
  1919                        TRUE,
  1920                        x,
  1921                        y,
  1922                        width,
  1923                        height);
  1925     gdk_flush();
  1927     PR_Sleep(PR_MillisecondsToInterval(aInterval));
  1930   gdk_gc_destroy(gc);
  1932   gdk_region_offset(aRegion, -x, -y);
  1934 #endif /* MOZ_X11 */
  1935 #endif // DEBUG
  1936 #endif
  1938 struct ExposeRegion
  1940     nsIntRegion mRegion;
  1942 #if (MOZ_WIDGET_GTK == 2)
  1943     GdkRectangle *mRects;
  1944     GdkRectangle *mRectsEnd;
  1946     ExposeRegion() : mRects(nullptr)
  1949     ~ExposeRegion()
  1951         g_free(mRects);
  1953     bool Init(GdkEventExpose *aEvent)
  1955         gint nrects;
  1956         gdk_region_get_rectangles(aEvent->region, &mRects, &nrects);
  1958         if (nrects > MAX_RECTS_IN_REGION) {
  1959             // Just use the bounding box
  1960             mRects[0] = aEvent->area;
  1961             nrects = 1;
  1964         mRectsEnd = mRects + nrects;
  1966         for (GdkRectangle *r = mRects; r < mRectsEnd; r++) {
  1967             mRegion.Or(mRegion, nsIntRect(r->x, r->y, r->width, r->height));
  1968             LOGDRAW(("\t%d %d %d %d\n", r->x, r->y, r->width, r->height));
  1970         return true;
  1973 #else
  1974 # ifdef cairo_copy_clip_rectangle_list
  1975 #  error "Looks like we're including Mozilla's cairo instead of system cairo"
  1976 # endif
  1977     cairo_rectangle_list_t *mRects;
  1979     ExposeRegion() : mRects(nullptr)
  1982     ~ExposeRegion()
  1984         cairo_rectangle_list_destroy(mRects);
  1986     bool Init(cairo_t* cr)
  1988         mRects = cairo_copy_clip_rectangle_list(cr);
  1989         if (mRects->status != CAIRO_STATUS_SUCCESS) {
  1990             NS_WARNING("Failed to obtain cairo rectangle list.");
  1991             return false;
  1994         for (int i = 0; i < mRects->num_rectangles; i++)  {
  1995             const cairo_rectangle_t& r = mRects->rectangles[i];
  1996             mRegion.Or(mRegion, nsIntRect(r.x, r.y, r.width, r.height));
  1997             LOGDRAW(("\t%d %d %d %d\n", r.x, r.y, r.width, r.height));
  1999         return true;
  2001 #endif
  2002 };
  2004 #if (MOZ_WIDGET_GTK == 2)
  2005 gboolean
  2006 nsWindow::OnExposeEvent(GdkEventExpose *aEvent)
  2007 #else
  2008 gboolean
  2009 nsWindow::OnExposeEvent(cairo_t *cr)
  2010 #endif
  2012     if (mIsDestroyed) {
  2013         return FALSE;
  2016     // Windows that are not visible will be painted after they become visible.
  2017     if (!mGdkWindow || mIsFullyObscured || !mHasMappedToplevel)
  2018         return FALSE;
  2020     nsIWidgetListener *listener =
  2021         mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener;
  2022     if (!listener)
  2023         return FALSE;
  2025     ExposeRegion exposeRegion;
  2026 #if (MOZ_WIDGET_GTK == 2)
  2027     if (!exposeRegion.Init(aEvent)) {
  2028 #else
  2029     if (!exposeRegion.Init(cr)) {
  2030 #endif
  2031         return FALSE;
  2034     nsIntRegion &region = exposeRegion.mRegion;
  2036     ClientLayerManager *clientLayers =
  2037         (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT)
  2038         ? static_cast<ClientLayerManager*>(GetLayerManager())
  2039         : nullptr;
  2041     if (clientLayers && mCompositorParent) {
  2042         // We need to paint to the screen even if nothing changed, since if we
  2043         // don't have a compositing window manager, our pixels could be stale.
  2044         clientLayers->SetNeedsComposite(true);
  2045         clientLayers->SendInvalidRegion(region);
  2048     // Dispatch WillPaintWindow notification to allow scripts etc. to run
  2049     // before we paint
  2051         listener->WillPaintWindow(this);
  2053         // If the window has been destroyed during the will paint notification,
  2054         // there is nothing left to do.
  2055         if (!mGdkWindow)
  2056             return TRUE;
  2058         // Re-get the listener since the will paint notification might have
  2059         // killed it.
  2060         listener =
  2061             mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener;
  2062         if (!listener)
  2063             return FALSE;
  2066     if (clientLayers && mCompositorParent && clientLayers->NeedsComposite()) {
  2067         mCompositorParent->ScheduleRenderOnCompositorThread();
  2068         clientLayers->SetNeedsComposite(false);
  2071     LOGDRAW(("sending expose event [%p] %p 0x%lx (rects follow):\n",
  2072              (void *)this, (void *)mGdkWindow,
  2073              gdk_x11_window_get_xid(mGdkWindow)));
  2075     // Our bounds may have changed after calling WillPaintWindow.  Clip
  2076     // to the new bounds here.  The region is relative to this
  2077     // window.
  2078     region.And(region, nsIntRect(0, 0, mBounds.width, mBounds.height));
  2080     bool shaped = false;
  2081     if (eTransparencyTransparent == GetTransparencyMode()) {
  2082         GdkScreen *screen = gdk_window_get_screen(mGdkWindow);
  2083         if (gdk_screen_is_composited(screen) &&
  2084             gdk_window_get_visual(mGdkWindow) ==
  2085             gdk_screen_get_rgba_visual(screen)) {
  2086             // Remove possible shape mask from when window manger was not
  2087             // previously compositing.
  2088             static_cast<nsWindow*>(GetTopLevelWidget())->
  2089                 ClearTransparencyBitmap();
  2090         } else {
  2091             shaped = true;
  2095     if (!shaped) {
  2096         GList *children =
  2097             gdk_window_peek_children(mGdkWindow);
  2098         while (children) {
  2099             GdkWindow *gdkWin = GDK_WINDOW(children->data);
  2100             nsWindow *kid = get_window_for_gdk_window(gdkWin);
  2101             if (kid && gdk_window_is_visible(gdkWin)) {
  2102                 nsAutoTArray<nsIntRect,1> clipRects;
  2103                 kid->GetWindowClipRegion(&clipRects);
  2104                 nsIntRect bounds;
  2105                 kid->GetBounds(bounds);
  2106                 for (uint32_t i = 0; i < clipRects.Length(); ++i) {
  2107                     nsIntRect r = clipRects[i] + bounds.TopLeft();
  2108                     region.Sub(region, r);
  2111             children = children->next;
  2115     if (region.IsEmpty()) {
  2116         return TRUE;
  2119     // If this widget uses OMTC...
  2120     if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) {
  2121         listener->PaintWindow(this, region);
  2122         listener->DidPaintWindow();
  2123         return TRUE;
  2126     gfxASurface* surf;
  2127 #if (MOZ_WIDGET_GTK == 2)
  2128     surf = GetThebesSurface();
  2129 #else
  2130     surf = GetThebesSurface(cr);
  2131 #endif
  2133     nsRefPtr<gfxContext> ctx;
  2134     if (gfxPlatform::GetPlatform()->
  2135             SupportsAzureContentForType(BackendType::CAIRO)) {
  2136         IntSize intSize(surf->GetSize().width, surf->GetSize().height);
  2137         ctx = new gfxContext(gfxPlatform::GetPlatform()->
  2138             CreateDrawTargetForSurface(surf, intSize));
  2139     } else if (gfxPlatform::GetPlatform()->
  2140                    SupportsAzureContentForType(BackendType::SKIA) &&
  2141                surf->GetType() == gfxSurfaceType::Image) {
  2142        gfxImageSurface* imgSurf = static_cast<gfxImageSurface*>(surf);
  2143        SurfaceFormat format = ImageFormatToSurfaceFormat(imgSurf->Format());
  2144        IntSize intSize(surf->GetSize().width, surf->GetSize().height);
  2145        ctx = new gfxContext(gfxPlatform::GetPlatform()->CreateDrawTargetForData(
  2146            imgSurf->Data(), intSize, imgSurf->Stride(), format));
  2147     }  else {
  2148         ctx = new gfxContext(surf);
  2151 #ifdef MOZ_X11
  2152     nsIntRect boundsRect; // for shaped only
  2154     ctx->NewPath();
  2155     if (shaped) {
  2156         // Collapse update area to the bounding box. This is so we only have to
  2157         // call UpdateTranslucentWindowAlpha once. After we have dropped
  2158         // support for non-Thebes graphics, UpdateTranslucentWindowAlpha will be
  2159         // our private interface so we can rework things to avoid this.
  2160         boundsRect = region.GetBounds();
  2161         ctx->Rectangle(gfxRect(boundsRect.x, boundsRect.y,
  2162                                boundsRect.width, boundsRect.height));
  2163     } else {
  2164         gfxUtils::PathFromRegion(ctx, region);
  2166     ctx->Clip();
  2168     BufferMode layerBuffering;
  2169     if (shaped) {
  2170         // The double buffering is done here to extract the shape mask.
  2171         // (The shape mask won't be necessary when a visual with an alpha
  2172         // channel is used on compositing window managers.)
  2173         layerBuffering = mozilla::layers::BufferMode::BUFFER_NONE;
  2174         ctx->PushGroup(gfxContentType::COLOR_ALPHA);
  2175 #ifdef MOZ_HAVE_SHMIMAGE
  2176     } else if (nsShmImage::UseShm()) {
  2177         // We're using an xshm mapping as a back buffer.
  2178         layerBuffering = mozilla::layers::BufferMode::BUFFER_NONE;
  2179 #endif // MOZ_HAVE_SHMIMAGE
  2180     } else {
  2181         // Get the layer manager to do double buffering (if necessary).
  2182         layerBuffering = mozilla::layers::BufferMode::BUFFERED;
  2185 #if 0
  2186     // NOTE: Paint flashing region would be wrong for cairo, since
  2187     // cairo inflates the update region, etc.  So don't paint flash
  2188     // for cairo.
  2189 #ifdef DEBUG
  2190     // XXX aEvent->region may refer to a newly-invalid area.  FIXME
  2191     if (0 && WANT_PAINT_FLASHING && gtk_widget_get_window(aEvent))
  2192         gdk_window_flash(mGdkWindow, 1, 100, aEvent->region);
  2193 #endif
  2194 #endif
  2196 #endif // MOZ_X11
  2198     bool painted = false;
  2200       if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC) {
  2201         AutoLayerManagerSetup setupLayerManager(this, ctx, layerBuffering);
  2202         painted = listener->PaintWindow(this, region);
  2206 #ifdef MOZ_X11
  2207     // PaintWindow can Destroy us (bug 378273), avoid doing any paint
  2208     // operations below if that happened - it will lead to XError and exit().
  2209     if (shaped) {
  2210         if (MOZ_LIKELY(!mIsDestroyed)) {
  2211             if (painted) {
  2212                 nsRefPtr<gfxPattern> pattern = ctx->PopGroup();
  2214                 UpdateAlpha(pattern, boundsRect);
  2216                 ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
  2217                 ctx->SetPattern(pattern);
  2218                 ctx->Paint();
  2222 #  ifdef MOZ_HAVE_SHMIMAGE
  2223     if (mShmImage && MOZ_LIKELY(!mIsDestroyed)) {
  2224 #if (MOZ_WIDGET_GTK == 2)
  2225         mShmImage->Put(mGdkWindow, exposeRegion.mRects, exposeRegion.mRectsEnd);
  2226 #else
  2227         mShmImage->Put(mGdkWindow, exposeRegion.mRects);
  2228 #endif
  2230 #  endif  // MOZ_HAVE_SHMIMAGE
  2231 #endif // MOZ_X11
  2233     listener->DidPaintWindow();
  2235     // Synchronously flush any new dirty areas
  2236 #if (MOZ_WIDGET_GTK == 2)
  2237     GdkRegion* dirtyArea = gdk_window_get_update_area(mGdkWindow);
  2238 #else
  2239     cairo_region_t* dirtyArea = gdk_window_get_update_area(mGdkWindow);
  2240 #endif
  2242     if (dirtyArea) {
  2243         gdk_window_invalidate_region(mGdkWindow, dirtyArea, false);
  2244 #if (MOZ_WIDGET_GTK == 2)
  2245         gdk_region_destroy(dirtyArea);
  2246 #else
  2247         cairo_region_destroy(dirtyArea);
  2248 #endif
  2249         gdk_window_process_updates(mGdkWindow, false);
  2252     // check the return value!
  2253     return TRUE;
  2256 void
  2257 nsWindow::UpdateAlpha(gfxPattern* aPattern, nsIntRect aBoundsRect)
  2259     // We need to create our own buffer to force the stride to match the
  2260     // expected stride.
  2261     int32_t stride = GetAlignedStride<4>(BytesPerPixel(SurfaceFormat::A8) *
  2262                                          aBoundsRect.width);
  2263     int32_t bufferSize = stride * aBoundsRect.height;
  2264     nsAutoArrayPtr<uint8_t> imageBuffer(new (std::nothrow) uint8_t[bufferSize]);
  2265     RefPtr<DrawTarget> drawTarget = gfxPlatform::GetPlatform()->
  2266         CreateDrawTargetForData(imageBuffer, ToIntSize(aBoundsRect.Size()),
  2267                                 stride, SurfaceFormat::A8);
  2269     if (drawTarget) {
  2270         drawTarget->FillRect(Rect(0, 0, aBoundsRect.width, aBoundsRect.height),
  2271                              *aPattern->GetPattern(drawTarget),
  2272                              DrawOptions(1.0, CompositionOp::OP_SOURCE));
  2274     UpdateTranslucentWindowAlphaInternal(aBoundsRect, imageBuffer, stride);
  2277 gboolean
  2278 nsWindow::OnConfigureEvent(GtkWidget *aWidget, GdkEventConfigure *aEvent)
  2280     // These events are only received on toplevel windows.
  2281     //
  2282     // GDK ensures that the coordinates are the client window top-left wrt the
  2283     // root window.
  2284     //
  2285     //   GDK calculates the cordinates for real ConfigureNotify events on
  2286     //   managed windows (that would normally be relative to the parent
  2287     //   window).
  2288     //
  2289     //   Synthetic ConfigureNotify events are from the window manager and
  2290     //   already relative to the root window.  GDK creates all X windows with
  2291     //   border_width = 0, so synthetic events also indicate the top-left of
  2292     //   the client window.
  2293     //
  2294     //   Override-redirect windows are children of the root window so parent
  2295     //   coordinates are root coordinates.
  2297     LOG(("configure event [%p] %d %d %d %d\n", (void *)this,
  2298          aEvent->x, aEvent->y, aEvent->width, aEvent->height));
  2300     nsIntRect screenBounds;
  2301     GetScreenBounds(screenBounds);
  2303     if (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog) {
  2304         // This check avoids unwanted rollup on spurious configure events from
  2305         // Cygwin/X (bug 672103).
  2306         if (mBounds.x != screenBounds.x ||
  2307             mBounds.y != screenBounds.y) {
  2308             CheckForRollup(0, 0, false, true);
  2312     // This event indicates that the window position may have changed.
  2313     // mBounds.Size() is updated in OnSizeAllocate().
  2315     // (The gtk_window_get_window_type() function is only available from
  2316     // version 2.20.)
  2317     NS_ASSERTION(GTK_IS_WINDOW(aWidget),
  2318                  "Configure event on widget that is not a GtkWindow");
  2319     gint type;
  2320     g_object_get(aWidget, "type", &type, nullptr);
  2321     if (type == GTK_WINDOW_POPUP) {
  2322         // Override-redirect window
  2323         //
  2324         // These windows should not be moved by the window manager, and so any
  2325         // change in position is a result of our direction.  mBounds has
  2326         // already been set in Move() or Resize(), and that is more
  2327         // up-to-date than the position in the ConfigureNotify event if the
  2328         // event is from an earlier window move.
  2329         //
  2330         // Skipping the WindowMoved call saves context menus from an infinite
  2331         // loop when nsXULPopupManager::PopupMoved moves the window to the new
  2332         // position and nsMenuPopupFrame::SetPopupPosition adds
  2333         // offsetForContextMenu on each iteration.
  2334         return FALSE;
  2337     mBounds.MoveTo(screenBounds.TopLeft());
  2339     // XXX mozilla will invalidate the entire window after this move
  2340     // complete.  wtf?
  2341     NotifyWindowMoved(mBounds.x, mBounds.y);
  2343     return FALSE;
  2346 void
  2347 nsWindow::OnContainerUnrealize()
  2349     // The GdkWindows are about to be destroyed (but not deleted), so remove
  2350     // their references back to their container widget while the GdkWindow
  2351     // hierarchy is still available.
  2353     if (mGdkWindow) {
  2354         DestroyChildWindows();
  2356         g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr);
  2357         mGdkWindow = nullptr;
  2361 void
  2362 nsWindow::OnSizeAllocate(GtkAllocation *aAllocation)
  2364     LOG(("size_allocate [%p] %d %d %d %d\n",
  2365          (void *)this, aAllocation->x, aAllocation->y,
  2366          aAllocation->width, aAllocation->height));
  2368     nsIntSize size(aAllocation->width, aAllocation->height);
  2369     if (mBounds.Size() == size)
  2370         return;
  2372     // Invalidate the new part of the window now for the pending paint to
  2373     // minimize background flashes (GDK does not do this for external resizes
  2374     // of toplevels.)
  2375     if (mBounds.width < size.width) {
  2376         GdkRectangle rect =
  2377             { mBounds.width, 0, size.width - mBounds.width, size.height };
  2378         gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
  2380     if (mBounds.height < size.height) {
  2381         GdkRectangle rect =
  2382             { 0, mBounds.height, size.width, size.height - mBounds.height };
  2383         gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
  2386     mBounds.SizeTo(size);
  2388     if (!mGdkWindow)
  2389         return;
  2391     DispatchResized(size.width, size.height);
  2394 void
  2395 nsWindow::OnDeleteEvent()
  2397     if (mWidgetListener)
  2398         mWidgetListener->RequestWindowClose(this);
  2401 void
  2402 nsWindow::OnEnterNotifyEvent(GdkEventCrossing *aEvent)
  2404     // This skips NotifyVirtual and NotifyNonlinearVirtual enter notify events
  2405     // when the pointer enters a child window.  If the destination window is a
  2406     // Gecko window then we'll catch the corresponding event on that window,
  2407     // but we won't notice when the pointer directly enters a foreign (plugin)
  2408     // child window without passing over a visible portion of a Gecko window.
  2409     if (aEvent->subwindow != nullptr)
  2410         return;
  2412     // Check before is_parent_ungrab_enter() as the button state may have
  2413     // changed while a non-Gecko ancestor window had a pointer grab.
  2414     DispatchMissedButtonReleases(aEvent);
  2416     if (is_parent_ungrab_enter(aEvent))
  2417         return;
  2419     WidgetMouseEvent event(true, NS_MOUSE_ENTER, this, WidgetMouseEvent::eReal);
  2421     event.refPoint.x = nscoord(aEvent->x);
  2422     event.refPoint.y = nscoord(aEvent->y);
  2424     event.time = aEvent->time;
  2426     LOG(("OnEnterNotify: %p\n", (void *)this));
  2428     nsEventStatus status;
  2429     DispatchEvent(&event, status);
  2432 // XXX Is this the right test for embedding cases?
  2433 static bool
  2434 is_top_level_mouse_exit(GdkWindow* aWindow, GdkEventCrossing *aEvent)
  2436     gint x = gint(aEvent->x_root);
  2437     gint y = gint(aEvent->y_root);
  2438     GdkDisplay* display = gdk_window_get_display(aWindow);
  2439     GdkWindow* winAtPt = gdk_display_get_window_at_pointer(display, &x, &y);
  2440     if (!winAtPt)
  2441         return true;
  2442     GdkWindow* topLevelAtPt = gdk_window_get_toplevel(winAtPt);
  2443     GdkWindow* topLevelWidget = gdk_window_get_toplevel(aWindow);
  2444     return topLevelAtPt != topLevelWidget;
  2447 void
  2448 nsWindow::OnLeaveNotifyEvent(GdkEventCrossing *aEvent)
  2450     // This ignores NotifyVirtual and NotifyNonlinearVirtual leave notify
  2451     // events when the pointer leaves a child window.  If the destination
  2452     // window is a Gecko window then we'll catch the corresponding event on
  2453     // that window.
  2454     //
  2455     // XXXkt However, we will miss toplevel exits when the pointer directly
  2456     // leaves a foreign (plugin) child window without passing over a visible
  2457     // portion of a Gecko window.
  2458     if (aEvent->subwindow != nullptr)
  2459         return;
  2461     WidgetMouseEvent event(true, NS_MOUSE_EXIT, this, WidgetMouseEvent::eReal);
  2463     event.refPoint.x = nscoord(aEvent->x);
  2464     event.refPoint.y = nscoord(aEvent->y);
  2466     event.time = aEvent->time;
  2468     event.exit = is_top_level_mouse_exit(mGdkWindow, aEvent)
  2469         ? WidgetMouseEvent::eTopLevel : WidgetMouseEvent::eChild;
  2471     LOG(("OnLeaveNotify: %p\n", (void *)this));
  2473     nsEventStatus status;
  2474     DispatchEvent(&event, status);
  2477 void
  2478 nsWindow::OnMotionNotifyEvent(GdkEventMotion *aEvent)
  2480     // see if we can compress this event
  2481     // XXXldb Why skip every other motion event when we have multiple,
  2482     // but not more than that?
  2483     bool synthEvent = false;
  2484 #ifdef MOZ_X11
  2485     XEvent xevent;
  2487     while (XPending (GDK_WINDOW_XDISPLAY(aEvent->window))) {
  2488         XEvent peeked;
  2489         XPeekEvent (GDK_WINDOW_XDISPLAY(aEvent->window), &peeked);
  2490         if (peeked.xany.window != gdk_x11_window_get_xid(aEvent->window)
  2491             || peeked.type != MotionNotify)
  2492             break;
  2494         synthEvent = true;
  2495         XNextEvent (GDK_WINDOW_XDISPLAY(aEvent->window), &xevent);
  2497 #if (MOZ_WIDGET_GTK == 2)
  2498     // if plugins still keeps the focus, get it back
  2499     if (gPluginFocusWindow && gPluginFocusWindow != this) {
  2500         nsRefPtr<nsWindow> kungFuDeathGrip = gPluginFocusWindow;
  2501         gPluginFocusWindow->LoseNonXEmbedPluginFocus();
  2503 #endif /* MOZ_WIDGET_GTK2 */
  2504 #endif /* MOZ_X11 */
  2506     WidgetMouseEvent event(true, NS_MOUSE_MOVE, this, WidgetMouseEvent::eReal);
  2508     gdouble pressure = 0;
  2509     gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
  2510     // Sometime gdk generate 0 pressure value between normal values
  2511     // We have to ignore that and use last valid value
  2512     if (pressure)
  2513       mLastMotionPressure = pressure;
  2514     event.pressure = mLastMotionPressure;
  2516     guint modifierState;
  2517     if (synthEvent) {
  2518 #ifdef MOZ_X11
  2519         event.refPoint.x = nscoord(xevent.xmotion.x);
  2520         event.refPoint.y = nscoord(xevent.xmotion.y);
  2522         modifierState = xevent.xmotion.state;
  2524         event.time = xevent.xmotion.time;
  2525 #else
  2526         event.refPoint.x = nscoord(aEvent->x);
  2527         event.refPoint.y = nscoord(aEvent->y);
  2529         modifierState = aEvent->state;
  2531         event.time = aEvent->time;
  2532 #endif /* MOZ_X11 */
  2534     else {
  2535         // XXX see OnScrollEvent()
  2536         if (aEvent->window == mGdkWindow) {
  2537             event.refPoint.x = nscoord(aEvent->x);
  2538             event.refPoint.y = nscoord(aEvent->y);
  2539         } else {
  2540             LayoutDeviceIntPoint point(NSToIntFloor(aEvent->x_root),
  2541                                        NSToIntFloor(aEvent->y_root));
  2542             event.refPoint = point -
  2543                 LayoutDeviceIntPoint::FromUntyped(WidgetToScreenOffset());
  2546         modifierState = aEvent->state;
  2548         event.time = aEvent->time;
  2551     KeymapWrapper::InitInputEvent(event, modifierState);
  2553     nsEventStatus status;
  2554     DispatchEvent(&event, status);
  2557 // If the automatic pointer grab on ButtonPress has deactivated before
  2558 // ButtonRelease, and the mouse button is released while the pointer is not
  2559 // over any a Gecko window, then the ButtonRelease event will not be received.
  2560 // (A similar situation exists when the pointer is grabbed with owner_events
  2561 // True as the ButtonRelease may be received on a foreign [plugin] window).
  2562 // Use this method to check for released buttons when the pointer returns to a
  2563 // Gecko window.
  2564 void
  2565 nsWindow::DispatchMissedButtonReleases(GdkEventCrossing *aGdkEvent)
  2567     guint changed = aGdkEvent->state ^ gButtonState;
  2568     // Only consider button releases.
  2569     // (Ignore button presses that occurred outside Gecko.)
  2570     guint released = changed & gButtonState;
  2571     gButtonState = aGdkEvent->state;
  2573     // Loop over each button, excluding mouse wheel buttons 4 and 5 for which
  2574     // GDK ignores releases.
  2575     for (guint buttonMask = GDK_BUTTON1_MASK;
  2576          buttonMask <= GDK_BUTTON3_MASK;
  2577          buttonMask <<= 1) {
  2579         if (released & buttonMask) {
  2580             int16_t buttonType;
  2581             switch (buttonMask) {
  2582             case GDK_BUTTON1_MASK:
  2583                 buttonType = WidgetMouseEvent::eLeftButton;
  2584                 break;
  2585             case GDK_BUTTON2_MASK:
  2586                 buttonType = WidgetMouseEvent::eMiddleButton;
  2587                 break;
  2588             default:
  2589                 NS_ASSERTION(buttonMask == GDK_BUTTON3_MASK,
  2590                              "Unexpected button mask");
  2591                 buttonType = WidgetMouseEvent::eRightButton;
  2594             LOG(("Synthesized button %u release on %p\n",
  2595                  guint(buttonType + 1), (void *)this));
  2597             // Dispatch a synthesized button up event to tell Gecko about the
  2598             // change in state.  This event is marked as synthesized so that
  2599             // it is not dispatched as a DOM event, because we don't know the
  2600             // position, widget, modifiers, or time/order.
  2601             WidgetMouseEvent synthEvent(true, NS_MOUSE_BUTTON_UP, this,
  2602                                         WidgetMouseEvent::eSynthesized);
  2603             synthEvent.button = buttonType;
  2604             nsEventStatus status;
  2605             DispatchEvent(&synthEvent, status);
  2610 void
  2611 nsWindow::InitButtonEvent(WidgetMouseEvent& aEvent,
  2612                           GdkEventButton* aGdkEvent)
  2614     // XXX see OnScrollEvent()
  2615     if (aGdkEvent->window == mGdkWindow) {
  2616         aEvent.refPoint.x = nscoord(aGdkEvent->x);
  2617         aEvent.refPoint.y = nscoord(aGdkEvent->y);
  2618     } else {
  2619         LayoutDeviceIntPoint point(NSToIntFloor(aGdkEvent->x_root),
  2620                                    NSToIntFloor(aGdkEvent->y_root));
  2621         aEvent.refPoint = point -
  2622             LayoutDeviceIntPoint::FromUntyped(WidgetToScreenOffset());
  2625     guint modifierState = aGdkEvent->state;
  2626     // aEvent's state doesn't include this event's information.  Therefore,
  2627     // if aEvent is mouse button down event, we need to set it manually.
  2628     // Note that we cannot do same thing for NS_MOUSE_BUTTON_UP because
  2629     // system may have two or more mice and same button of another mouse
  2630     // may be still pressed.
  2631     if (aGdkEvent->type != GDK_BUTTON_RELEASE) {
  2632         switch (aGdkEvent->button) {
  2633             case 1:
  2634                 modifierState |= GDK_BUTTON1_MASK;
  2635                 break;
  2636             case 2:
  2637                 modifierState |= GDK_BUTTON2_MASK;
  2638                 break;
  2639             case 3:
  2640                 modifierState |= GDK_BUTTON3_MASK;
  2641                 break;
  2645     KeymapWrapper::InitInputEvent(aEvent, modifierState);
  2647     aEvent.time = aGdkEvent->time;
  2649     switch (aGdkEvent->type) {
  2650     case GDK_2BUTTON_PRESS:
  2651         aEvent.clickCount = 2;
  2652         break;
  2653     case GDK_3BUTTON_PRESS:
  2654         aEvent.clickCount = 3;
  2655         break;
  2656         // default is one click
  2657     default:
  2658         aEvent.clickCount = 1;
  2662 static guint ButtonMaskFromGDKButton(guint button)
  2664     return GDK_BUTTON1_MASK << (button - 1);
  2667 void
  2668 nsWindow::OnButtonPressEvent(GdkEventButton *aEvent)
  2670     LOG(("Button %u press on %p\n", aEvent->button, (void *)this));
  2672     nsEventStatus status;
  2674     // If you double click in GDK, it will actually generate a second
  2675     // GDK_BUTTON_PRESS before sending the GDK_2BUTTON_PRESS, and this is
  2676     // different than the DOM spec.  GDK puts this in the queue
  2677     // programatically, so it's safe to assume that if there's a
  2678     // double click in the queue, it was generated so we can just drop
  2679     // this click.
  2680     GdkEvent *peekedEvent = gdk_event_peek();
  2681     if (peekedEvent) {
  2682         GdkEventType type = peekedEvent->any.type;
  2683         gdk_event_free(peekedEvent);
  2684         if (type == GDK_2BUTTON_PRESS || type == GDK_3BUTTON_PRESS)
  2685             return;
  2688     nsWindow *containerWindow = GetContainerWindow();
  2689     if (!gFocusWindow && containerWindow) {
  2690         containerWindow->DispatchActivateEvent();
  2693     // check to see if we should rollup
  2694     if (CheckForRollup(aEvent->x_root, aEvent->y_root, false, false))
  2695         return;
  2697     gdouble pressure = 0;
  2698     gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
  2699     mLastMotionPressure = pressure;
  2701     uint16_t domButton;
  2702     switch (aEvent->button) {
  2703     case 1:
  2704         domButton = WidgetMouseEvent::eLeftButton;
  2705         break;
  2706     case 2:
  2707         domButton = WidgetMouseEvent::eMiddleButton;
  2708         break;
  2709     case 3:
  2710         domButton = WidgetMouseEvent::eRightButton;
  2711         break;
  2712     // These are mapped to horizontal scroll
  2713     case 6:
  2714     case 7:
  2715         NS_WARNING("We're not supporting legacy horizontal scroll event");
  2716         return;
  2717     // Map buttons 8-9 to back/forward
  2718     case 8:
  2719         DispatchCommandEvent(nsGkAtoms::Back);
  2720         return;
  2721     case 9:
  2722         DispatchCommandEvent(nsGkAtoms::Forward);
  2723         return;
  2724     default:
  2725         return;
  2728     gButtonState |= ButtonMaskFromGDKButton(aEvent->button);
  2730     WidgetMouseEvent event(true, NS_MOUSE_BUTTON_DOWN, this,
  2731                            WidgetMouseEvent::eReal);
  2732     event.button = domButton;
  2733     InitButtonEvent(event, aEvent);
  2734     event.pressure = mLastMotionPressure;
  2736     DispatchEvent(&event, status);
  2738     // right menu click on linux should also pop up a context menu
  2739     if (domButton == WidgetMouseEvent::eRightButton &&
  2740         MOZ_LIKELY(!mIsDestroyed)) {
  2741         WidgetMouseEvent contextMenuEvent(true, NS_CONTEXTMENU, this,
  2742                                           WidgetMouseEvent::eReal);
  2743         InitButtonEvent(contextMenuEvent, aEvent);
  2744         contextMenuEvent.pressure = mLastMotionPressure;
  2745         DispatchEvent(&contextMenuEvent, status);
  2749 void
  2750 nsWindow::OnButtonReleaseEvent(GdkEventButton *aEvent)
  2752     LOG(("Button %u release on %p\n", aEvent->button, (void *)this));
  2754     uint16_t domButton;
  2755     switch (aEvent->button) {
  2756     case 1:
  2757         domButton = WidgetMouseEvent::eLeftButton;
  2758         break;
  2759     case 2:
  2760         domButton = WidgetMouseEvent::eMiddleButton;
  2761         break;
  2762     case 3:
  2763         domButton = WidgetMouseEvent::eRightButton;
  2764         break;
  2765     default:
  2766         return;
  2769     gButtonState &= ~ButtonMaskFromGDKButton(aEvent->button);
  2771     WidgetMouseEvent event(true, NS_MOUSE_BUTTON_UP, this,
  2772                            WidgetMouseEvent::eReal);
  2773     event.button = domButton;
  2774     InitButtonEvent(event, aEvent);
  2775     gdouble pressure = 0;
  2776     gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
  2777     event.pressure = pressure ? pressure : mLastMotionPressure;
  2779     nsEventStatus status;
  2780     DispatchEvent(&event, status);
  2781     mLastMotionPressure = pressure;
  2784 void
  2785 nsWindow::OnContainerFocusInEvent(GdkEventFocus *aEvent)
  2787     LOGFOCUS(("OnContainerFocusInEvent [%p]\n", (void *)this));
  2789     // Unset the urgency hint, if possible
  2790     GtkWidget* top_window = GetToplevelWidget();
  2791     if (top_window && (gtk_widget_get_visible(top_window)))
  2792         SetUrgencyHint(top_window, false);
  2794     // Return if being called within SetFocus because the focus manager
  2795     // already knows that the window is active.
  2796     if (gBlockActivateEvent) {
  2797         LOGFOCUS(("activated notification is blocked [%p]\n", (void *)this));
  2798         return;
  2801     // If keyboard input will be accepted, the focus manager will call
  2802     // SetFocus to set the correct window.
  2803     gFocusWindow = nullptr;
  2805     DispatchActivateEvent();
  2807     if (!gFocusWindow) {
  2808         // We don't really have a window for dispatching key events, but
  2809         // setting a non-nullptr value here prevents OnButtonPressEvent() from
  2810         // dispatching an activation notification if the widget is already
  2811         // active.
  2812         gFocusWindow = this;
  2815     LOGFOCUS(("Events sent from focus in event [%p]\n", (void *)this));
  2818 void
  2819 nsWindow::OnContainerFocusOutEvent(GdkEventFocus *aEvent)
  2821     LOGFOCUS(("OnContainerFocusOutEvent [%p]\n", (void *)this));
  2823     if (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog) {
  2824         nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
  2825         nsCOMPtr<nsIDragSession> dragSession;
  2826         dragService->GetCurrentSession(getter_AddRefs(dragSession));
  2828         // Rollup popups when a window is focused out unless a drag is occurring.
  2829         // This check is because drags grab the keyboard and cause a focus out on
  2830         // versions of GTK before 2.18.
  2831         bool shouldRollup = !dragSession;
  2832         if (!shouldRollup) {
  2833             // we also roll up when a drag is from a different application
  2834             nsCOMPtr<nsIDOMNode> sourceNode;
  2835             dragSession->GetSourceNode(getter_AddRefs(sourceNode));
  2836             shouldRollup = (sourceNode == nullptr);
  2839         if (shouldRollup) {
  2840             CheckForRollup(0, 0, false, true);
  2844 #if (MOZ_WIDGET_GTK == 2) && defined(MOZ_X11)
  2845     // plugin lose focus
  2846     if (gPluginFocusWindow) {
  2847         nsRefPtr<nsWindow> kungFuDeathGrip = gPluginFocusWindow;
  2848         gPluginFocusWindow->LoseNonXEmbedPluginFocus();
  2850 #endif /* MOZ_X11 && MOZ_WIDGET_GTK2 */
  2852     if (gFocusWindow) {
  2853         nsRefPtr<nsWindow> kungFuDeathGrip = gFocusWindow;
  2854         if (gFocusWindow->mIMModule) {
  2855             gFocusWindow->mIMModule->OnBlurWindow(gFocusWindow);
  2857         gFocusWindow = nullptr;
  2860     DispatchDeactivateEvent();
  2862     LOGFOCUS(("Done with container focus out [%p]\n", (void *)this));
  2865 bool
  2866 nsWindow::DispatchCommandEvent(nsIAtom* aCommand)
  2868     nsEventStatus status;
  2869     WidgetCommandEvent event(true, nsGkAtoms::onAppCommand, aCommand, this);
  2870     DispatchEvent(&event, status);
  2871     return TRUE;
  2874 bool
  2875 nsWindow::DispatchContentCommandEvent(int32_t aMsg)
  2877   nsEventStatus status;
  2878   WidgetContentCommandEvent event(true, aMsg, this);
  2879   DispatchEvent(&event, status);
  2880   return TRUE;
  2883 static bool
  2884 IsCtrlAltTab(GdkEventKey *aEvent)
  2886     return aEvent->keyval == GDK_Tab &&
  2887         KeymapWrapper::AreModifiersActive(
  2888             KeymapWrapper::CTRL | KeymapWrapper::ALT, aEvent->state);
  2891 bool
  2892 nsWindow::DispatchKeyDownEvent(GdkEventKey *aEvent, bool *aCancelled)
  2894     NS_PRECONDITION(aCancelled, "aCancelled must not be null");
  2896     *aCancelled = false;
  2898     if (IsCtrlAltTab(aEvent)) {
  2899         return false;
  2902     // send the key down event
  2903     nsEventStatus status;
  2904     WidgetKeyboardEvent downEvent(true, NS_KEY_DOWN, this);
  2905     KeymapWrapper::InitKeyEvent(downEvent, aEvent);
  2906     DispatchEvent(&downEvent, status);
  2907     *aCancelled = (status == nsEventStatus_eConsumeNoDefault);
  2908     return true;
  2911 gboolean
  2912 nsWindow::OnKeyPressEvent(GdkEventKey *aEvent)
  2914     LOGFOCUS(("OnKeyPressEvent [%p]\n", (void *)this));
  2916     // if we are in the middle of composing text, XIM gets to see it
  2917     // before mozilla does.
  2918     bool IMEWasEnabled = false;
  2919     if (mIMModule) {
  2920         IMEWasEnabled = mIMModule->IsEnabled();
  2921         if (mIMModule->OnKeyEvent(this, aEvent)) {
  2922             return TRUE;
  2926     nsEventStatus status;
  2928     // work around for annoying things.
  2929     if (IsCtrlAltTab(aEvent)) {
  2930         return TRUE;
  2933     nsCOMPtr<nsIWidget> kungFuDeathGrip = this;
  2935     // Dispatch keydown event always.  At auto repeating, we should send
  2936     // KEYDOWN -> KEYPRESS -> KEYDOWN -> KEYPRESS ... -> KEYUP
  2937     // However, old distributions (e.g., Ubuntu 9.10) sent native key
  2938     // release event, so, on such platform, the DOM events will be:
  2939     // KEYDOWN -> KEYPRESS -> KEYUP -> KEYDOWN -> KEYPRESS -> KEYUP...
  2941     bool isKeyDownCancelled = false;
  2942     if (DispatchKeyDownEvent(aEvent, &isKeyDownCancelled) &&
  2943         (MOZ_UNLIKELY(mIsDestroyed) || isKeyDownCancelled)) {
  2944         return TRUE;
  2947     // If a keydown event handler causes to enable IME, i.e., it moves
  2948     // focus from IME unusable content to IME usable editor, we should
  2949     // send the native key event to IME for the first input on the editor.
  2950     if (!IMEWasEnabled && mIMModule && mIMModule->IsEnabled()) {
  2951         // Notice our keydown event was already dispatched.  This prevents
  2952         // unnecessary DOM keydown event in the editor.
  2953         if (mIMModule->OnKeyEvent(this, aEvent, true)) {
  2954             return TRUE;
  2958     // Don't pass modifiers as NS_KEY_PRESS events.
  2959     // TODO: Instead of selectively excluding some keys from NS_KEY_PRESS events,
  2960     //       we should instead selectively include (as per MSDN spec; no official
  2961     //       spec covers KeyPress events).
  2962     if (!KeymapWrapper::IsKeyPressEventNecessary(aEvent)) {
  2963         return TRUE;
  2966 #ifdef MOZ_X11
  2967 #if ! defined AIX // no XFree86 on AIX 5L
  2968     // Look for specialized app-command keys
  2969     switch (aEvent->keyval) {
  2970         case XF86XK_Back:
  2971             return DispatchCommandEvent(nsGkAtoms::Back);
  2972         case XF86XK_Forward:
  2973             return DispatchCommandEvent(nsGkAtoms::Forward);
  2974         case XF86XK_Refresh:
  2975             return DispatchCommandEvent(nsGkAtoms::Reload);
  2976         case XF86XK_Stop:
  2977             return DispatchCommandEvent(nsGkAtoms::Stop);
  2978         case XF86XK_Search:
  2979             return DispatchCommandEvent(nsGkAtoms::Search);
  2980         case XF86XK_Favorites:
  2981             return DispatchCommandEvent(nsGkAtoms::Bookmarks);
  2982         case XF86XK_HomePage:
  2983             return DispatchCommandEvent(nsGkAtoms::Home);
  2984         case XF86XK_Copy:
  2985         case GDK_F16:  // F16, F20, F18, F14 are old keysyms for Copy Cut Paste Undo
  2986             return DispatchContentCommandEvent(NS_CONTENT_COMMAND_COPY);
  2987         case XF86XK_Cut:
  2988         case GDK_F20:
  2989             return DispatchContentCommandEvent(NS_CONTENT_COMMAND_CUT);
  2990         case XF86XK_Paste:
  2991         case GDK_F18:
  2992             return DispatchContentCommandEvent(NS_CONTENT_COMMAND_PASTE);
  2993         case GDK_Redo:
  2994             return DispatchContentCommandEvent(NS_CONTENT_COMMAND_REDO);
  2995         case GDK_Undo:
  2996         case GDK_F14:
  2997             return DispatchContentCommandEvent(NS_CONTENT_COMMAND_UNDO);
  2999 #endif /* ! AIX */
  3000 #endif /* MOZ_X11 */
  3002     WidgetKeyboardEvent event(true, NS_KEY_PRESS, this);
  3003     KeymapWrapper::InitKeyEvent(event, aEvent);
  3005     // before we dispatch a key, check if it's the context menu key.
  3006     // If so, send a context menu key event instead.
  3007     if (is_context_menu_key(event)) {
  3008         WidgetMouseEvent contextMenuEvent(true, NS_CONTEXTMENU, this,
  3009                                           WidgetMouseEvent::eReal,
  3010                                           WidgetMouseEvent::eContextMenuKey);
  3012         contextMenuEvent.refPoint = LayoutDeviceIntPoint(0, 0);
  3013         contextMenuEvent.time = aEvent->time;
  3014         contextMenuEvent.clickCount = 1;
  3015         KeymapWrapper::InitInputEvent(contextMenuEvent, aEvent->state);
  3016         DispatchEvent(&contextMenuEvent, status);
  3018     else {
  3019         // If the character code is in the BMP, send the key press event.
  3020         // Otherwise, send a text event with the equivalent UTF-16 string.
  3021         if (IS_IN_BMP(event.charCode)) {
  3022             DispatchEvent(&event, status);
  3024         else {
  3025             WidgetTextEvent textEvent(true, NS_TEXT_TEXT, this);
  3026             char16_t textString[3];
  3027             textString[0] = H_SURROGATE(event.charCode);
  3028             textString[1] = L_SURROGATE(event.charCode);
  3029             textString[2] = 0;
  3030             textEvent.theText = textString;
  3031             textEvent.time = event.time;
  3032             DispatchEvent(&textEvent, status);
  3036     // If the event was consumed, return.
  3037     if (status == nsEventStatus_eConsumeNoDefault) {
  3038         return TRUE;
  3041     return FALSE;
  3044 gboolean
  3045 nsWindow::OnKeyReleaseEvent(GdkEventKey *aEvent)
  3047     LOGFOCUS(("OnKeyReleaseEvent [%p]\n", (void *)this));
  3049     if (mIMModule && mIMModule->OnKeyEvent(this, aEvent)) {
  3050         return TRUE;
  3053     // send the key event as a key up event
  3054     WidgetKeyboardEvent event(true, NS_KEY_UP, this);
  3055     KeymapWrapper::InitKeyEvent(event, aEvent);
  3057     nsEventStatus status;
  3058     DispatchEvent(&event, status);
  3060     // If the event was consumed, return.
  3061     if (status == nsEventStatus_eConsumeNoDefault) {
  3062         return TRUE;
  3065     return FALSE;
  3068 void
  3069 nsWindow::OnScrollEvent(GdkEventScroll *aEvent)
  3071     // check to see if we should rollup
  3072     if (CheckForRollup(aEvent->x_root, aEvent->y_root, true, false))
  3073         return;
  3075     WidgetWheelEvent wheelEvent(true, NS_WHEEL_WHEEL, this);
  3076     wheelEvent.deltaMode = nsIDOMWheelEvent::DOM_DELTA_LINE;
  3077     switch (aEvent->direction) {
  3078     case GDK_SCROLL_UP:
  3079         wheelEvent.deltaY = wheelEvent.lineOrPageDeltaY = -3;
  3080         break;
  3081     case GDK_SCROLL_DOWN:
  3082         wheelEvent.deltaY = wheelEvent.lineOrPageDeltaY = 3;
  3083         break;
  3084     case GDK_SCROLL_LEFT:
  3085         wheelEvent.deltaX = wheelEvent.lineOrPageDeltaX = -1;
  3086         break;
  3087     case GDK_SCROLL_RIGHT:
  3088         wheelEvent.deltaX = wheelEvent.lineOrPageDeltaX = 1;
  3089         break;
  3092     NS_ASSERTION(wheelEvent.deltaX || wheelEvent.deltaY,
  3093                  "deltaX or deltaY must be non-zero");
  3095     if (aEvent->window == mGdkWindow) {
  3096         // we are the window that the event happened on so no need for expensive WidgetToScreenOffset
  3097         wheelEvent.refPoint.x = nscoord(aEvent->x);
  3098         wheelEvent.refPoint.y = nscoord(aEvent->y);
  3099     } else {
  3100         // XXX we're never quite sure which GdkWindow the event came from due to our custom bubbling
  3101         // in scroll_event_cb(), so use ScreenToWidget to translate the screen root coordinates into
  3102         // coordinates relative to this widget.
  3103         LayoutDeviceIntPoint point(NSToIntFloor(aEvent->x_root),
  3104                                    NSToIntFloor(aEvent->y_root));
  3105         wheelEvent.refPoint = point -
  3106             LayoutDeviceIntPoint::FromUntyped(WidgetToScreenOffset());
  3109     KeymapWrapper::InitInputEvent(wheelEvent, aEvent->state);
  3111     wheelEvent.time = aEvent->time;
  3113     nsEventStatus status;
  3114     DispatchEvent(&wheelEvent, status);
  3117 void
  3118 nsWindow::OnVisibilityNotifyEvent(GdkEventVisibility *aEvent)
  3120     LOGDRAW(("Visibility event %i on [%p] %p\n",
  3121              aEvent->state, this, aEvent->window));
  3123     if (!mGdkWindow)
  3124         return;
  3126     switch (aEvent->state) {
  3127     case GDK_VISIBILITY_UNOBSCURED:
  3128     case GDK_VISIBILITY_PARTIAL:
  3129         if (mIsFullyObscured && mHasMappedToplevel) {
  3130             // GDK_EXPOSE events have been ignored, so make sure GDK
  3131             // doesn't think that the window has already been painted.
  3132             gdk_window_invalidate_rect(mGdkWindow, nullptr, FALSE);
  3135         mIsFullyObscured = false;
  3137         if (!nsGtkIMModule::IsVirtualKeyboardOpened()) {
  3138             // if we have to retry the grab, retry it.
  3139             EnsureGrabs();
  3141         break;
  3142     default: // includes GDK_VISIBILITY_FULLY_OBSCURED
  3143         mIsFullyObscured = true;
  3144         break;
  3148 void
  3149 nsWindow::OnWindowStateEvent(GtkWidget *aWidget, GdkEventWindowState *aEvent)
  3151     LOG(("nsWindow::OnWindowStateEvent [%p] changed %d new_window_state %d\n",
  3152          (void *)this, aEvent->changed_mask, aEvent->new_window_state));
  3154     if (IS_MOZ_CONTAINER(aWidget)) {
  3155         // This event is notifying the container widget of changes to the
  3156         // toplevel window.  Just detect changes affecting whether windows are
  3157         // viewable.
  3158         //
  3159         // (A visibility notify event is sent to each window that becomes
  3160         // viewable when the toplevel is mapped, but we can't rely on that for
  3161         // setting mHasMappedToplevel because these toplevel window state
  3162         // events are asynchronous.  The windows in the hierarchy now may not
  3163         // be the same windows as when the toplevel was mapped, so they may
  3164         // not get VisibilityNotify events.)
  3165         bool mapped =
  3166             !(aEvent->new_window_state &
  3167               (GDK_WINDOW_STATE_ICONIFIED|GDK_WINDOW_STATE_WITHDRAWN));
  3168         if (mHasMappedToplevel != mapped) {
  3169             SetHasMappedToplevel(mapped);
  3171         return;
  3173     // else the widget is a shell widget.
  3175     // We don't care about anything but changes in the maximized/icon/fullscreen
  3176     // states
  3177     if ((aEvent->changed_mask
  3178          & (GDK_WINDOW_STATE_ICONIFIED |
  3179             GDK_WINDOW_STATE_MAXIMIZED |
  3180             GDK_WINDOW_STATE_FULLSCREEN)) == 0) {
  3181         return;
  3184     if (aEvent->new_window_state & GDK_WINDOW_STATE_ICONIFIED) {
  3185         LOG(("\tIconified\n"));
  3186         mSizeState = nsSizeMode_Minimized;
  3187 #ifdef ACCESSIBILITY
  3188         DispatchMinimizeEventAccessible();
  3189 #endif //ACCESSIBILITY
  3191     else if (aEvent->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) {
  3192         LOG(("\tFullscreen\n"));
  3193         mSizeState = nsSizeMode_Fullscreen;
  3195     else if (aEvent->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) {
  3196         LOG(("\tMaximized\n"));
  3197         mSizeState = nsSizeMode_Maximized;
  3198 #ifdef ACCESSIBILITY
  3199         DispatchMaximizeEventAccessible();
  3200 #endif //ACCESSIBILITY
  3202     else {
  3203         LOG(("\tNormal\n"));
  3204         mSizeState = nsSizeMode_Normal;
  3205 #ifdef ACCESSIBILITY
  3206         DispatchRestoreEventAccessible();
  3207 #endif //ACCESSIBILITY
  3210     if (mWidgetListener)
  3211       mWidgetListener->SizeModeChanged(mSizeState);
  3214 void
  3215 nsWindow::ThemeChanged()
  3217     NotifyThemeChanged();
  3219     if (!mGdkWindow || MOZ_UNLIKELY(mIsDestroyed))
  3220         return;
  3222     // Dispatch theme change notification to all child windows
  3223     GList *children =
  3224         gdk_window_peek_children(mGdkWindow);
  3225     while (children) {
  3226         GdkWindow *gdkWin = GDK_WINDOW(children->data);
  3228         nsWindow *win = (nsWindow*) g_object_get_data(G_OBJECT(gdkWin),
  3229                                                       "nsWindow");
  3231         if (win && win != this) { // guard against infinite recursion
  3232             nsRefPtr<nsWindow> kungFuDeathGrip = win;
  3233             win->ThemeChanged();
  3236         children = children->next;
  3240 void
  3241 nsWindow::DispatchDragEvent(uint32_t aMsg, const nsIntPoint& aRefPoint,
  3242                             guint aTime)
  3244     WidgetDragEvent event(true, aMsg, this);
  3246     if (aMsg == NS_DRAGDROP_OVER) {
  3247         InitDragEvent(event);
  3250     event.refPoint = LayoutDeviceIntPoint::FromUntyped(aRefPoint);
  3251     event.time = aTime;
  3253     nsEventStatus status;
  3254     DispatchEvent(&event, status);
  3257 void
  3258 nsWindow::OnDragDataReceivedEvent(GtkWidget *aWidget,
  3259                                   GdkDragContext *aDragContext,
  3260                                   gint aX,
  3261                                   gint aY,
  3262                                   GtkSelectionData  *aSelectionData,
  3263                                   guint aInfo,
  3264                                   guint aTime,
  3265                                   gpointer aData)
  3267     LOGDRAG(("nsWindow::OnDragDataReceived(%p)\n", (void*)this));
  3269     nsDragService::GetInstance()->
  3270         TargetDataReceived(aWidget, aDragContext, aX, aY,
  3271                            aSelectionData, aInfo, aTime);
  3274 static void
  3275 GetBrandName(nsXPIDLString& brandName)
  3277     nsCOMPtr<nsIStringBundleService> bundleService =
  3278         do_GetService(NS_STRINGBUNDLE_CONTRACTID);
  3280     nsCOMPtr<nsIStringBundle> bundle;
  3281     if (bundleService)
  3282         bundleService->CreateBundle(
  3283             "chrome://branding/locale/brand.properties",
  3284             getter_AddRefs(bundle));
  3286     if (bundle)
  3287         bundle->GetStringFromName(
  3288             MOZ_UTF16("brandShortName"),
  3289             getter_Copies(brandName));
  3291     if (brandName.IsEmpty())
  3292         brandName.Assign(NS_LITERAL_STRING("Mozilla"));
  3295 static GdkWindow *
  3296 CreateGdkWindow(GdkWindow *parent, GtkWidget *widget)
  3298     GdkWindowAttr attributes;
  3299     gint          attributes_mask = GDK_WA_VISUAL;
  3301     attributes.event_mask = kEvents;
  3303     attributes.width = 1;
  3304     attributes.height = 1;
  3305     attributes.wclass = GDK_INPUT_OUTPUT;
  3306     attributes.visual = gtk_widget_get_visual(widget);
  3307     attributes.window_type = GDK_WINDOW_CHILD;
  3309 #if (MOZ_WIDGET_GTK == 2)
  3310     attributes_mask |= GDK_WA_COLORMAP;
  3311     attributes.colormap = gtk_widget_get_colormap(widget);
  3312 #endif
  3314     GdkWindow *window = gdk_window_new(parent, &attributes, attributes_mask);
  3315     gdk_window_set_user_data(window, widget);
  3317 // GTK3 TODO?
  3318 #if (MOZ_WIDGET_GTK == 2)
  3319     /* set the default pixmap to None so that you don't end up with the
  3320        gtk default which is BlackPixel. */
  3321     gdk_window_set_back_pixmap(window, nullptr, FALSE);
  3322 #endif
  3324     return window;
  3327 nsresult
  3328 nsWindow::Create(nsIWidget        *aParent,
  3329                  nsNativeWidget    aNativeParent,
  3330                  const nsIntRect  &aRect,
  3331                  nsDeviceContext *aContext,
  3332                  nsWidgetInitData *aInitData)
  3334     // only set the base parent if we're going to be a dialog or a
  3335     // toplevel
  3336     nsIWidget *baseParent = aInitData &&
  3337         (aInitData->mWindowType == eWindowType_dialog ||
  3338          aInitData->mWindowType == eWindowType_toplevel ||
  3339          aInitData->mWindowType == eWindowType_invisible) ?
  3340         nullptr : aParent;
  3342 #ifdef ACCESSIBILITY
  3343     // Send a DBus message to check whether a11y is enabled
  3344     a11y::PreInit();
  3345 #endif
  3347     // Ensure that the toolkit is created.
  3348     nsGTKToolkit::GetToolkit();
  3350     // initialize all the common bits of this class
  3351     BaseCreate(baseParent, aRect, aContext, aInitData);
  3353     // Do we need to listen for resizes?
  3354     bool listenForResizes = false;;
  3355     if (aNativeParent || (aInitData && aInitData->mListenForResizes))
  3356         listenForResizes = true;
  3358     // and do our common creation
  3359     CommonCreate(aParent, listenForResizes);
  3361     // save our bounds
  3362     mBounds = aRect;
  3363     ConstrainSize(&mBounds.width, &mBounds.height);
  3365     // figure out our parent window
  3366     GtkWidget      *parentMozContainer = nullptr;
  3367     GtkContainer   *parentGtkContainer = nullptr;
  3368     GdkWindow      *parentGdkWindow = nullptr;
  3369     GtkWindow      *topLevelParent = nullptr;
  3370     nsWindow       *parentnsWindow = nullptr;
  3371     GtkWidget      *eventWidget = nullptr;
  3373     if (aParent) {
  3374         parentnsWindow = static_cast<nsWindow*>(aParent);
  3375         parentGdkWindow = parentnsWindow->mGdkWindow;
  3376     } else if (aNativeParent && GDK_IS_WINDOW(aNativeParent)) {
  3377         parentGdkWindow = GDK_WINDOW(aNativeParent);
  3378         parentnsWindow = get_window_for_gdk_window(parentGdkWindow);
  3379         if (!parentnsWindow)
  3380             return NS_ERROR_FAILURE;
  3382     } else if (aNativeParent && GTK_IS_CONTAINER(aNativeParent)) {
  3383         parentGtkContainer = GTK_CONTAINER(aNativeParent);
  3386     if (parentGdkWindow) {
  3387         // get the widget for the window - it should be a moz container
  3388         parentMozContainer = parentnsWindow->GetMozContainerWidget();
  3389         if (!parentMozContainer)
  3390             return NS_ERROR_FAILURE;
  3392         // get the toplevel window just in case someone needs to use it
  3393         // for setting transients or whatever.
  3394         topLevelParent =
  3395             GTK_WINDOW(gtk_widget_get_toplevel(parentMozContainer));
  3398     // ok, create our windows
  3399     switch (mWindowType) {
  3400     case eWindowType_dialog:
  3401     case eWindowType_popup:
  3402     case eWindowType_toplevel:
  3403     case eWindowType_invisible: {
  3404         mIsTopLevel = true;
  3406         // We only move a general managed toplevel window if someone has
  3407         // actually placed the window somewhere.  If no placement has taken
  3408         // place, we just let the window manager Do The Right Thing.
  3409         //
  3410         // Indicate that if we're shown, we at least need to have our size set.
  3411         // If we get explicitly moved, the position will also be set.
  3412         mNeedsResize = true;
  3414         if (mWindowType == eWindowType_dialog) {
  3415             mShell = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  3416             SetDefaultIcon();
  3417             gtk_window_set_wmclass(GTK_WINDOW(mShell), "Dialog", 
  3418                                    gdk_get_program_class());
  3419             gtk_window_set_type_hint(GTK_WINDOW(mShell),
  3420                                      GDK_WINDOW_TYPE_HINT_DIALOG);
  3421             gtk_window_set_transient_for(GTK_WINDOW(mShell),
  3422                                          topLevelParent);
  3424         else if (mWindowType == eWindowType_popup) {
  3425             // With popup windows, we want to control their position, so don't
  3426             // wait for the window manager to place them (which wouldn't
  3427             // happen with override-redirect windows anyway).
  3428             mNeedsMove = true;
  3430             // Popups that are not noautohide are only temporary. The are used
  3431             // for menus and the like and disappear when another window is used.
  3432             // For most popups, use the standard GtkWindowType GTK_WINDOW_POPUP,
  3433             // which will use a Window with the override-redirect attribute
  3434             // (for temporary windows).
  3435             // For long-lived windows, their stacking order is managed by the
  3436             // window manager, as indicated by GTK_WINDOW_TOPLEVEL ...
  3437             GtkWindowType type = aInitData->mNoAutoHide ?
  3438                                      GTK_WINDOW_TOPLEVEL : GTK_WINDOW_POPUP;
  3439             mShell = gtk_window_new(type);
  3440             gtk_window_set_wmclass(GTK_WINDOW(mShell), "Popup",
  3441                                    gdk_get_program_class());
  3443             if (aInitData->mSupportTranslucency) {
  3444                 // We need to select an ARGB visual here instead of in
  3445                 // SetTransparencyMode() because it has to be done before the
  3446                 // widget is realized.  An ARGB visual is only useful if we
  3447                 // are on a compositing window manager.
  3448                 GdkScreen *screen = gtk_widget_get_screen(mShell);
  3449                 if (gdk_screen_is_composited(screen)) {
  3450 #if (MOZ_WIDGET_GTK == 2)
  3451                     GdkColormap *colormap =
  3452                         gdk_screen_get_rgba_colormap(screen);
  3453                     gtk_widget_set_colormap(mShell, colormap);
  3454 #else
  3455                     GdkVisual *visual = gdk_screen_get_rgba_visual(screen);
  3456                     gtk_widget_set_visual(mShell, visual);
  3457 #endif
  3460             if (aInitData->mNoAutoHide) {
  3461                 // ... but the window manager does not decorate this window,
  3462                 // nor provide a separate taskbar icon.
  3463                 if (mBorderStyle == eBorderStyle_default) {
  3464                   gtk_window_set_decorated(GTK_WINDOW(mShell), FALSE);
  3466                 else {
  3467                   bool decorate = mBorderStyle & eBorderStyle_title;
  3468                   gtk_window_set_decorated(GTK_WINDOW(mShell), decorate);
  3469                   if (decorate) {
  3470                     gtk_window_set_deletable(GTK_WINDOW(mShell), mBorderStyle & eBorderStyle_close);
  3473                 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mShell), TRUE);
  3474                 // Element focus is managed by the parent window so the
  3475                 // WM_HINTS input field is set to False to tell the window
  3476                 // manager not to set input focus to this window ...
  3477                 gtk_window_set_accept_focus(GTK_WINDOW(mShell), FALSE);
  3478 #ifdef MOZ_X11
  3479                 // ... but when the window manager offers focus through
  3480                 // WM_TAKE_FOCUS, focus is requested on the parent window.
  3481                 gtk_widget_realize(mShell);
  3482                 gdk_window_add_filter(gtk_widget_get_window(mShell),
  3483                                       popup_take_focus_filter, nullptr); 
  3484 #endif
  3487             GdkWindowTypeHint gtkTypeHint;
  3488             if (aInitData->mIsDragPopup) {
  3489                 gtkTypeHint = GDK_WINDOW_TYPE_HINT_DND;
  3491             else {
  3492                 switch (aInitData->mPopupHint) {
  3493                     case ePopupTypeMenu:
  3494                         gtkTypeHint = GDK_WINDOW_TYPE_HINT_POPUP_MENU;
  3495                         break;
  3496                     case ePopupTypeTooltip:
  3497                         gtkTypeHint = GDK_WINDOW_TYPE_HINT_TOOLTIP;
  3498                         break;
  3499                     default:
  3500                         gtkTypeHint = GDK_WINDOW_TYPE_HINT_UTILITY;
  3501                         break;
  3504             gtk_window_set_type_hint(GTK_WINDOW(mShell), gtkTypeHint);
  3506             if (topLevelParent) {
  3507                 gtk_window_set_transient_for(GTK_WINDOW(mShell),
  3508                                             topLevelParent);
  3511         else { // must be eWindowType_toplevel
  3512             mShell = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  3513             SetDefaultIcon();
  3514             gtk_window_set_wmclass(GTK_WINDOW(mShell), "Toplevel", 
  3515                                    gdk_get_program_class());
  3517             // each toplevel window gets its own window group
  3518             GtkWindowGroup *group = gtk_window_group_new();
  3519             gtk_window_group_add_window(group, GTK_WINDOW(mShell));
  3520             g_object_unref(group);
  3523         // Prevent GtkWindow from painting a background to flicker.
  3524         gtk_widget_set_app_paintable(mShell, TRUE);
  3526         // Create a container to hold child windows and child GtkWidgets.
  3527         GtkWidget *container = moz_container_new();
  3528         mContainer = MOZ_CONTAINER(container);
  3529         // Use mShell's window for drawing and events.
  3530         gtk_widget_set_has_window(container, FALSE);
  3531         eventWidget = mShell;
  3532         gtk_widget_add_events(eventWidget, kEvents);
  3533         gtk_container_add(GTK_CONTAINER(mShell), container);
  3534         gtk_widget_realize(container);
  3536         // make sure this is the focus widget in the container
  3537         gtk_widget_show(container);
  3538         gtk_widget_grab_focus(container);
  3540         // the drawing window
  3541         mGdkWindow = gtk_widget_get_window(mShell);
  3543         if (mWindowType == eWindowType_popup) {
  3544             // gdk does not automatically set the cursor for "temporary"
  3545             // windows, which are what gtk uses for popups.
  3547             mCursor = eCursor_wait; // force SetCursor to actually set the
  3548                                     // cursor, even though our internal state
  3549                                     // indicates that we already have the
  3550                                     // standard cursor.
  3551             SetCursor(eCursor_standard);
  3553             if (aInitData->mNoAutoHide) {
  3554                 gint wmd = ConvertBorderStyles(mBorderStyle);
  3555                 if (wmd != -1)
  3556                   gdk_window_set_decorations(gtk_widget_get_window(mShell), (GdkWMDecoration) wmd);
  3559             // If the popup ignores mouse events, set an empty input shape.
  3560             if (aInitData->mMouseTransparent) {
  3561 #if (MOZ_WIDGET_GTK == 2)
  3562               GdkRectangle rect = { 0, 0, 0, 0 };
  3563               GdkRegion *region = gdk_region_rectangle(&rect);
  3565               gdk_window_input_shape_combine_region(mGdkWindow, region, 0, 0);
  3566               gdk_region_destroy(region);
  3567 #else
  3568               cairo_rectangle_int_t rect = { 0, 0, 0, 0 };
  3569               cairo_region_t *region = cairo_region_create_rectangle(&rect);
  3571               gdk_window_input_shape_combine_region(mGdkWindow, region, 0, 0);
  3572               cairo_region_destroy(region);
  3573 #endif
  3577         break;
  3578     case eWindowType_plugin:
  3579     case eWindowType_child: {
  3580         if (parentMozContainer) {
  3581             mGdkWindow = CreateGdkWindow(parentGdkWindow, parentMozContainer);
  3582             mHasMappedToplevel = parentnsWindow->mHasMappedToplevel;
  3584         else if (parentGtkContainer) {
  3585             // This MozContainer has its own window for drawing and receives
  3586             // events because there is no mShell widget (corresponding to this
  3587             // nsWindow).
  3588             GtkWidget *container = moz_container_new();
  3589             mContainer = MOZ_CONTAINER(container);
  3590             eventWidget = container;
  3591             gtk_widget_add_events(eventWidget, kEvents);
  3592             gtk_container_add(parentGtkContainer, container);
  3593             gtk_widget_realize(container);
  3595             mGdkWindow = gtk_widget_get_window(container);
  3597         else {
  3598             NS_WARNING("Warning: tried to create a new child widget with no parent!");
  3599             return NS_ERROR_FAILURE;
  3602         break;
  3603     default:
  3604         break;
  3607     // label the drawing window with this object so we can find our way home
  3608     g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", this);
  3610     if (mContainer)
  3611         g_object_set_data(G_OBJECT(mContainer), "nsWindow", this);
  3613     if (mShell)
  3614         g_object_set_data(G_OBJECT(mShell), "nsWindow", this);
  3616     // attach listeners for events
  3617     if (mShell) {
  3618         g_signal_connect(mShell, "configure_event",
  3619                          G_CALLBACK(configure_event_cb), nullptr);
  3620         g_signal_connect(mShell, "delete_event",
  3621                          G_CALLBACK(delete_event_cb), nullptr);
  3622         g_signal_connect(mShell, "window_state_event",
  3623                          G_CALLBACK(window_state_event_cb), nullptr);
  3625         GtkSettings* default_settings = gtk_settings_get_default();
  3626         g_signal_connect_after(default_settings,
  3627                                "notify::gtk-theme-name",
  3628                                G_CALLBACK(theme_changed_cb), this);
  3629         g_signal_connect_after(default_settings,
  3630                                "notify::gtk-font-name",
  3631                                G_CALLBACK(theme_changed_cb), this);
  3634     if (mContainer) {
  3635         // Widget signals
  3636         g_signal_connect(mContainer, "unrealize",
  3637                          G_CALLBACK(container_unrealize_cb), nullptr);
  3638         g_signal_connect_after(mContainer, "size_allocate",
  3639                                G_CALLBACK(size_allocate_cb), nullptr);
  3640         g_signal_connect(mContainer, "hierarchy-changed",
  3641                          G_CALLBACK(hierarchy_changed_cb), nullptr);
  3642         // Initialize mHasMappedToplevel.
  3643         hierarchy_changed_cb(GTK_WIDGET(mContainer), nullptr);
  3644         // Expose, focus, key, and drag events are sent even to GTK_NO_WINDOW
  3645         // widgets.
  3646 #if (MOZ_WIDGET_GTK == 2)
  3647         g_signal_connect(mContainer, "expose_event",
  3648                          G_CALLBACK(expose_event_cb), nullptr);
  3649 #else
  3650         g_signal_connect(G_OBJECT(mContainer), "draw",
  3651                          G_CALLBACK(expose_event_cb), nullptr);
  3652 #endif
  3653         g_signal_connect(mContainer, "focus_in_event",
  3654                          G_CALLBACK(focus_in_event_cb), nullptr);
  3655         g_signal_connect(mContainer, "focus_out_event",
  3656                          G_CALLBACK(focus_out_event_cb), nullptr);
  3657         g_signal_connect(mContainer, "key_press_event",
  3658                          G_CALLBACK(key_press_event_cb), nullptr);
  3659         g_signal_connect(mContainer, "key_release_event",
  3660                          G_CALLBACK(key_release_event_cb), nullptr);
  3662         gtk_drag_dest_set((GtkWidget *)mContainer,
  3663                           (GtkDestDefaults)0,
  3664                           nullptr,
  3665                           0,
  3666                           (GdkDragAction)0);
  3668         g_signal_connect(mContainer, "drag_motion",
  3669                          G_CALLBACK(drag_motion_event_cb), nullptr);
  3670         g_signal_connect(mContainer, "drag_leave",
  3671                          G_CALLBACK(drag_leave_event_cb), nullptr);
  3672         g_signal_connect(mContainer, "drag_drop",
  3673                          G_CALLBACK(drag_drop_event_cb), nullptr);
  3674         g_signal_connect(mContainer, "drag_data_received",
  3675                          G_CALLBACK(drag_data_received_event_cb), nullptr);
  3677         GtkWidget *widgets[] = { GTK_WIDGET(mContainer), mShell };
  3678         for (size_t i = 0; i < ArrayLength(widgets) && widgets[i]; ++i) {
  3679             // Visibility events are sent to the owning widget of the relevant
  3680             // window but do not propagate to parent widgets so connect on
  3681             // mShell (if it exists) as well as mContainer.
  3682             g_signal_connect(widgets[i], "visibility-notify-event",
  3683                              G_CALLBACK(visibility_notify_event_cb), nullptr);
  3684             // Similarly double buffering is controlled by the window's owning
  3685             // widget.  Disable double buffering for painting directly to the
  3686             // X Window.
  3687             gtk_widget_set_double_buffered(widgets[i], FALSE);
  3690         // We create input contexts for all containers, except for
  3691         // toplevel popup windows
  3692         if (mWindowType != eWindowType_popup) {
  3693             mIMModule = new nsGtkIMModule(this);
  3695     } else if (!mIMModule) {
  3696         nsWindow *container = GetContainerWindow();
  3697         if (container) {
  3698             mIMModule = container->mIMModule;
  3702     if (eventWidget) {
  3703 #if (MOZ_WIDGET_GTK == 2)
  3704         // Don't let GTK mess with the shapes of our GdkWindows
  3705         GTK_PRIVATE_SET_FLAG(eventWidget, GTK_HAS_SHAPE_MASK);
  3706 #endif
  3708         // These events are sent to the owning widget of the relevant window
  3709         // and propagate up to the first widget that handles the events, so we
  3710         // need only connect on mShell, if it exists, to catch events on its
  3711         // window and windows of mContainer.
  3712         g_signal_connect(eventWidget, "enter-notify-event",
  3713                          G_CALLBACK(enter_notify_event_cb), nullptr);
  3714         g_signal_connect(eventWidget, "leave-notify-event",
  3715                          G_CALLBACK(leave_notify_event_cb), nullptr);
  3716         g_signal_connect(eventWidget, "motion-notify-event",
  3717                          G_CALLBACK(motion_notify_event_cb), nullptr);
  3718         g_signal_connect(eventWidget, "button-press-event",
  3719                          G_CALLBACK(button_press_event_cb), nullptr);
  3720         g_signal_connect(eventWidget, "button-release-event",
  3721                          G_CALLBACK(button_release_event_cb), nullptr);
  3722         g_signal_connect(eventWidget, "scroll-event",
  3723                          G_CALLBACK(scroll_event_cb), nullptr);
  3726     LOG(("nsWindow [%p]\n", (void *)this));
  3727     if (mShell) {
  3728         LOG(("\tmShell %p mContainer %p mGdkWindow %p 0x%lx\n",
  3729              mShell, mContainer, mGdkWindow,
  3730              gdk_x11_window_get_xid(mGdkWindow)));
  3731     } else if (mContainer) {
  3732         LOG(("\tmContainer %p mGdkWindow %p\n", mContainer, mGdkWindow));
  3734     else if (mGdkWindow) {
  3735         LOG(("\tmGdkWindow %p parent %p\n",
  3736              mGdkWindow, gdk_window_get_parent(mGdkWindow)));
  3739     // resize so that everything is set to the right dimensions
  3740     if (!mIsTopLevel)
  3741         Resize(mBounds.x, mBounds.y, mBounds.width, mBounds.height, false);
  3743     return NS_OK;
  3746 NS_IMETHODIMP
  3747 nsWindow::SetWindowClass(const nsAString &xulWinType)
  3749   if (!mShell)
  3750     return NS_ERROR_FAILURE;
  3752   const char *res_class = gdk_get_program_class();
  3753   if (!res_class)
  3754     return NS_ERROR_FAILURE;
  3756   char *res_name = ToNewCString(xulWinType);
  3757   if (!res_name)
  3758     return NS_ERROR_OUT_OF_MEMORY;
  3760   const char *role = nullptr;
  3762   // Parse res_name into a name and role. Characters other than
  3763   // [A-Za-z0-9_-] are converted to '_'. Anything after the first
  3764   // colon is assigned to role; if there's no colon, assign the
  3765   // whole thing to both role and res_name.
  3766   for (char *c = res_name; *c; c++) {
  3767     if (':' == *c) {
  3768       *c = 0;
  3769       role = c + 1;
  3771     else if (!isascii(*c) || (!isalnum(*c) && ('_' != *c) && ('-' != *c)))
  3772       *c = '_';
  3774   res_name[0] = toupper(res_name[0]);
  3775   if (!role) role = res_name;
  3777   GdkWindow *shellWindow = gtk_widget_get_window(GTK_WIDGET(mShell));
  3778   gdk_window_set_role(shellWindow, role);
  3780 #ifdef MOZ_X11
  3781   XClassHint *class_hint = XAllocClassHint();
  3782   if (!class_hint) {
  3783     nsMemory::Free(res_name);
  3784     return NS_ERROR_OUT_OF_MEMORY;
  3786   class_hint->res_name = res_name;
  3787   class_hint->res_class = const_cast<char*>(res_class);
  3789   // Can't use gtk_window_set_wmclass() for this; it prints
  3790   // a warning & refuses to make the change.
  3791   XSetClassHint(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
  3792                 gdk_x11_window_get_xid(shellWindow),
  3793                 class_hint);
  3794   XFree(class_hint);
  3795 #endif /* MOZ_X11 */
  3797   nsMemory::Free(res_name);
  3799   return NS_OK;
  3802 void
  3803 nsWindow::NativeResize(int32_t aWidth, int32_t aHeight, bool    aRepaint)
  3805     LOG(("nsWindow::NativeResize [%p] %d %d\n", (void *)this,
  3806          aWidth, aHeight));
  3808     // clear our resize flag
  3809     mNeedsResize = false;
  3811     if (mIsTopLevel) {
  3812         gtk_window_resize(GTK_WINDOW(mShell), aWidth, aHeight);
  3814     else if (mContainer) {
  3815         GtkWidget *widget = GTK_WIDGET(mContainer);
  3816         GtkAllocation allocation, prev_allocation;
  3817         gtk_widget_get_allocation(widget, &prev_allocation);
  3818         allocation.x = prev_allocation.x;
  3819         allocation.y = prev_allocation.y;
  3820         allocation.width = aWidth;
  3821         allocation.height = aHeight;
  3822         gtk_widget_size_allocate(widget, &allocation);
  3824     else if (mGdkWindow) {
  3825         gdk_window_resize(mGdkWindow, aWidth, aHeight);
  3829 void
  3830 nsWindow::NativeResize(int32_t aX, int32_t aY,
  3831                        int32_t aWidth, int32_t aHeight,
  3832                        bool    aRepaint)
  3834     mNeedsResize = false;
  3835     mNeedsMove = false;
  3837     LOG(("nsWindow::NativeResize [%p] %d %d %d %d\n", (void *)this,
  3838          aX, aY, aWidth, aHeight));
  3840     if (mIsTopLevel) {
  3841         // aX and aY give the position of the window manager frame top-left.
  3842         gtk_window_move(GTK_WINDOW(mShell), aX, aY);
  3843         // This sets the client window size.
  3844         gtk_window_resize(GTK_WINDOW(mShell), aWidth, aHeight);
  3846     else if (mContainer) {
  3847         GtkAllocation allocation;
  3848         allocation.x = aX;
  3849         allocation.y = aY;
  3850         allocation.width = aWidth;
  3851         allocation.height = aHeight;
  3852         gtk_widget_size_allocate(GTK_WIDGET(mContainer), &allocation);
  3854     else if (mGdkWindow) {
  3855         gdk_window_move_resize(mGdkWindow, aX, aY, aWidth, aHeight);
  3859 void
  3860 nsWindow::NativeShow(bool aAction)
  3862     if (aAction) {
  3863         // unset our flag now that our window has been shown
  3864         mNeedsShow = false;
  3866         if (mIsTopLevel) {
  3867             // Set up usertime/startupID metadata for the created window.
  3868             if (mWindowType != eWindowType_invisible) {
  3869                 SetUserTimeAndStartupIDForActivatedWindow(mShell);
  3872             gtk_widget_show(mShell);
  3874         else if (mContainer) {
  3875             gtk_widget_show(GTK_WIDGET(mContainer));
  3877         else if (mGdkWindow) {
  3878             gdk_window_show_unraised(mGdkWindow);
  3881     else {
  3882         if (mIsTopLevel) {
  3883             gtk_widget_hide(GTK_WIDGET(mShell));
  3885             ClearTransparencyBitmap(); // Release some resources
  3887         else if (mContainer) {
  3888             gtk_widget_hide(GTK_WIDGET(mContainer));
  3890         else if (mGdkWindow) {
  3891             gdk_window_hide(mGdkWindow);
  3896 void
  3897 nsWindow::SetHasMappedToplevel(bool aState)
  3899     // Even when aState == mHasMappedToplevel (as when this method is called
  3900     // from Show()), child windows need to have their state checked, so don't
  3901     // return early.
  3902     bool oldState = mHasMappedToplevel;
  3903     mHasMappedToplevel = aState;
  3905     // mHasMappedToplevel is not updated for children of windows that are
  3906     // hidden; GDK knows not to send expose events for these windows.  The
  3907     // state is recorded on the hidden window itself, but, for child trees of
  3908     // hidden windows, their state essentially becomes disconnected from their
  3909     // hidden parent.  When the hidden parent gets shown, the child trees are
  3910     // reconnected, and the state of the window being shown can be easily
  3911     // propagated.
  3912     if (!mIsShown || !mGdkWindow)
  3913         return;
  3915     if (aState && !oldState && !mIsFullyObscured) {
  3916         // GDK_EXPOSE events have been ignored but the window is now visible,
  3917         // so make sure GDK doesn't think that the window has already been
  3918         // painted.
  3919         gdk_window_invalidate_rect(mGdkWindow, nullptr, FALSE);
  3921         // Check that a grab didn't fail due to the window not being
  3922         // viewable.
  3923         EnsureGrabs();
  3926     for (GList *children = gdk_window_peek_children(mGdkWindow);
  3927          children;
  3928          children = children->next) {
  3929         GdkWindow *gdkWin = GDK_WINDOW(children->data);
  3930         nsWindow *child = get_window_for_gdk_window(gdkWin);
  3932         if (child && child->mHasMappedToplevel != aState) {
  3933             child->SetHasMappedToplevel(aState);
  3938 nsIntSize
  3939 nsWindow::GetSafeWindowSize(nsIntSize aSize)
  3941     // The X protocol uses CARD32 for window sizes, but the server (1.11.3)
  3942     // reads it as CARD16.  Sizes of pixmaps, used for drawing, are (unsigned)
  3943     // CARD16 in the protocol, but the server's ProcCreatePixmap returns
  3944     // BadAlloc if dimensions cannot be represented by signed shorts.
  3945     nsIntSize result = aSize;
  3946     const int32_t kInt16Max = 32767;
  3947     if (result.width > kInt16Max) {
  3948         result.width = kInt16Max;
  3950     if (result.height > kInt16Max) {
  3951         result.height = kInt16Max;
  3953     return result;
  3956 void
  3957 nsWindow::EnsureGrabs(void)
  3959     if (mRetryPointerGrab)
  3960         GrabPointer(sRetryGrabTime);
  3963 void
  3964 nsWindow::CleanLayerManagerRecursive(void) {
  3965     if (mLayerManager) {
  3966         mLayerManager->Destroy();
  3967         mLayerManager = nullptr;
  3970     DestroyCompositor();
  3972     GList* children = gdk_window_peek_children(mGdkWindow);
  3973     for (GList* list = children; list; list = list->next) {
  3974         nsWindow* window = get_window_for_gdk_window(GDK_WINDOW(list->data));
  3975         if (window) {
  3976             window->CleanLayerManagerRecursive();
  3981 void
  3982 nsWindow::SetTransparencyMode(nsTransparencyMode aMode)
  3984     if (!mShell) {
  3985         // Pass the request to the toplevel window
  3986         GtkWidget *topWidget = GetToplevelWidget();
  3987         if (!topWidget)
  3988             return;
  3990         nsWindow *topWindow = get_window_for_gtk_widget(topWidget);
  3991         if (!topWindow)
  3992             return;
  3994         topWindow->SetTransparencyMode(aMode);
  3995         return;
  3997     bool isTransparent = aMode == eTransparencyTransparent;
  3999     if (mIsTransparent == isTransparent)
  4000         return;
  4002     if (!isTransparent) {
  4003         ClearTransparencyBitmap();
  4004     } // else the new default alpha values are "all 1", so we don't
  4005     // need to change anything yet
  4007     mIsTransparent = isTransparent;
  4009     // Need to clean our LayerManager up while still alive because
  4010     // we don't want to use layers acceleration on shaped windows
  4011     CleanLayerManagerRecursive();
  4014 nsTransparencyMode
  4015 nsWindow::GetTransparencyMode()
  4017     if (!mShell) {
  4018         // Pass the request to the toplevel window
  4019         GtkWidget *topWidget = GetToplevelWidget();
  4020         if (!topWidget) {
  4021             return eTransparencyOpaque;
  4024         nsWindow *topWindow = get_window_for_gtk_widget(topWidget);
  4025         if (!topWindow) {
  4026             return eTransparencyOpaque;
  4029         return topWindow->GetTransparencyMode();
  4032     return mIsTransparent ? eTransparencyTransparent : eTransparencyOpaque;
  4035 nsresult
  4036 nsWindow::ConfigureChildren(const nsTArray<Configuration>& aConfigurations)
  4038     for (uint32_t i = 0; i < aConfigurations.Length(); ++i) {
  4039         const Configuration& configuration = aConfigurations[i];
  4040         nsWindow* w = static_cast<nsWindow*>(configuration.mChild);
  4041         NS_ASSERTION(w->GetParent() == this,
  4042                      "Configured widget is not a child");
  4043         w->SetWindowClipRegion(configuration.mClipRegion, true);
  4044         if (w->mBounds.Size() != configuration.mBounds.Size()) {
  4045             w->Resize(configuration.mBounds.x, configuration.mBounds.y,
  4046                       configuration.mBounds.width, configuration.mBounds.height,
  4047                       true);
  4048         } else if (w->mBounds.TopLeft() != configuration.mBounds.TopLeft()) {
  4049             w->Move(configuration.mBounds.x, configuration.mBounds.y);
  4051         w->SetWindowClipRegion(configuration.mClipRegion, false);
  4053     return NS_OK;
  4056 static pixman_box32
  4057 ToPixmanBox(const nsIntRect& aRect)
  4059     pixman_box32_t result;
  4060     result.x1 = aRect.x;
  4061     result.y1 = aRect.y;
  4062     result.x2 = aRect.XMost();
  4063     result.y2 = aRect.YMost();
  4064     return result;
  4067 static nsIntRect
  4068 ToIntRect(const pixman_box32& aBox)
  4070     nsIntRect result;
  4071     result.x = aBox.x1;
  4072     result.y = aBox.y1;
  4073     result.width = aBox.x2 - aBox.x1;
  4074     result.height = aBox.y2 - aBox.y1;
  4075     return result;
  4078 static void
  4079 InitRegion(pixman_region32* aRegion,
  4080            const nsTArray<nsIntRect>& aRects)
  4082     nsAutoTArray<pixman_box32,10> rects;
  4083     rects.SetCapacity(aRects.Length());
  4084     for (uint32_t i = 0; i < aRects.Length (); ++i) {
  4085         if (!aRects[i].IsEmpty()) {
  4086             rects.AppendElement(ToPixmanBox(aRects[i]));
  4090     pixman_region32_init_rects(aRegion,
  4091                                rects.Elements(), rects.Length());
  4094 static void
  4095 GetIntRects(pixman_region32& aRegion, nsTArray<nsIntRect>* aRects)
  4097     int nRects;
  4098     pixman_box32* boxes = pixman_region32_rectangles(&aRegion, &nRects);
  4099     aRects->SetCapacity(aRects->Length() + nRects);
  4100     for (int i = 0; i < nRects; ++i) {
  4101         aRects->AppendElement(ToIntRect(boxes[i]));
  4105 void
  4106 nsWindow::SetWindowClipRegion(const nsTArray<nsIntRect>& aRects,
  4107                               bool aIntersectWithExisting)
  4109     const nsTArray<nsIntRect>* newRects = &aRects;
  4111     nsAutoTArray<nsIntRect,1> intersectRects;
  4112     if (aIntersectWithExisting) {
  4113         nsAutoTArray<nsIntRect,1> existingRects;
  4114         GetWindowClipRegion(&existingRects);
  4116         nsAutoRef<pixman_region32> existingRegion;
  4117         InitRegion(&existingRegion, existingRects);
  4118         nsAutoRef<pixman_region32> newRegion;
  4119         InitRegion(&newRegion, aRects);
  4120         nsAutoRef<pixman_region32> intersectRegion;
  4121         pixman_region32_init(&intersectRegion);
  4122         pixman_region32_intersect(&intersectRegion,
  4123                                   &newRegion, &existingRegion);
  4125         // If mClipRects is null we haven't set a clip rect yet, so we
  4126         // need to set the clip even if it is equal.
  4127         if (mClipRects &&
  4128             pixman_region32_equal(&intersectRegion, &existingRegion)) {
  4129             return;
  4132         if (!pixman_region32_equal(&intersectRegion, &newRegion)) {
  4133             GetIntRects(intersectRegion, &intersectRects);
  4134             newRects = &intersectRects;
  4138     if (!StoreWindowClipRegion(*newRects))
  4139         return;
  4141     if (!mGdkWindow)
  4142         return;
  4144 #if (MOZ_WIDGET_GTK == 2)
  4145     GdkRegion *region = gdk_region_new(); // aborts on OOM
  4146     for (uint32_t i = 0; i < newRects->Length(); ++i) {
  4147         const nsIntRect& r = newRects->ElementAt(i);
  4148         GdkRectangle rect = { r.x, r.y, r.width, r.height };
  4149         gdk_region_union_with_rect(region, &rect);
  4152     gdk_window_shape_combine_region(mGdkWindow, region, 0, 0);
  4153     gdk_region_destroy(region);
  4154 #else
  4155     cairo_region_t *region = cairo_region_create();
  4156     for (uint32_t i = 0; i < newRects->Length(); ++i) {
  4157         const nsIntRect& r = newRects->ElementAt(i);
  4158         cairo_rectangle_int_t rect = { r.x, r.y, r.width, r.height };
  4159         cairo_region_union_rectangle(region, &rect);
  4162     gdk_window_shape_combine_region(mGdkWindow, region, 0, 0);
  4163     cairo_region_destroy(region);
  4164 #endif
  4166     return;
  4169 void
  4170 nsWindow::ResizeTransparencyBitmap()
  4172     if (!mTransparencyBitmap)
  4173         return;
  4175     if (mBounds.width == mTransparencyBitmapWidth &&
  4176         mBounds.height == mTransparencyBitmapHeight)
  4177         return;
  4179     int32_t newRowBytes = GetBitmapStride(mBounds.width);
  4180     int32_t newSize = newRowBytes * mBounds.height;
  4181     gchar* newBits = new gchar[newSize];
  4182     // fill new mask with "transparent", first
  4183     memset(newBits, 0, newSize);
  4185     // Now copy the intersection of the old and new areas into the new mask
  4186     int32_t copyWidth = std::min(mBounds.width, mTransparencyBitmapWidth);
  4187     int32_t copyHeight = std::min(mBounds.height, mTransparencyBitmapHeight);
  4188     int32_t oldRowBytes = GetBitmapStride(mTransparencyBitmapWidth);
  4189     int32_t copyBytes = GetBitmapStride(copyWidth);
  4191     int32_t i;
  4192     gchar* fromPtr = mTransparencyBitmap;
  4193     gchar* toPtr = newBits;
  4194     for (i = 0; i < copyHeight; i++) {
  4195         memcpy(toPtr, fromPtr, copyBytes);
  4196         fromPtr += oldRowBytes;
  4197         toPtr += newRowBytes;
  4200     delete[] mTransparencyBitmap;
  4201     mTransparencyBitmap = newBits;
  4202     mTransparencyBitmapWidth = mBounds.width;
  4203     mTransparencyBitmapHeight = mBounds.height;
  4206 static bool
  4207 ChangedMaskBits(gchar* aMaskBits, int32_t aMaskWidth, int32_t aMaskHeight,
  4208         const nsIntRect& aRect, uint8_t* aAlphas, int32_t aStride)
  4210     int32_t x, y, xMax = aRect.XMost(), yMax = aRect.YMost();
  4211     int32_t maskBytesPerRow = GetBitmapStride(aMaskWidth);
  4212     for (y = aRect.y; y < yMax; y++) {
  4213         gchar* maskBytes = aMaskBits + y*maskBytesPerRow;
  4214         uint8_t* alphas = aAlphas;
  4215         for (x = aRect.x; x < xMax; x++) {
  4216             bool newBit = *alphas > 0x7f;
  4217             alphas++;
  4219             gchar maskByte = maskBytes[x >> 3];
  4220             bool maskBit = (maskByte & (1 << (x & 7))) != 0;
  4222             if (maskBit != newBit) {
  4223                 return true;
  4226         aAlphas += aStride;
  4229     return false;
  4232 static
  4233 void UpdateMaskBits(gchar* aMaskBits, int32_t aMaskWidth, int32_t aMaskHeight,
  4234         const nsIntRect& aRect, uint8_t* aAlphas, int32_t aStride)
  4236     int32_t x, y, xMax = aRect.XMost(), yMax = aRect.YMost();
  4237     int32_t maskBytesPerRow = GetBitmapStride(aMaskWidth);
  4238     for (y = aRect.y; y < yMax; y++) {
  4239         gchar* maskBytes = aMaskBits + y*maskBytesPerRow;
  4240         uint8_t* alphas = aAlphas;
  4241         for (x = aRect.x; x < xMax; x++) {
  4242             bool newBit = *alphas > 0x7f;
  4243             alphas++;
  4245             gchar mask = 1 << (x & 7);
  4246             gchar maskByte = maskBytes[x >> 3];
  4247             // Note: '-newBit' turns 0 into 00...00 and 1 into 11...11
  4248             maskBytes[x >> 3] = (maskByte & ~mask) | (-newBit & mask);
  4250         aAlphas += aStride;
  4254 void
  4255 nsWindow::ApplyTransparencyBitmap()
  4257 #ifdef MOZ_X11
  4258     // We use X11 calls where possible, because GDK handles expose events
  4259     // for shaped windows in a way that's incompatible with us (Bug 635903).
  4260     // It doesn't occur when the shapes are set through X.
  4261     GdkWindow *shellWindow = gtk_widget_get_window(mShell);
  4262     Display* xDisplay = GDK_WINDOW_XDISPLAY(shellWindow);
  4263     Window xDrawable = GDK_WINDOW_XID(shellWindow);
  4264     Pixmap maskPixmap = XCreateBitmapFromData(xDisplay,
  4265                                               xDrawable,
  4266                                               mTransparencyBitmap,
  4267                                               mTransparencyBitmapWidth,
  4268                                               mTransparencyBitmapHeight);
  4269     XShapeCombineMask(xDisplay, xDrawable,
  4270                       ShapeBounding, 0, 0,
  4271                       maskPixmap, ShapeSet);
  4272     XFreePixmap(xDisplay, maskPixmap);
  4273 #else
  4274 #if (MOZ_WIDGET_GTK == 2)
  4275     gtk_widget_reset_shapes(mShell);
  4276     GdkBitmap* maskBitmap = gdk_bitmap_create_from_data(gtk_widget_get_window(mShell),
  4277             mTransparencyBitmap,
  4278             mTransparencyBitmapWidth, mTransparencyBitmapHeight);
  4279     if (!maskBitmap)
  4280         return;
  4282     gtk_widget_shape_combine_mask(mShell, maskBitmap, 0, 0);
  4283     g_object_unref(maskBitmap);
  4284 #else
  4285     cairo_surface_t *maskBitmap;
  4286     maskBitmap = cairo_image_surface_create_for_data((unsigned char*)mTransparencyBitmap, 
  4287                                                      CAIRO_FORMAT_A1, 
  4288                                                      mTransparencyBitmapWidth, 
  4289                                                      mTransparencyBitmapHeight,
  4290                                                      GetBitmapStride(mTransparencyBitmapWidth));
  4291     if (!maskBitmap)
  4292         return;
  4294     cairo_region_t * maskRegion = gdk_cairo_region_create_from_surface(maskBitmap);
  4295     gtk_widget_shape_combine_region(mShell, maskRegion);
  4296     cairo_region_destroy(maskRegion);
  4297     cairo_surface_destroy(maskBitmap);
  4298 #endif // MOZ_WIDGET_GTK2
  4299 #endif // MOZ_X11
  4302 void
  4303 nsWindow::ClearTransparencyBitmap()
  4305     if (!mTransparencyBitmap)
  4306         return;
  4308     delete[] mTransparencyBitmap;
  4309     mTransparencyBitmap = nullptr;
  4310     mTransparencyBitmapWidth = 0;
  4311     mTransparencyBitmapHeight = 0;
  4313     if (!mShell)
  4314         return;
  4316 #ifdef MOZ_X11
  4317     GdkWindow *window = gtk_widget_get_window(mShell);
  4318     if (!window)
  4319         return;
  4321     Display* xDisplay = GDK_WINDOW_XDISPLAY(window);
  4322     Window xWindow = gdk_x11_window_get_xid(window);
  4324     XShapeCombineMask(xDisplay, xWindow, ShapeBounding, 0, 0, None, ShapeSet);
  4325 #endif
  4328 nsresult
  4329 nsWindow::UpdateTranslucentWindowAlphaInternal(const nsIntRect& aRect,
  4330                                                uint8_t* aAlphas, int32_t aStride)
  4332     if (!mShell) {
  4333         // Pass the request to the toplevel window
  4334         GtkWidget *topWidget = GetToplevelWidget();
  4335         if (!topWidget)
  4336             return NS_ERROR_FAILURE;
  4338         nsWindow *topWindow = get_window_for_gtk_widget(topWidget);
  4339         if (!topWindow)
  4340             return NS_ERROR_FAILURE;
  4342         return topWindow->UpdateTranslucentWindowAlphaInternal(aRect, aAlphas, aStride);
  4345     NS_ASSERTION(mIsTransparent, "Window is not transparent");
  4347     if (mTransparencyBitmap == nullptr) {
  4348         int32_t size = GetBitmapStride(mBounds.width)*mBounds.height;
  4349         mTransparencyBitmap = new gchar[size];
  4350         memset(mTransparencyBitmap, 255, size);
  4351         mTransparencyBitmapWidth = mBounds.width;
  4352         mTransparencyBitmapHeight = mBounds.height;
  4353     } else {
  4354         ResizeTransparencyBitmap();
  4357     nsIntRect rect;
  4358     rect.IntersectRect(aRect, nsIntRect(0, 0, mBounds.width, mBounds.height));
  4360     if (!ChangedMaskBits(mTransparencyBitmap, mBounds.width, mBounds.height,
  4361                          rect, aAlphas, aStride))
  4362         // skip the expensive stuff if the mask bits haven't changed; hopefully
  4363         // this is the common case
  4364         return NS_OK;
  4366     UpdateMaskBits(mTransparencyBitmap, mBounds.width, mBounds.height,
  4367                    rect, aAlphas, aStride);
  4369     if (!mNeedsShow) {
  4370         ApplyTransparencyBitmap();
  4372     return NS_OK;
  4375 void
  4376 nsWindow::GrabPointer(guint32 aTime)
  4378     LOG(("GrabPointer time=0x%08x retry=%d\n",
  4379          (unsigned int)aTime, mRetryPointerGrab));
  4381     mRetryPointerGrab = false;
  4382     sRetryGrabTime = aTime;
  4384     // If the window isn't visible, just set the flag to retry the
  4385     // grab.  When this window becomes visible, the grab will be
  4386     // retried.
  4387     if (!mHasMappedToplevel || mIsFullyObscured) {
  4388         LOG(("GrabPointer: window not visible\n"));
  4389         mRetryPointerGrab = true;
  4390         return;
  4393     if (!mGdkWindow)
  4394         return;
  4396     gint retval;
  4397     retval = gdk_pointer_grab(mGdkWindow, TRUE,
  4398                               (GdkEventMask)(GDK_BUTTON_PRESS_MASK |
  4399                                              GDK_BUTTON_RELEASE_MASK |
  4400                                              GDK_ENTER_NOTIFY_MASK |
  4401                                              GDK_LEAVE_NOTIFY_MASK |
  4402                                              GDK_POINTER_MOTION_MASK),
  4403                               (GdkWindow *)nullptr, nullptr, aTime);
  4405     if (retval == GDK_GRAB_NOT_VIEWABLE) {
  4406         LOG(("GrabPointer: window not viewable; will retry\n"));
  4407         mRetryPointerGrab = true;
  4408     } else if (retval != GDK_GRAB_SUCCESS) {
  4409         LOG(("GrabPointer: pointer grab failed: %i\n", retval));
  4410         // A failed grab indicates that another app has grabbed the pointer.
  4411         // Check for rollup now, because, without the grab, we likely won't
  4412         // get subsequent button press events.
  4413         CheckForRollup(0, 0, false, true);
  4417 void
  4418 nsWindow::ReleaseGrabs(void)
  4420     LOG(("ReleaseGrabs\n"));
  4422     mRetryPointerGrab = false;
  4423     gdk_pointer_ungrab(GDK_CURRENT_TIME);
  4426 GtkWidget *
  4427 nsWindow::GetToplevelWidget()
  4429     if (mShell) {
  4430         return mShell;
  4433     GtkWidget *widget = GetMozContainerWidget();
  4434     if (!widget)
  4435         return nullptr;
  4437     return gtk_widget_get_toplevel(widget);
  4440 GtkWidget *
  4441 nsWindow::GetMozContainerWidget()
  4443     if (!mGdkWindow)
  4444         return nullptr;
  4446     if (mContainer)
  4447         return GTK_WIDGET(mContainer);
  4449     GtkWidget *owningWidget =
  4450         get_gtk_widget_for_gdk_window(mGdkWindow);
  4451     return owningWidget;
  4454 nsWindow *
  4455 nsWindow::GetContainerWindow()
  4457     GtkWidget *owningWidget = GetMozContainerWidget();
  4458     if (!owningWidget)
  4459         return nullptr;
  4461     nsWindow *window = get_window_for_gtk_widget(owningWidget);
  4462     NS_ASSERTION(window, "No nsWindow for container widget");
  4463     return window;
  4466 void
  4467 nsWindow::SetUrgencyHint(GtkWidget *top_window, bool state)
  4469     if (!top_window)
  4470         return;
  4472     gdk_window_set_urgency_hint(gtk_widget_get_window(top_window), state);
  4475 void *
  4476 nsWindow::SetupPluginPort(void)
  4478     if (!mGdkWindow)
  4479         return nullptr;
  4481     if (gdk_window_is_destroyed(mGdkWindow) == TRUE)
  4482         return nullptr;
  4484     Window window = gdk_x11_window_get_xid(mGdkWindow);
  4486     // we have to flush the X queue here so that any plugins that
  4487     // might be running on separate X connections will be able to use
  4488     // this window in case it was just created
  4489 #ifdef MOZ_X11
  4490     XWindowAttributes xattrs;    
  4491     Display *display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
  4492     XGetWindowAttributes(display, window, &xattrs);
  4493     XSelectInput (display, window,
  4494                   xattrs.your_event_mask |
  4495                   SubstructureNotifyMask);
  4497     gdk_window_add_filter(mGdkWindow, plugin_window_filter_func, this);
  4499     XSync(display, False);
  4500 #endif /* MOZ_X11 */
  4502     return (void *)window;
  4505 void
  4506 nsWindow::SetDefaultIcon(void)
  4508     SetIcon(NS_LITERAL_STRING("default"));
  4511 void
  4512 nsWindow::SetPluginType(PluginType aPluginType)
  4514     mPluginType = aPluginType;
  4517 #ifdef MOZ_X11
  4518 void
  4519 nsWindow::SetNonXEmbedPluginFocus()
  4521     if (gPluginFocusWindow == this || mPluginType!=PluginType_NONXEMBED) {
  4522         return;
  4525     if (gPluginFocusWindow) {
  4526         nsRefPtr<nsWindow> kungFuDeathGrip = gPluginFocusWindow;
  4527         gPluginFocusWindow->LoseNonXEmbedPluginFocus();
  4530     LOGFOCUS(("nsWindow::SetNonXEmbedPluginFocus\n"));
  4532     Window curFocusWindow;
  4533     int focusState;
  4535     GdkDisplay *gdkDisplay = gdk_window_get_display(mGdkWindow);
  4536     XGetInputFocus(gdk_x11_display_get_xdisplay(gdkDisplay),
  4537                    &curFocusWindow,
  4538                    &focusState);
  4540     LOGFOCUS(("\t curFocusWindow=%p\n", curFocusWindow));
  4542     GdkWindow* toplevel = gdk_window_get_toplevel(mGdkWindow);
  4543 #if (MOZ_WIDGET_GTK == 2)
  4544     GdkWindow *gdkfocuswin = gdk_window_lookup(curFocusWindow);
  4545 #else
  4546     GdkWindow *gdkfocuswin = gdk_x11_window_lookup_for_display(gdkDisplay,
  4547                                                                curFocusWindow);
  4548 #endif
  4550     // lookup with the focus proxy window is supposed to get the
  4551     // same GdkWindow as toplevel. If the current focused window
  4552     // is not the focus proxy, we return without any change.
  4553     if (gdkfocuswin != toplevel) {
  4554         return;
  4557     // switch the focus from the focus proxy to the plugin window
  4558     mOldFocusWindow = curFocusWindow;
  4559     XRaiseWindow(GDK_WINDOW_XDISPLAY(mGdkWindow),
  4560                  gdk_x11_window_get_xid(mGdkWindow));
  4561     gdk_error_trap_push();
  4562     XSetInputFocus(GDK_WINDOW_XDISPLAY(mGdkWindow),
  4563                    gdk_x11_window_get_xid(mGdkWindow),
  4564                    RevertToNone,
  4565                    CurrentTime);
  4566     gdk_flush();
  4567     gdk_error_trap_pop();
  4568     gPluginFocusWindow = this;
  4569     gdk_window_add_filter(nullptr, plugin_client_message_filter, this);
  4571     LOGFOCUS(("nsWindow::SetNonXEmbedPluginFocus oldfocus=%p new=%p\n",
  4572               mOldFocusWindow, gdk_x11_window_get_xid(mGdkWindow)));
  4575 void
  4576 nsWindow::LoseNonXEmbedPluginFocus()
  4578     LOGFOCUS(("nsWindow::LoseNonXEmbedPluginFocus\n"));
  4580     // This method is only for the nsWindow which contains a
  4581     // Non-XEmbed plugin, for example, JAVA plugin.
  4582     if (gPluginFocusWindow != this || mPluginType!=PluginType_NONXEMBED) {
  4583         return;
  4586     Window curFocusWindow;
  4587     int focusState;
  4589     XGetInputFocus(GDK_WINDOW_XDISPLAY(mGdkWindow),
  4590                    &curFocusWindow,
  4591                    &focusState);
  4593     // we only switch focus between plugin window and focus proxy. If the
  4594     // current focused window is not the plugin window, just removing the
  4595     // event filter that blocks the WM_TAKE_FOCUS is enough. WM and gtk2
  4596     // will take care of the focus later.
  4597     if (!curFocusWindow ||
  4598         curFocusWindow == gdk_x11_window_get_xid(mGdkWindow)) {
  4600         gdk_error_trap_push();
  4601         XRaiseWindow(GDK_WINDOW_XDISPLAY(mGdkWindow),
  4602                      mOldFocusWindow);
  4603         XSetInputFocus(GDK_WINDOW_XDISPLAY(mGdkWindow),
  4604                        mOldFocusWindow,
  4605                        RevertToParent,
  4606                        CurrentTime);
  4607         gdk_flush();
  4608         gdk_error_trap_pop();
  4610     gPluginFocusWindow = nullptr;
  4611     mOldFocusWindow = 0;
  4612     gdk_window_remove_filter(nullptr, plugin_client_message_filter, this);
  4614     LOGFOCUS(("nsWindow::LoseNonXEmbedPluginFocus end\n"));
  4616 #endif /* MOZ_X11 */
  4618 gint
  4619 nsWindow::ConvertBorderStyles(nsBorderStyle aStyle)
  4621     gint w = 0;
  4623     if (aStyle == eBorderStyle_default)
  4624         return -1;
  4626     // note that we don't handle eBorderStyle_close yet
  4627     if (aStyle & eBorderStyle_all)
  4628         w |= GDK_DECOR_ALL;
  4629     if (aStyle & eBorderStyle_border)
  4630         w |= GDK_DECOR_BORDER;
  4631     if (aStyle & eBorderStyle_resizeh)
  4632         w |= GDK_DECOR_RESIZEH;
  4633     if (aStyle & eBorderStyle_title)
  4634         w |= GDK_DECOR_TITLE;
  4635     if (aStyle & eBorderStyle_menu)
  4636         w |= GDK_DECOR_MENU;
  4637     if (aStyle & eBorderStyle_minimize)
  4638         w |= GDK_DECOR_MINIMIZE;
  4639     if (aStyle & eBorderStyle_maximize)
  4640         w |= GDK_DECOR_MAXIMIZE;
  4642     return w;
  4645 NS_IMETHODIMP
  4646 nsWindow::MakeFullScreen(bool aFullScreen)
  4648     LOG(("nsWindow::MakeFullScreen [%p] aFullScreen %d\n",
  4649          (void *)this, aFullScreen));
  4651     if (aFullScreen) {
  4652         if (mSizeMode != nsSizeMode_Fullscreen)
  4653             mLastSizeMode = mSizeMode;
  4655         mSizeMode = nsSizeMode_Fullscreen;
  4656         gtk_window_fullscreen(GTK_WINDOW(mShell));
  4658     else {
  4659         mSizeMode = mLastSizeMode;
  4660         gtk_window_unfullscreen(GTK_WINDOW(mShell));
  4663     NS_ASSERTION(mLastSizeMode != nsSizeMode_Fullscreen,
  4664                  "mLastSizeMode should never be fullscreen");
  4665     return NS_OK;
  4668 NS_IMETHODIMP
  4669 nsWindow::HideWindowChrome(bool aShouldHide)
  4671     if (!mShell) {
  4672         // Pass the request to the toplevel window
  4673         GtkWidget *topWidget = GetToplevelWidget();
  4674         if (!topWidget)
  4675             return NS_ERROR_FAILURE;
  4677         nsWindow *topWindow = get_window_for_gtk_widget(topWidget);
  4678         if (!topWindow)
  4679             return NS_ERROR_FAILURE;
  4681         return topWindow->HideWindowChrome(aShouldHide);
  4684     // Sawfish, metacity, and presumably other window managers get
  4685     // confused if we change the window decorations while the window
  4686     // is visible.
  4687     bool wasVisible = false;
  4688     GdkWindow *shellWindow = gtk_widget_get_window(mShell);
  4689     if (gdk_window_is_visible(shellWindow)) {
  4690         gdk_window_hide(shellWindow);
  4691         wasVisible = true;
  4694     gint wmd;
  4695     if (aShouldHide)
  4696         wmd = 0;
  4697     else
  4698         wmd = ConvertBorderStyles(mBorderStyle);
  4700     if (wmd != -1)
  4701       gdk_window_set_decorations(shellWindow, (GdkWMDecoration) wmd);
  4703     if (wasVisible)
  4704         gdk_window_show(shellWindow);
  4706     // For some window managers, adding or removing window decorations
  4707     // requires unmapping and remapping our toplevel window.  Go ahead
  4708     // and flush the queue here so that we don't end up with a BadWindow
  4709     // error later when this happens (when the persistence timer fires
  4710     // and GetWindowPos is called)
  4711 #ifdef MOZ_X11
  4712     XSync(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()) , False);
  4713 #else
  4714     gdk_flush ();
  4715 #endif /* MOZ_X11 */
  4717     return NS_OK;
  4720 bool
  4721 nsWindow::CheckForRollup(gdouble aMouseX, gdouble aMouseY,
  4722                          bool aIsWheel, bool aAlwaysRollup)
  4724     nsIRollupListener* rollupListener = GetActiveRollupListener();
  4725     nsCOMPtr<nsIWidget> rollupWidget;
  4726     if (rollupListener) {
  4727         rollupWidget = rollupListener->GetRollupWidget();
  4729     if (!rollupWidget) {
  4730         nsBaseWidget::gRollupListener = nullptr;
  4731         return false;
  4734     bool retVal = false;
  4735     GdkWindow *currentPopup =
  4736         (GdkWindow *)rollupWidget->GetNativeData(NS_NATIVE_WINDOW);
  4737     if (aAlwaysRollup || !is_mouse_in_window(currentPopup, aMouseX, aMouseY)) {
  4738         bool rollup = true;
  4739         if (aIsWheel) {
  4740             rollup = rollupListener->ShouldRollupOnMouseWheelEvent();
  4741             retVal = rollupListener->ShouldConsumeOnMouseWheelEvent();
  4743         // if we're dealing with menus, we probably have submenus and
  4744         // we don't want to rollup if the click is in a parent menu of
  4745         // the current submenu
  4746         uint32_t popupsToRollup = UINT32_MAX;
  4747         if (!aAlwaysRollup) {
  4748             nsAutoTArray<nsIWidget*, 5> widgetChain;
  4749             uint32_t sameTypeCount = rollupListener->GetSubmenuWidgetChain(&widgetChain);
  4750             for (uint32_t i=0; i<widgetChain.Length(); ++i) {
  4751                 nsIWidget* widget = widgetChain[i];
  4752                 GdkWindow* currWindow =
  4753                     (GdkWindow*) widget->GetNativeData(NS_NATIVE_WINDOW);
  4754                 if (is_mouse_in_window(currWindow, aMouseX, aMouseY)) {
  4755                   // don't roll up if the mouse event occurred within a
  4756                   // menu of the same type. If the mouse event occurred
  4757                   // in a menu higher than that, roll up, but pass the
  4758                   // number of popups to Rollup so that only those of the
  4759                   // same type close up.
  4760                   if (i < sameTypeCount) {
  4761                     rollup = false;
  4763                   else {
  4764                     popupsToRollup = sameTypeCount;
  4766                   break;
  4768             } // foreach parent menu widget
  4769         } // if rollup listener knows about menus
  4771         // if we've determined that we should still rollup, do it.
  4772         bool usePoint = !aIsWheel && !aAlwaysRollup;
  4773         nsIntPoint point(aMouseX, aMouseY);
  4774         if (rollup && rollupListener->Rollup(popupsToRollup, usePoint ? &point : nullptr, nullptr)) {
  4775             retVal = true;
  4778     return retVal;
  4781 /* static */
  4782 bool
  4783 nsWindow::DragInProgress(void)
  4785     nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
  4787     if (!dragService)
  4788         return false;
  4790     nsCOMPtr<nsIDragSession> currentDragSession;
  4791     dragService->GetCurrentSession(getter_AddRefs(currentDragSession));
  4793     return currentDragSession != nullptr;
  4796 static bool
  4797 is_mouse_in_window (GdkWindow* aWindow, gdouble aMouseX, gdouble aMouseY)
  4799     gint x = 0;
  4800     gint y = 0;
  4801     gint w, h;
  4803     gint offsetX = 0;
  4804     gint offsetY = 0;
  4806     GdkWindow *window = aWindow;
  4808     while (window) {
  4809         gint tmpX = 0;
  4810         gint tmpY = 0;
  4812         gdk_window_get_position(window, &tmpX, &tmpY);
  4813         GtkWidget *widget = get_gtk_widget_for_gdk_window(window);
  4815         // if this is a window, compute x and y given its origin and our
  4816         // offset
  4817         if (GTK_IS_WINDOW(widget)) {
  4818             x = tmpX + offsetX;
  4819             y = tmpY + offsetY;
  4820             break;
  4823         offsetX += tmpX;
  4824         offsetY += tmpY;
  4825         window = gdk_window_get_parent(window);
  4828 #if (MOZ_WIDGET_GTK == 2)
  4829     gdk_drawable_get_size(aWindow, &w, &h);
  4830 #else
  4831     w = gdk_window_get_width(aWindow);
  4832     h = gdk_window_get_height(aWindow);
  4833 #endif
  4835     if (aMouseX > x && aMouseX < x + w &&
  4836         aMouseY > y && aMouseY < y + h)
  4837         return true;
  4839     return false;
  4842 static nsWindow *
  4843 get_window_for_gtk_widget(GtkWidget *widget)
  4845     gpointer user_data = g_object_get_data(G_OBJECT(widget), "nsWindow");
  4847     return static_cast<nsWindow *>(user_data);
  4850 static nsWindow *
  4851 get_window_for_gdk_window(GdkWindow *window)
  4853     gpointer user_data = g_object_get_data(G_OBJECT(window), "nsWindow");
  4855     return static_cast<nsWindow *>(user_data);
  4858 static GtkWidget *
  4859 get_gtk_widget_for_gdk_window(GdkWindow *window)
  4861     gpointer user_data = nullptr;
  4862     gdk_window_get_user_data(window, &user_data);
  4864     return GTK_WIDGET(user_data);
  4867 static GdkCursor *
  4868 get_gtk_cursor(nsCursor aCursor)
  4870     GdkCursor *gdkcursor = nullptr;
  4871     uint8_t newType = 0xff;
  4873     if ((gdkcursor = gCursorCache[aCursor])) {
  4874         return gdkcursor;
  4877     GdkDisplay *defaultDisplay = gdk_display_get_default();
  4879     // The strategy here is to use standard GDK cursors, and, if not available,
  4880     // load by standard name with gdk_cursor_new_from_name.
  4881     // Spec is here: http://www.freedesktop.org/wiki/Specifications/cursor-spec/
  4882     switch (aCursor) {
  4883     case eCursor_standard:
  4884         gdkcursor = gdk_cursor_new(GDK_LEFT_PTR);
  4885         break;
  4886     case eCursor_wait:
  4887         gdkcursor = gdk_cursor_new(GDK_WATCH);
  4888         break;
  4889     case eCursor_select:
  4890         gdkcursor = gdk_cursor_new(GDK_XTERM);
  4891         break;
  4892     case eCursor_hyperlink:
  4893         gdkcursor = gdk_cursor_new(GDK_HAND2);
  4894         break;
  4895     case eCursor_n_resize:
  4896         gdkcursor = gdk_cursor_new(GDK_TOP_SIDE);
  4897         break;
  4898     case eCursor_s_resize:
  4899         gdkcursor = gdk_cursor_new(GDK_BOTTOM_SIDE);
  4900         break;
  4901     case eCursor_w_resize:
  4902         gdkcursor = gdk_cursor_new(GDK_LEFT_SIDE);
  4903         break;
  4904     case eCursor_e_resize:
  4905         gdkcursor = gdk_cursor_new(GDK_RIGHT_SIDE);
  4906         break;
  4907     case eCursor_nw_resize:
  4908         gdkcursor = gdk_cursor_new(GDK_TOP_LEFT_CORNER);
  4909         break;
  4910     case eCursor_se_resize:
  4911         gdkcursor = gdk_cursor_new(GDK_BOTTOM_RIGHT_CORNER);
  4912         break;
  4913     case eCursor_ne_resize:
  4914         gdkcursor = gdk_cursor_new(GDK_TOP_RIGHT_CORNER);
  4915         break;
  4916     case eCursor_sw_resize:
  4917         gdkcursor = gdk_cursor_new(GDK_BOTTOM_LEFT_CORNER);
  4918         break;
  4919     case eCursor_crosshair:
  4920         gdkcursor = gdk_cursor_new(GDK_CROSSHAIR);
  4921         break;
  4922     case eCursor_move:
  4923         gdkcursor = gdk_cursor_new(GDK_FLEUR);
  4924         break;
  4925     case eCursor_help:
  4926         gdkcursor = gdk_cursor_new(GDK_QUESTION_ARROW);
  4927         break;
  4928     case eCursor_copy: // CSS3
  4929         gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "copy");
  4930         if (!gdkcursor)
  4931             newType = MOZ_CURSOR_COPY;
  4932         break;
  4933     case eCursor_alias:
  4934         gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "alias");
  4935         if (!gdkcursor)
  4936             newType = MOZ_CURSOR_ALIAS;
  4937         break;
  4938     case eCursor_context_menu:
  4939         gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "context-menu");
  4940         if (!gdkcursor)
  4941             newType = MOZ_CURSOR_CONTEXT_MENU;
  4942         break;
  4943     case eCursor_cell:
  4944         gdkcursor = gdk_cursor_new(GDK_PLUS);
  4945         break;
  4946     // Those two aren’t standardized. Trying both KDE’s and GNOME’s names
  4947     case eCursor_grab:
  4948         gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "openhand");
  4949         if (!gdkcursor)
  4950             gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "hand1");
  4951         if (!gdkcursor)
  4952             newType = MOZ_CURSOR_HAND_GRAB;
  4953         break;
  4954     case eCursor_grabbing:
  4955         gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "closedhand");
  4956         if (!gdkcursor)
  4957             gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "grabbing");
  4958         if (!gdkcursor)
  4959             newType = MOZ_CURSOR_HAND_GRABBING;
  4960         break;
  4961     case eCursor_spinning:
  4962         gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "progress");
  4963         if (!gdkcursor)
  4964             newType = MOZ_CURSOR_SPINNING;
  4965         break;
  4966     case eCursor_zoom_in:
  4967         newType = MOZ_CURSOR_ZOOM_IN;
  4968         break;
  4969     case eCursor_zoom_out:
  4970         newType = MOZ_CURSOR_ZOOM_OUT;
  4971         break;
  4972     case eCursor_not_allowed:
  4973         gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "not-allowed");
  4974         if (!gdkcursor) // nonstandard, yet common
  4975             gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "crossed_circle");
  4976         if (!gdkcursor)
  4977             newType = MOZ_CURSOR_NOT_ALLOWED;
  4978         break;
  4979     case eCursor_no_drop:
  4980         gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "no-drop");
  4981         if (!gdkcursor) // this nonstandard sequence makes it work on KDE and GNOME
  4982             gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "forbidden");
  4983         if (!gdkcursor)
  4984             gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "circle");
  4985         if (!gdkcursor)
  4986             newType = MOZ_CURSOR_NOT_ALLOWED;
  4987         break;
  4988     case eCursor_vertical_text:
  4989         newType = MOZ_CURSOR_VERTICAL_TEXT;
  4990         break;
  4991     case eCursor_all_scroll:
  4992         gdkcursor = gdk_cursor_new(GDK_FLEUR);
  4993         break;
  4994     case eCursor_nesw_resize:
  4995         gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "size_bdiag");
  4996         if (!gdkcursor)
  4997             newType = MOZ_CURSOR_NESW_RESIZE;
  4998         break;
  4999     case eCursor_nwse_resize:
  5000         gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "size_fdiag");
  5001         if (!gdkcursor)
  5002             newType = MOZ_CURSOR_NWSE_RESIZE;
  5003         break;
  5004     case eCursor_ns_resize:
  5005         gdkcursor = gdk_cursor_new(GDK_SB_V_DOUBLE_ARROW);
  5006         break;
  5007     case eCursor_ew_resize:
  5008         gdkcursor = gdk_cursor_new(GDK_SB_H_DOUBLE_ARROW);
  5009         break;
  5010     // Here, two better fitting cursors exist in some cursor themes. Try those first
  5011     case eCursor_row_resize:
  5012         gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "split_v");
  5013         if (!gdkcursor)
  5014             gdkcursor = gdk_cursor_new(GDK_SB_V_DOUBLE_ARROW);
  5015         break;
  5016     case eCursor_col_resize:
  5017         gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "split_h");
  5018         if (!gdkcursor)
  5019             gdkcursor = gdk_cursor_new(GDK_SB_H_DOUBLE_ARROW);
  5020         break;
  5021     case eCursor_none:
  5022         newType = MOZ_CURSOR_NONE;
  5023         break;
  5024     default:
  5025         NS_ASSERTION(aCursor, "Invalid cursor type");
  5026         gdkcursor = gdk_cursor_new(GDK_LEFT_PTR);
  5027         break;
  5030     // If by now we don't have a xcursor, this means we have to make a custom
  5031     // one. First, we try creating a named cursor based on the hash of our
  5032     // custom bitmap, as libXcursor has some magic to convert bitmapped cursors
  5033     // to themed cursors
  5034     if (newType != 0xFF && GtkCursors[newType].hash) {
  5035         gdkcursor = gdk_cursor_new_from_name(defaultDisplay, GtkCursors[newType].hash);
  5038     // If we still don't have a xcursor, we now really create a bitmap cursor
  5039     if (newType != 0xff && !gdkcursor) {
  5040         GdkPixbuf * cursor_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32);
  5041         if (!cursor_pixbuf)
  5042             return nullptr;
  5044         guchar *data = gdk_pixbuf_get_pixels(cursor_pixbuf);
  5046         // Read data from GtkCursors and compose RGBA surface from 1bit bitmap and mask
  5047         // GtkCursors bits and mask are 32x32 monochrome bitmaps (1 bit for each pixel)
  5048         // so it's 128 byte array (4 bytes for are one bitmap row and there are 32 rows here).
  5049         const unsigned char *bits = GtkCursors[newType].bits;
  5050         const unsigned char *mask_bits = GtkCursors[newType].mask_bits;
  5052         for (int i = 0; i < 128; i++) {
  5053             char bit = *bits++;
  5054             char mask = *mask_bits++;
  5055             for (int j = 0; j < 8; j++) {
  5056                 unsigned char pix = ~(((bit >> j) & 0x01) * 0xff);
  5057                 *data++ = pix;
  5058                 *data++ = pix;
  5059                 *data++ = pix;
  5060                 *data++ = (((mask >> j) & 0x01) * 0xff);
  5064         gdkcursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), cursor_pixbuf,
  5065                                                GtkCursors[newType].hot_x,
  5066                                                GtkCursors[newType].hot_y);
  5068         g_object_unref(cursor_pixbuf);
  5071     gCursorCache[aCursor] = gdkcursor;
  5073     return gdkcursor;
  5076 // gtk callbacks
  5078 #if (MOZ_WIDGET_GTK == 2)
  5079 static gboolean
  5080 expose_event_cb(GtkWidget *widget, GdkEventExpose *event)
  5082     nsRefPtr<nsWindow> window = get_window_for_gdk_window(event->window);
  5083     if (!window)
  5084         return FALSE;
  5086     window->OnExposeEvent(event);
  5087     return FALSE;
  5089 #else
  5090 void
  5091 draw_window_of_widget(GtkWidget *widget, GdkWindow *aWindow, cairo_t *cr)
  5093     if (gtk_cairo_should_draw_window(cr, aWindow)) {
  5094         nsRefPtr<nsWindow> window = get_window_for_gdk_window(aWindow);
  5095         if (!window) {
  5096             NS_WARNING("Cannot get nsWindow from GtkWidget");
  5098         else {      
  5099             cairo_save(cr);      
  5100             gtk_cairo_transform_to_window(cr, widget, aWindow);  
  5101             // TODO - window->OnExposeEvent() can destroy this or other windows,
  5102             // do we need to handle it somehow?
  5103             window->OnExposeEvent(cr);
  5104             cairo_restore(cr);
  5108     GList *children = gdk_window_get_children(aWindow);
  5109     GList *child = children;
  5110     while (child) {
  5111         GdkWindow *window = GDK_WINDOW(child->data);
  5112         gpointer windowWidget;
  5113         gdk_window_get_user_data(window, &windowWidget);
  5114         if (windowWidget == widget) {
  5115             draw_window_of_widget(widget, window, cr);
  5117         child = g_list_next(child);
  5119     g_list_free(children);
  5122 /* static */
  5123 gboolean
  5124 expose_event_cb(GtkWidget *widget, cairo_t *cr)
  5126     draw_window_of_widget(widget, gtk_widget_get_window(widget), cr);
  5127     return FALSE;
  5129 #endif //MOZ_WIDGET_GTK2
  5131 static gboolean
  5132 configure_event_cb(GtkWidget *widget,
  5133                    GdkEventConfigure *event)
  5135     nsRefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
  5136     if (!window)
  5137         return FALSE;
  5139     return window->OnConfigureEvent(widget, event);
  5142 static void
  5143 container_unrealize_cb (GtkWidget *widget)
  5145     nsRefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
  5146     if (!window)
  5147         return;
  5149     window->OnContainerUnrealize();
  5152 static void
  5153 size_allocate_cb (GtkWidget *widget, GtkAllocation *allocation)
  5155     nsRefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
  5156     if (!window)
  5157         return;
  5159     window->OnSizeAllocate(allocation);
  5162 static gboolean
  5163 delete_event_cb(GtkWidget *widget, GdkEventAny *event)
  5165     nsRefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
  5166     if (!window)
  5167         return FALSE;
  5169     window->OnDeleteEvent();
  5171     return TRUE;
  5174 static gboolean
  5175 enter_notify_event_cb(GtkWidget *widget,
  5176                       GdkEventCrossing *event)
  5178     nsRefPtr<nsWindow> window = get_window_for_gdk_window(event->window);
  5179     if (!window)
  5180         return TRUE;
  5182     window->OnEnterNotifyEvent(event);
  5184     return TRUE;
  5187 static gboolean
  5188 leave_notify_event_cb(GtkWidget *widget,
  5189                       GdkEventCrossing *event)
  5191     if (is_parent_grab_leave(event)) {
  5192         return TRUE;
  5195     // bug 369599: Suppress LeaveNotify events caused by pointer grabs to
  5196     // avoid generating spurious mouse exit events.
  5197     gint x = gint(event->x_root);
  5198     gint y = gint(event->y_root);
  5199     GdkDisplay* display = gtk_widget_get_display(widget);
  5200     GdkWindow* winAtPt = gdk_display_get_window_at_pointer(display, &x, &y);
  5201     if (winAtPt == event->window) {
  5202         return TRUE;
  5205     nsRefPtr<nsWindow> window = get_window_for_gdk_window(event->window);
  5206     if (!window)
  5207         return TRUE;
  5209     window->OnLeaveNotifyEvent(event);
  5211     return TRUE;
  5214 static nsWindow*
  5215 GetFirstNSWindowForGDKWindow(GdkWindow *aGdkWindow)
  5217     nsWindow* window;
  5218     while (!(window = get_window_for_gdk_window(aGdkWindow))) {
  5219         // The event has bubbled to the moz_container widget as passed into each caller's *widget parameter,
  5220         // but its corresponding nsWindow is an ancestor of the window that we need.  Instead, look at
  5221         // event->window and find the first ancestor nsWindow of it because event->window may be in a plugin.
  5222         aGdkWindow = gdk_window_get_parent(aGdkWindow);
  5223         if (!aGdkWindow) {
  5224             window = nullptr;
  5225             break;
  5228     return window;
  5231 static gboolean
  5232 motion_notify_event_cb(GtkWidget *widget, GdkEventMotion *event)
  5234     UpdateLastInputEventTime(event);
  5236     nsWindow *window = GetFirstNSWindowForGDKWindow(event->window);
  5237     if (!window)
  5238         return FALSE;
  5240     window->OnMotionNotifyEvent(event);
  5242     return TRUE;
  5245 static gboolean
  5246 button_press_event_cb(GtkWidget *widget, GdkEventButton *event)
  5248     UpdateLastInputEventTime(event);
  5250     nsWindow *window = GetFirstNSWindowForGDKWindow(event->window);
  5251     if (!window)
  5252         return FALSE;
  5254     window->OnButtonPressEvent(event);
  5256     return TRUE;
  5259 static gboolean
  5260 button_release_event_cb(GtkWidget *widget, GdkEventButton *event)
  5262     UpdateLastInputEventTime(event);
  5264     nsWindow *window = GetFirstNSWindowForGDKWindow(event->window);
  5265     if (!window)
  5266         return FALSE;
  5268     window->OnButtonReleaseEvent(event);
  5270     return TRUE;
  5273 static gboolean
  5274 focus_in_event_cb(GtkWidget *widget, GdkEventFocus *event)
  5276     nsRefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
  5277     if (!window)
  5278         return FALSE;
  5280     window->OnContainerFocusInEvent(event);
  5282     return FALSE;
  5285 static gboolean
  5286 focus_out_event_cb(GtkWidget *widget, GdkEventFocus *event)
  5288     nsRefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
  5289     if (!window)
  5290         return FALSE;
  5292     window->OnContainerFocusOutEvent(event);
  5294     return FALSE;
  5297 #ifdef MOZ_X11
  5298 // For long-lived popup windows that don't really take focus themselves but
  5299 // may have elements that accept keyboard input when the parent window is
  5300 // active, focus is handled specially.  These windows include noautohide
  5301 // panels.  (This special handling is not necessary for temporary popups where
  5302 // the keyboard is grabbed.)
  5303 //
  5304 // Mousing over or clicking on these windows should not cause them to steal
  5305 // focus from their parent windows, so, the input field of WM_HINTS is set to
  5306 // False to request that the window manager not set the input focus to this
  5307 // window.  http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7
  5308 //
  5309 // However, these windows can still receive WM_TAKE_FOCUS messages from the
  5310 // window manager, so they can still detect when the user has indicated that
  5311 // they wish to direct keyboard input at these windows.  When the window
  5312 // manager offers focus to these windows (after a mouse over or click, for
  5313 // example), a request to make the parent window active is issued.  When the
  5314 // parent window becomes active, keyboard events will be received.
  5316 static GdkFilterReturn
  5317 popup_take_focus_filter(GdkXEvent *gdk_xevent,
  5318                         GdkEvent *event,
  5319                         gpointer data)
  5321     XEvent* xevent = static_cast<XEvent*>(gdk_xevent);
  5322     if (xevent->type != ClientMessage)
  5323         return GDK_FILTER_CONTINUE;
  5325     XClientMessageEvent& xclient = xevent->xclient;
  5326     if (xclient.message_type != gdk_x11_get_xatom_by_name("WM_PROTOCOLS"))
  5327         return GDK_FILTER_CONTINUE;
  5329     Atom atom = xclient.data.l[0];
  5330     if (atom != gdk_x11_get_xatom_by_name("WM_TAKE_FOCUS"))
  5331         return GDK_FILTER_CONTINUE;
  5333     guint32 timestamp = xclient.data.l[1];
  5335     GtkWidget* widget = get_gtk_widget_for_gdk_window(event->any.window);
  5336     if (!widget)
  5337         return GDK_FILTER_CONTINUE;
  5339     GtkWindow* parent = gtk_window_get_transient_for(GTK_WINDOW(widget));
  5340     if (!parent)
  5341         return GDK_FILTER_CONTINUE;
  5343     if (gtk_window_is_active(parent))
  5344         return GDK_FILTER_REMOVE; // leave input focus on the parent
  5346     GdkWindow* parent_window = gtk_widget_get_window(GTK_WIDGET(parent));
  5347     if (!parent_window)
  5348         return GDK_FILTER_CONTINUE;
  5350     // In case the parent has not been deconified.
  5351     gdk_window_show_unraised(parent_window);
  5353     // Request focus on the parent window.
  5354     // Use gdk_window_focus rather than gtk_window_present to avoid
  5355     // raising the parent window.
  5356     gdk_window_focus(parent_window, timestamp);
  5357     return GDK_FILTER_REMOVE;
  5360 static GdkFilterReturn
  5361 plugin_window_filter_func(GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
  5363     GdkWindow  *plugin_window;
  5364     XEvent     *xevent;
  5365     Window      xeventWindow;
  5367     nsRefPtr<nsWindow> nswindow = (nsWindow*)data;
  5368     GdkFilterReturn return_val;
  5370     xevent = (XEvent *)gdk_xevent;
  5371     return_val = GDK_FILTER_CONTINUE;
  5373     switch (xevent->type)
  5375         case CreateNotify:
  5376         case ReparentNotify:
  5377             if (xevent->type==CreateNotify) {
  5378                 xeventWindow = xevent->xcreatewindow.window;
  5380             else {
  5381                 if (xevent->xreparent.event != xevent->xreparent.parent)
  5382                     break;
  5383                 xeventWindow = xevent->xreparent.window;
  5385 #if (MOZ_WIDGET_GTK == 2)
  5386             plugin_window = gdk_window_lookup(xeventWindow);
  5387 #else
  5388             plugin_window = gdk_x11_window_lookup_for_display(
  5389                                   gdk_x11_lookup_xdisplay(xevent->xcreatewindow.display), xeventWindow);
  5390 #endif        
  5391             if (plugin_window) {
  5392                 GtkWidget *widget =
  5393                     get_gtk_widget_for_gdk_window(plugin_window);
  5395 // TODO GTK3
  5396 #if (MOZ_WIDGET_GTK == 2)
  5397                 if (GTK_IS_XTBIN(widget)) {
  5398                     nswindow->SetPluginType(nsWindow::PluginType_NONXEMBED);
  5399                     break;
  5401                 else 
  5402 #endif
  5403                 if(GTK_IS_SOCKET(widget)) {
  5404                     if (!g_object_get_data(G_OBJECT(widget), "enable-xt-focus")) {
  5405                         nswindow->SetPluginType(nsWindow::PluginType_XEMBED);
  5406                         break;
  5410             nswindow->SetPluginType(nsWindow::PluginType_NONXEMBED);
  5411             return_val = GDK_FILTER_REMOVE;
  5412             break;
  5413         case EnterNotify:
  5414             nswindow->SetNonXEmbedPluginFocus();
  5415             break;
  5416         case DestroyNotify:
  5417             gdk_window_remove_filter
  5418                 ((GdkWindow*)(nswindow->GetNativeData(NS_NATIVE_WINDOW)),
  5419                  plugin_window_filter_func,
  5420                  nswindow);
  5421             // Currently we consider all plugins are non-xembed and calls
  5422             // LoseNonXEmbedPluginFocus without any checking.
  5423             nswindow->LoseNonXEmbedPluginFocus();
  5424             break;
  5425         default:
  5426             break;
  5428     return return_val;
  5431 static GdkFilterReturn
  5432 plugin_client_message_filter(GdkXEvent *gdk_xevent,
  5433                              GdkEvent *event,
  5434                              gpointer data)
  5436     XEvent    *xevent;
  5437     xevent = (XEvent *)gdk_xevent;
  5439     GdkFilterReturn return_val;
  5440     return_val = GDK_FILTER_CONTINUE;
  5442     if (!gPluginFocusWindow || xevent->type!=ClientMessage) {
  5443         return return_val;
  5446     // When WM sends out WM_TAKE_FOCUS, gtk2 will use XSetInputFocus
  5447     // to set the focus to the focus proxy. To prevent this happen
  5448     // while the focus is on the plugin, we filter the WM_TAKE_FOCUS
  5449     // out.
  5450     if (gdk_x11_get_xatom_by_name("WM_PROTOCOLS")
  5451             != xevent->xclient.message_type) {
  5452         return return_val;
  5455     if ((Atom) xevent->xclient.data.l[0] ==
  5456             gdk_x11_get_xatom_by_name("WM_TAKE_FOCUS")) {
  5457         // block it from gtk2.0 focus proxy
  5458         return_val = GDK_FILTER_REMOVE;
  5461     return return_val;
  5463 #endif /* MOZ_X11 */
  5465 static gboolean
  5466 key_press_event_cb(GtkWidget *widget, GdkEventKey *event)
  5468     LOG(("key_press_event_cb\n"));
  5470     UpdateLastInputEventTime(event);
  5472     // find the window with focus and dispatch this event to that widget
  5473     nsWindow *window = get_window_for_gtk_widget(widget);
  5474     if (!window)
  5475         return FALSE;
  5477     nsRefPtr<nsWindow> focusWindow = gFocusWindow ? gFocusWindow : window;
  5479 #ifdef MOZ_X11
  5480     // Keyboard repeat can cause key press events to queue up when there are
  5481     // slow event handlers (bug 301029).  Throttle these events by removing
  5482     // consecutive pending duplicate KeyPress events to the same window.
  5483     // We use the event time of the last one.
  5484     // Note: GDK calls XkbSetDetectableAutorepeat so that KeyRelease events
  5485     // are generated only when the key is physically released.
  5486 #define NS_GDKEVENT_MATCH_MASK 0x1FFF /* GDK_SHIFT_MASK .. GDK_BUTTON5_MASK */
  5487     GdkDisplay* gdkDisplay = gtk_widget_get_display(widget);
  5488     Display* dpy = GDK_DISPLAY_XDISPLAY(gdkDisplay);
  5489     while (XPending(dpy)) {
  5490         XEvent next_event;
  5491         XPeekEvent(dpy, &next_event);
  5492         GdkWindow* nextGdkWindow =
  5493             gdk_x11_window_lookup_for_display(gdkDisplay, next_event.xany.window);
  5494         if (nextGdkWindow != event->window ||
  5495             next_event.type != KeyPress ||
  5496             next_event.xkey.keycode != event->hardware_keycode ||
  5497             next_event.xkey.state != (event->state & NS_GDKEVENT_MATCH_MASK)) {
  5498             break;
  5500         XNextEvent(dpy, &next_event);
  5501         event->time = next_event.xkey.time;
  5503 #endif
  5505     return focusWindow->OnKeyPressEvent(event);
  5508 static gboolean
  5509 key_release_event_cb(GtkWidget *widget, GdkEventKey *event)
  5511     LOG(("key_release_event_cb\n"));
  5513     UpdateLastInputEventTime(event);
  5515     // find the window with focus and dispatch this event to that widget
  5516     nsWindow *window = get_window_for_gtk_widget(widget);
  5517     if (!window)
  5518         return FALSE;
  5520     nsRefPtr<nsWindow> focusWindow = gFocusWindow ? gFocusWindow : window;
  5522     return focusWindow->OnKeyReleaseEvent(event);
  5525 static gboolean
  5526 scroll_event_cb(GtkWidget *widget, GdkEventScroll *event)
  5528     nsWindow *window = GetFirstNSWindowForGDKWindow(event->window);
  5529     if (!window)
  5530         return FALSE;
  5532     window->OnScrollEvent(event);
  5534     return TRUE;
  5537 static gboolean
  5538 visibility_notify_event_cb (GtkWidget *widget, GdkEventVisibility *event)
  5540     nsRefPtr<nsWindow> window = get_window_for_gdk_window(event->window);
  5541     if (!window)
  5542         return FALSE;
  5544     window->OnVisibilityNotifyEvent(event);
  5546     return TRUE;
  5549 static void
  5550 hierarchy_changed_cb (GtkWidget *widget,
  5551                       GtkWidget *previous_toplevel)
  5553     GtkWidget *toplevel = gtk_widget_get_toplevel(widget);
  5554     GdkWindowState old_window_state = GDK_WINDOW_STATE_WITHDRAWN;
  5555     GdkEventWindowState event;
  5557     event.new_window_state = GDK_WINDOW_STATE_WITHDRAWN;
  5559     if (GTK_IS_WINDOW(previous_toplevel)) {
  5560         g_signal_handlers_disconnect_by_func(previous_toplevel,
  5561                                              FuncToGpointer(window_state_event_cb),
  5562                                              widget);
  5563         GdkWindow *win = gtk_widget_get_window(previous_toplevel);
  5564         if (win) {
  5565             old_window_state = gdk_window_get_state(win);
  5569     if (GTK_IS_WINDOW(toplevel)) {
  5570         g_signal_connect_swapped(toplevel, "window-state-event",
  5571                                  G_CALLBACK(window_state_event_cb), widget);
  5572         GdkWindow *win = gtk_widget_get_window(toplevel);
  5573         if (win) {
  5574             event.new_window_state = gdk_window_get_state(win);
  5578     event.changed_mask = static_cast<GdkWindowState>
  5579         (old_window_state ^ event.new_window_state);
  5581     if (event.changed_mask) {
  5582         event.type = GDK_WINDOW_STATE;
  5583         event.window = nullptr;
  5584         event.send_event = TRUE;
  5585         window_state_event_cb(widget, &event);
  5589 static gboolean
  5590 window_state_event_cb (GtkWidget *widget, GdkEventWindowState *event)
  5592     nsRefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
  5593     if (!window)
  5594         return FALSE;
  5596     window->OnWindowStateEvent(widget, event);
  5598     return FALSE;
  5601 static void
  5602 theme_changed_cb (GtkSettings *settings, GParamSpec *pspec, nsWindow *data)
  5604     nsRefPtr<nsWindow> window = data;
  5605     window->ThemeChanged();
  5608 //////////////////////////////////////////////////////////////////////
  5609 // These are all of our drag and drop operations
  5611 void
  5612 nsWindow::InitDragEvent(WidgetDragEvent &aEvent)
  5614     // set the keyboard modifiers
  5615     guint modifierState = KeymapWrapper::GetCurrentModifierState();
  5616     KeymapWrapper::InitInputEvent(aEvent, modifierState);
  5619 static gboolean
  5620 drag_motion_event_cb(GtkWidget *aWidget,
  5621                      GdkDragContext *aDragContext,
  5622                      gint aX,
  5623                      gint aY,
  5624                      guint aTime,
  5625                      gpointer aData)
  5627     nsRefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
  5628     if (!window)
  5629         return FALSE;
  5631     // figure out which internal widget this drag motion actually happened on
  5632     nscoord retx = 0;
  5633     nscoord rety = 0;
  5635     GdkWindow *innerWindow =
  5636         get_inner_gdk_window(gtk_widget_get_window(aWidget), aX, aY,
  5637                              &retx, &rety);
  5638     nsRefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow);
  5640     if (!innerMostWindow) {
  5641         innerMostWindow = window;
  5644     LOGDRAG(("nsWindow drag-motion signal for %p\n", (void*)innerMostWindow));
  5646     return nsDragService::GetInstance()->
  5647         ScheduleMotionEvent(innerMostWindow, aDragContext,
  5648                             nsIntPoint(retx, rety), aTime);
  5651 static void
  5652 drag_leave_event_cb(GtkWidget *aWidget,
  5653                     GdkDragContext *aDragContext,
  5654                     guint aTime,
  5655                     gpointer aData)
  5657     nsRefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
  5658     if (!window)
  5659         return;
  5661     nsDragService *dragService = nsDragService::GetInstance();
  5663     nsWindow *mostRecentDragWindow = dragService->GetMostRecentDestWindow();
  5664     if (!mostRecentDragWindow) {
  5665         // This can happen when the target will not accept a drop.  A GTK drag
  5666         // source sends the leave message to the destination before the
  5667         // drag-failed signal on the source widget, but the leave message goes
  5668         // via the X server, and so doesn't get processed at least until the
  5669         // event loop runs again.
  5670         return;
  5673     GtkWidget *mozContainer = mostRecentDragWindow->GetMozContainerWidget();
  5674     if (aWidget != mozContainer)
  5676         // When the drag moves between widgets, GTK can send leave signal for
  5677         // the old widget after the motion or drop signal for the new widget.
  5678         // We'll send the leave event when the motion or drop event is run.
  5679         return;
  5682     LOGDRAG(("nsWindow drag-leave signal for %p\n",
  5683              (void*)mostRecentDragWindow));
  5685     dragService->ScheduleLeaveEvent();
  5689 static gboolean
  5690 drag_drop_event_cb(GtkWidget *aWidget,
  5691                    GdkDragContext *aDragContext,
  5692                    gint aX,
  5693                    gint aY,
  5694                    guint aTime,
  5695                    gpointer aData)
  5697     nsRefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
  5698     if (!window)
  5699         return FALSE;
  5701     // figure out which internal widget this drag motion actually happened on
  5702     nscoord retx = 0;
  5703     nscoord rety = 0;
  5705     GdkWindow *innerWindow =
  5706         get_inner_gdk_window(gtk_widget_get_window(aWidget), aX, aY,
  5707                              &retx, &rety);
  5708     nsRefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow);
  5710     if (!innerMostWindow) {
  5711         innerMostWindow = window;
  5714     LOGDRAG(("nsWindow drag-drop signal for %p\n", (void*)innerMostWindow));
  5716     return nsDragService::GetInstance()->
  5717         ScheduleDropEvent(innerMostWindow, aDragContext,
  5718                           nsIntPoint(retx, rety), aTime);
  5721 static void
  5722 drag_data_received_event_cb(GtkWidget *aWidget,
  5723                             GdkDragContext *aDragContext,
  5724                             gint aX,
  5725                             gint aY,
  5726                             GtkSelectionData  *aSelectionData,
  5727                             guint aInfo,
  5728                             guint aTime,
  5729                             gpointer aData)
  5731     nsRefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
  5732     if (!window)
  5733         return;
  5735     window->OnDragDataReceivedEvent(aWidget,
  5736                                     aDragContext,
  5737                                     aX, aY,
  5738                                     aSelectionData,
  5739                                     aInfo, aTime, aData);
  5742 static nsresult
  5743 initialize_prefs(void)
  5745     gRaiseWindows =
  5746         Preferences::GetBool("mozilla.widget.raise-on-setfocus", true);
  5748     return NS_OK;
  5751 static GdkWindow *
  5752 get_inner_gdk_window (GdkWindow *aWindow,
  5753                       gint x, gint y,
  5754                       gint *retx, gint *rety)
  5756     gint cx, cy, cw, ch;
  5757     GList *children = gdk_window_peek_children(aWindow);
  5758     for (GList *child = g_list_last(children);
  5759          child;
  5760          child = g_list_previous(child)) {
  5761         GdkWindow *childWindow = (GdkWindow *) child->data;
  5762         if (get_window_for_gdk_window(childWindow)) {
  5763 #if (MOZ_WIDGET_GTK == 2)
  5764             gdk_window_get_geometry(childWindow, &cx, &cy, &cw, &ch, nullptr);
  5765 #else
  5766             gdk_window_get_geometry(childWindow, &cx, &cy, &cw, &ch);
  5767 #endif
  5768             if ((cx < x) && (x < (cx + cw)) &&
  5769                 (cy < y) && (y < (cy + ch)) &&
  5770                 gdk_window_is_visible(childWindow)) {
  5771                 return get_inner_gdk_window(childWindow,
  5772                                             x - cx, y - cy,
  5773                                             retx, rety);
  5777     *retx = x;
  5778     *rety = y;
  5779     return aWindow;
  5782 static inline bool
  5783 is_context_menu_key(const WidgetKeyboardEvent& aKeyEvent)
  5785     return ((aKeyEvent.keyCode == NS_VK_F10 && aKeyEvent.IsShift() &&
  5786              !aKeyEvent.IsControl() && !aKeyEvent.IsMeta() &&
  5787              !aKeyEvent.IsAlt()) ||
  5788             (aKeyEvent.keyCode == NS_VK_CONTEXT_MENU && !aKeyEvent.IsShift() &&
  5789              !aKeyEvent.IsControl() && !aKeyEvent.IsMeta() &&
  5790              !aKeyEvent.IsAlt()));
  5793 static int
  5794 is_parent_ungrab_enter(GdkEventCrossing *aEvent)
  5796     return (GDK_CROSSING_UNGRAB == aEvent->mode) &&
  5797         ((GDK_NOTIFY_ANCESTOR == aEvent->detail) ||
  5798          (GDK_NOTIFY_VIRTUAL == aEvent->detail));
  5802 static int
  5803 is_parent_grab_leave(GdkEventCrossing *aEvent)
  5805     return (GDK_CROSSING_GRAB == aEvent->mode) &&
  5806         ((GDK_NOTIFY_ANCESTOR == aEvent->detail) ||
  5807             (GDK_NOTIFY_VIRTUAL == aEvent->detail));
  5810 #ifdef ACCESSIBILITY
  5811 void
  5812 nsWindow::CreateRootAccessible()
  5814     if (mIsTopLevel && !mRootAccessible) {
  5815         LOG(("nsWindow:: Create Toplevel Accessibility\n"));
  5816         mRootAccessible = GetRootAccessible();
  5820 void
  5821 nsWindow::DispatchEventToRootAccessible(uint32_t aEventType)
  5823     if (!a11y::ShouldA11yBeEnabled()) {
  5824         return;
  5827     nsCOMPtr<nsIAccessibilityService> accService =
  5828         do_GetService("@mozilla.org/accessibilityService;1");
  5829     if (!accService) {
  5830         return;
  5833     // Get the root document accessible and fire event to it.
  5834     a11y::Accessible* acc = GetRootAccessible();
  5835     if (acc) {
  5836         accService->FireAccessibleEvent(aEventType, acc);
  5840 void
  5841 nsWindow::DispatchActivateEventAccessible(void)
  5843     DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_ACTIVATE);
  5846 void
  5847 nsWindow::DispatchDeactivateEventAccessible(void)
  5849     DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_DEACTIVATE);
  5852 void
  5853 nsWindow::DispatchMaximizeEventAccessible(void)
  5855     DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_MAXIMIZE);
  5858 void
  5859 nsWindow::DispatchMinimizeEventAccessible(void)
  5861     DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_MINIMIZE);
  5864 void
  5865 nsWindow::DispatchRestoreEventAccessible(void)
  5867     DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_RESTORE);
  5870 #endif /* #ifdef ACCESSIBILITY */
  5872 // nsChildWindow class
  5874 nsChildWindow::nsChildWindow()
  5878 nsChildWindow::~nsChildWindow()
  5882 NS_IMETHODIMP
  5883 nsWindow::NotifyIME(const IMENotification& aIMENotification)
  5885     if (MOZ_UNLIKELY(!mIMModule)) {
  5886         switch (aIMENotification.mMessage) {
  5887             case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
  5888             case REQUEST_TO_COMMIT_COMPOSITION:
  5889             case REQUEST_TO_CANCEL_COMPOSITION:
  5890             case NOTIFY_IME_OF_FOCUS:
  5891             case NOTIFY_IME_OF_BLUR:
  5892               return NS_ERROR_NOT_AVAILABLE;
  5893             default:
  5894               break;
  5897     switch (aIMENotification.mMessage) {
  5898         // TODO: We should replace NOTIFY_IME_OF_CURSOR_POS_CHANGED with
  5899         //       NOTIFY_IME_OF_SELECTION_CHANGE.  The required behavior is
  5900         //       really different from committing composition.
  5901         case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
  5902         case REQUEST_TO_COMMIT_COMPOSITION:
  5903             return mIMModule->CommitIMEComposition(this);
  5904         case REQUEST_TO_CANCEL_COMPOSITION:
  5905             return mIMModule->CancelIMEComposition(this);
  5906         case NOTIFY_IME_OF_FOCUS:
  5907             mIMModule->OnFocusChangeInGecko(true);
  5908             return NS_OK;
  5909         case NOTIFY_IME_OF_BLUR:
  5910             mIMModule->OnFocusChangeInGecko(false);
  5911             return NS_OK;
  5912         case NOTIFY_IME_OF_COMPOSITION_UPDATE:
  5913             mIMModule->OnUpdateComposition();
  5914             return NS_OK;
  5915         default:
  5916             return NS_ERROR_NOT_IMPLEMENTED;
  5920 NS_IMETHODIMP_(void)
  5921 nsWindow::SetInputContext(const InputContext& aContext,
  5922                           const InputContextAction& aAction)
  5924     if (!mIMModule) {
  5925         return;
  5927     mIMModule->SetInputContext(this, &aContext, &aAction);
  5930 NS_IMETHODIMP_(InputContext)
  5931 nsWindow::GetInputContext()
  5933   InputContext context;
  5934   if (!mIMModule) {
  5935       context.mIMEState.mEnabled = IMEState::DISABLED;
  5936       context.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED;
  5937       // If IME context isn't available on this widget, we should set |this|
  5938       // instead of nullptr since nullptr means that the platform has only one
  5939       // context per process.
  5940       context.mNativeIMEContext = this;
  5941   } else {
  5942       context = mIMModule->GetInputContext();
  5943       context.mNativeIMEContext = mIMModule;
  5945   return context;
  5948 NS_IMETHODIMP_(bool)
  5949 nsWindow::ExecuteNativeKeyBinding(NativeKeyBindingsType aType,
  5950                                   const WidgetKeyboardEvent& aEvent,
  5951                                   DoCommandCallback aCallback,
  5952                                   void* aCallbackData)
  5954     NativeKeyBindings* keyBindings = NativeKeyBindings::GetInstance(aType);
  5955     return keyBindings->Execute(aEvent, aCallback, aCallbackData);
  5958 NS_IMETHODIMP
  5959 nsWindow::GetToggledKeyState(uint32_t aKeyCode, bool* aLEDState)
  5961     NS_ENSURE_ARG_POINTER(aLEDState);
  5963     KeymapWrapper::Modifiers modifier;
  5964     switch (aKeyCode) {
  5965         case NS_VK_CAPS_LOCK:   modifier = KeymapWrapper::CAPS_LOCK;   break;
  5966         case NS_VK_NUM_LOCK:    modifier = KeymapWrapper::NUM_LOCK;    break;
  5967         case NS_VK_SCROLL_LOCK: modifier = KeymapWrapper::SCROLL_LOCK; break;
  5968         default: return NS_ERROR_INVALID_ARG;
  5971     *aLEDState =
  5972         KeymapWrapper::AreModifiersCurrentlyActive(modifier);
  5973     return NS_OK;
  5976 #if defined(MOZ_X11) && (MOZ_WIDGET_GTK == 2)
  5977 /* static */ already_AddRefed<gfxASurface>
  5978 nsWindow::GetSurfaceForGdkDrawable(GdkDrawable* aDrawable,
  5979                                    const nsIntSize& aSize)
  5981     GdkVisual* visual = gdk_drawable_get_visual(aDrawable);
  5982     Screen* xScreen =
  5983         gdk_x11_screen_get_xscreen(gdk_drawable_get_screen(aDrawable));
  5984     Display* xDisplay = DisplayOfScreen(xScreen);
  5985     Drawable xDrawable = gdk_x11_drawable_get_xid(aDrawable);
  5987     nsRefPtr<gfxASurface> result;
  5989     if (visual) {
  5990         Visual* xVisual = gdk_x11_visual_get_xvisual(visual);
  5992         result = new gfxXlibSurface(xDisplay, xDrawable, xVisual,
  5993                                     gfxIntSize(aSize.width, aSize.height));
  5994     } else {
  5995         // no visual? we must be using an xrender format.  Find a format
  5996         // for this depth.
  5997         XRenderPictFormat *pf = nullptr;
  5998         switch (gdk_drawable_get_depth(aDrawable)) {
  5999             case 32:
  6000                 pf = XRenderFindStandardFormat(xDisplay, PictStandardARGB32);
  6001                 break;
  6002             case 24:
  6003                 pf = XRenderFindStandardFormat(xDisplay, PictStandardRGB24);
  6004                 break;
  6005             default:
  6006                 NS_ERROR("Don't know how to handle the given depth!");
  6007                 break;
  6010         result = new gfxXlibSurface(xScreen, xDrawable, pf,
  6011                                     gfxIntSize(aSize.width, aSize.height));
  6014     return result.forget();
  6016 #endif
  6018 TemporaryRef<DrawTarget>
  6019 nsWindow::StartRemoteDrawing()
  6021 #if (MOZ_WIDGET_GTK == 2)
  6022   gfxASurface *surf = GetThebesSurface();
  6023 #else
  6024   // TODO GTK3
  6025   gfxASurface *surf = nullptr;
  6026 #endif
  6027   if (!surf) {
  6028     return nullptr;
  6031   IntSize size(surf->GetSize().width, surf->GetSize().height);
  6032   if (size.width <= 0 || size.height <= 0) {
  6033     return nullptr;
  6036   return gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, size);
  6039 // return the gfxASurface for rendering to this widget
  6040 gfxASurface*
  6041 #if (MOZ_WIDGET_GTK == 2)
  6042 nsWindow::GetThebesSurface()
  6043 #else
  6044 nsWindow::GetThebesSurface(cairo_t *cr)
  6045 #endif
  6047     if (!mGdkWindow)
  6048         return nullptr;
  6050 #if (MOZ_WIDGET_GTK != 2)
  6051     cairo_surface_t *surf = cairo_get_target(cr);
  6052     if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) {
  6053       NS_NOTREACHED("Missing cairo target?");
  6054       return nullptr;
  6056 #endif // MOZ_WIDGET_GTK2
  6058 #ifdef MOZ_X11
  6059     gint width, height;
  6061 #if (MOZ_WIDGET_GTK == 2)
  6062     gdk_drawable_get_size(GDK_DRAWABLE(mGdkWindow), &width, &height);
  6063 #else
  6064     width = gdk_window_get_width(mGdkWindow);
  6065     height = gdk_window_get_height(mGdkWindow);
  6066 #endif
  6068     // Owen Taylor says this is the right thing to do!
  6069     width = std::min(32767, width);
  6070     height = std::min(32767, height);
  6071     gfxIntSize size(width, height);
  6073     GdkVisual *gdkVisual = gdk_window_get_visual(mGdkWindow);
  6074     Visual* visual = gdk_x11_visual_get_xvisual(gdkVisual);
  6076 #  ifdef MOZ_HAVE_SHMIMAGE
  6077     bool usingShm = false;
  6078     if (nsShmImage::UseShm()) {
  6079         // EnsureShmImage() is a dangerous interface, but we guarantee
  6080         // that the thebes surface and the shmimage have the same
  6081         // lifetime
  6082         mThebesSurface =
  6083             nsShmImage::EnsureShmImage(size,
  6084                                        visual, gdk_visual_get_depth(gdkVisual),
  6085                                        mShmImage);
  6086         usingShm = mThebesSurface != nullptr;
  6088     if (!usingShm)
  6089 #  endif  // MOZ_HAVE_SHMIMAGE
  6091 #if (MOZ_WIDGET_GTK == 2)
  6092     mThebesSurface = new gfxXlibSurface
  6093         (GDK_WINDOW_XDISPLAY(mGdkWindow),
  6094          gdk_x11_window_get_xid(mGdkWindow),
  6095          visual,
  6096          size);
  6097 #else
  6098 #if MOZ_TREE_CAIRO
  6099 #error "cairo-gtk3 target must be built with --enable-system-cairo"
  6100 #else
  6101     mThebesSurface = gfxASurface::Wrap(surf);
  6102 #endif
  6103 #endif
  6105 #endif
  6107     // if the surface creation is reporting an error, then
  6108     // we don't have a surface to give back
  6109     if (mThebesSurface && mThebesSurface->CairoStatus() != 0) {
  6110         mThebesSurface = nullptr;
  6113     return mThebesSurface;
  6116 // Code shared begin BeginMoveDrag and BeginResizeDrag
  6117 bool
  6118 nsWindow::GetDragInfo(WidgetMouseEvent* aMouseEvent,
  6119                       GdkWindow** aWindow, gint* aButton,
  6120                       gint* aRootX, gint* aRootY)
  6122     if (aMouseEvent->button != WidgetMouseEvent::eLeftButton) {
  6123         // we can only begin a move drag with the left mouse button
  6124         return false;
  6126     *aButton = 1;
  6128     // get the gdk window for this widget
  6129     GdkWindow* gdk_window = mGdkWindow;
  6130     if (!gdk_window) {
  6131         return false;
  6133     NS_ABORT_IF_FALSE(GDK_IS_WINDOW(gdk_window), "must really be window");
  6135     // find the top-level window
  6136     gdk_window = gdk_window_get_toplevel(gdk_window);
  6137     NS_ABORT_IF_FALSE(gdk_window,
  6138                       "gdk_window_get_toplevel should not return null");
  6139     *aWindow = gdk_window;
  6141     if (!aMouseEvent->widget) {
  6142         return false;
  6145     // FIXME: It would be nice to have the widget position at the time
  6146     // of the event, but it's relatively unlikely that the widget has
  6147     // moved since the mousedown.  (On the other hand, it's quite likely
  6148     // that the mouse has moved, which is why we use the mouse position
  6149     // from the event.)
  6150     nsIntPoint offset = aMouseEvent->widget->WidgetToScreenOffset();
  6151     *aRootX = aMouseEvent->refPoint.x + offset.x;
  6152     *aRootY = aMouseEvent->refPoint.y + offset.y;
  6154     return true;
  6157 NS_IMETHODIMP
  6158 nsWindow::BeginMoveDrag(WidgetMouseEvent* aEvent)
  6160     NS_ABORT_IF_FALSE(aEvent, "must have event");
  6161     NS_ABORT_IF_FALSE(aEvent->eventStructType == NS_MOUSE_EVENT,
  6162                       "event must have correct struct type");
  6164     GdkWindow *gdk_window;
  6165     gint button, screenX, screenY;
  6166     if (!GetDragInfo(aEvent, &gdk_window, &button, &screenX, &screenY)) {
  6167         return NS_ERROR_FAILURE;
  6170     // tell the window manager to start the move
  6171     gdk_window_begin_move_drag(gdk_window, button, screenX, screenY,
  6172                                aEvent->time);
  6174     return NS_OK;
  6177 NS_IMETHODIMP
  6178 nsWindow::BeginResizeDrag(WidgetGUIEvent* aEvent,
  6179                           int32_t aHorizontal,
  6180                           int32_t aVertical)
  6182     NS_ENSURE_ARG_POINTER(aEvent);
  6184     if (aEvent->eventStructType != NS_MOUSE_EVENT) {
  6185         // you can only begin a resize drag with a mouse event
  6186         return NS_ERROR_INVALID_ARG;
  6189     GdkWindow *gdk_window;
  6190     gint button, screenX, screenY;
  6191     if (!GetDragInfo(aEvent->AsMouseEvent(), &gdk_window, &button,
  6192                      &screenX, &screenY)) {
  6193         return NS_ERROR_FAILURE;
  6196     // work out what GdkWindowEdge we're talking about
  6197     GdkWindowEdge window_edge;
  6198     if (aVertical < 0) {
  6199         if (aHorizontal < 0) {
  6200             window_edge = GDK_WINDOW_EDGE_NORTH_WEST;
  6201         } else if (aHorizontal == 0) {
  6202             window_edge = GDK_WINDOW_EDGE_NORTH;
  6203         } else {
  6204             window_edge = GDK_WINDOW_EDGE_NORTH_EAST;
  6206     } else if (aVertical == 0) {
  6207         if (aHorizontal < 0) {
  6208             window_edge = GDK_WINDOW_EDGE_WEST;
  6209         } else if (aHorizontal == 0) {
  6210             return NS_ERROR_INVALID_ARG;
  6211         } else {
  6212             window_edge = GDK_WINDOW_EDGE_EAST;
  6214     } else {
  6215         if (aHorizontal < 0) {
  6216             window_edge = GDK_WINDOW_EDGE_SOUTH_WEST;
  6217         } else if (aHorizontal == 0) {
  6218             window_edge = GDK_WINDOW_EDGE_SOUTH;
  6219         } else {
  6220             window_edge = GDK_WINDOW_EDGE_SOUTH_EAST;
  6224     // tell the window manager to start the resize
  6225     gdk_window_begin_resize_drag(gdk_window, window_edge, button,
  6226                                  screenX, screenY, aEvent->time);
  6228     return NS_OK;
  6231 nsIWidget::LayerManager*
  6232 nsWindow::GetLayerManager(PLayerTransactionChild* aShadowManager,
  6233                           LayersBackend aBackendHint,
  6234                           LayerManagerPersistence aPersistence,
  6235                           bool* aAllowRetaining)
  6237     if (!mLayerManager && eTransparencyTransparent == GetTransparencyMode()) {
  6238         mLayerManager = CreateBasicLayerManager();
  6241     return nsBaseWidget::GetLayerManager(aShadowManager, aBackendHint,
  6242                                          aPersistence, aAllowRetaining);
  6245 void
  6246 nsWindow::ClearCachedResources()
  6248     if (mLayerManager &&
  6249         mLayerManager->GetBackendType() == mozilla::layers::LayersBackend::LAYERS_BASIC) {
  6250         mLayerManager->ClearCachedResources();
  6253     GList* children = gdk_window_peek_children(mGdkWindow);
  6254     for (GList* list = children; list; list = list->next) {
  6255         nsWindow* window = get_window_for_gdk_window(GDK_WINDOW(list->data));
  6256         if (window) {
  6257             window->ClearCachedResources();
  6262 nsresult
  6263 nsWindow::SynthesizeNativeMouseEvent(nsIntPoint aPoint,
  6264                                      uint32_t aNativeMessage,
  6265                                      uint32_t aModifierFlags)
  6267   if (!mGdkWindow) {
  6268     return NS_OK;
  6271   GdkDisplay* display = gdk_window_get_display(mGdkWindow);
  6273   // When a button-release event is requested, create it here and put it in the
  6274   // event queue. This will not emit a motion event - this needs to be done
  6275   // explicitly *before* requesting a button-release. You will also need to wait
  6276   // for the motion event to be dispatched before requesting a button-release
  6277   // event to maintain the desired event order.
  6278   if (aNativeMessage == GDK_BUTTON_RELEASE) {
  6279     GdkEvent event;
  6280     memset(&event, 0, sizeof(GdkEvent));
  6281     event.type = (GdkEventType)aNativeMessage;
  6282     event.button.button = 1;
  6283     event.button.window = mGdkWindow;
  6284     event.button.time = GDK_CURRENT_TIME;
  6286 #if (MOZ_WIDGET_GTK == 3)
  6287     // Get device for event source
  6288     GdkDeviceManager *device_manager = gdk_display_get_device_manager(display);
  6289     event.button.device = gdk_device_manager_get_client_pointer(device_manager);
  6290 #endif
  6292     gdk_event_put(&event);
  6293   } else {
  6294     // We don't support specific events other than button-release. In case
  6295     // aNativeMessage != GDK_BUTTON_RELEASE we'll synthesize a motion event
  6296     // that will be emitted by gdk_display_warp_pointer().
  6297     GdkScreen* screen = gdk_window_get_screen(mGdkWindow);
  6298     gdk_display_warp_pointer(display, screen, aPoint.x, aPoint.y);
  6301   return NS_OK;

mercurial