Bug 566480 - Make dragging the menubar drag the window on GTK themes where that should work (and also fix GTK resize drags to initialize drag better). r=karlt,dbaron,enn,dao
authorMichael Ventnor <ventnor.bugzilla@gmail.com>, L. David Baron <dbaron@dbaron.org>
Sat, 17 Jul 2010 10:11:54 +0200
changeset 47857 b00bb3a115de35aafd0b2a7b379eaa7127ff65fa
parent 47856 ca35f59a9ff12639f43d5ee5c9516006dc21109f
child 47858 ee0f5ab96cf2fe20ba8f558fdf0869b194f83fda
push id14449
push userdgottwald@mozilla.com
push dateSat, 17 Jul 2010 08:12:46 +0000
treeherdermozilla-central@b00bb3a115de [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskarlt, dbaron, enn, dao
bugs566480
milestone2.0b2pre
first release with
nightly linux32
b00bb3a115de / 4.0b2pre / 20100717030004 / files
nightly linux64
b00bb3a115de / 4.0b2pre / 20100717030824 / files
nightly mac
b00bb3a115de / 4.0b2pre / 20100717030725 / files
nightly win32
b00bb3a115de / 4.0b2pre / 20100717054110 / files
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
Bug 566480 - Make dragging the menubar drag the window on GTK themes where that should work (and also fix GTK resize drags to initialize drag better). r=karlt,dbaron,enn,dao
content/base/src/nsGkAtomList.h
dom/base/nsGlobalWindow.cpp
dom/interfaces/base/nsIDOMChromeWindow.idl
layout/style/nsCSSRuleProcessor.cpp
layout/style/nsMediaFeatures.cpp
toolkit/content/WindowDraggingUtils.jsm
toolkit/themes/gnomestripe/global/global.css
widget/public/nsILookAndFeel.h
widget/public/nsIWidget.h
widget/src/gtk2/nsLookAndFeel.cpp
widget/src/gtk2/nsLookAndFeel.h
widget/src/gtk2/nsWindow.cpp
widget/src/gtk2/nsWindow.h
widget/src/xpwidgets/nsBaseWidget.cpp
widget/src/xpwidgets/nsBaseWidget.h
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -1701,23 +1701,25 @@ GK_ATOM(scrollbar_thumb_proportional, "s
 GK_ATOM(images_in_menus, "images-in-menus")
 GK_ATOM(images_in_buttons, "images-in-buttons")
 GK_ATOM(windows_default_theme, "windows-default-theme")
 GK_ATOM(mac_graphite_theme, "mac-graphite-theme")
 GK_ATOM(windows_compositor, "windows-compositor")
 GK_ATOM(windows_classic, "windows-classic")
 GK_ATOM(touch_enabled, "touch-enabled")
 GK_ATOM(maemo_classic, "maemo-classic")
+GK_ATOM(menubar_drag, "menubar-drag")
 
 // And the same again, as media query keywords.
 GK_ATOM(_moz_scrollbar_start_backward, "-moz-scrollbar-start-backward")
 GK_ATOM(_moz_scrollbar_start_forward, "-moz-scrollbar-start-forward")
 GK_ATOM(_moz_scrollbar_end_backward, "-moz-scrollbar-end-backward")
 GK_ATOM(_moz_scrollbar_end_forward, "-moz-scrollbar-end-forward")
 GK_ATOM(_moz_scrollbar_thumb_proportional, "-moz-scrollbar-thumb-proportional")
 GK_ATOM(_moz_images_in_menus, "-moz-images-in-menus")
 GK_ATOM(_moz_images_in_buttons, "-moz-images-in-buttons")
 GK_ATOM(_moz_windows_default_theme, "-moz-windows-default-theme")
 GK_ATOM(_moz_mac_graphite_theme, "-moz-mac-graphite-theme")
 GK_ATOM(_moz_windows_compositor, "-moz-windows-compositor")
 GK_ATOM(_moz_windows_classic, "-moz-windows-classic")
 GK_ATOM(_moz_touch_enabled, "-moz-touch-enabled")
 GK_ATOM(_moz_maemo_classic, "-moz-maemo-classic")
+GK_ATOM(_moz_menubar_drag, "-moz-menubar-drag")
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -9428,16 +9428,35 @@ nsGlobalChromeWindow::GetAttentionWithCy
 
   if (widget) {
     rv = widget->GetAttention(aCycleCount);
   }
 
   return rv;
 }
 
+NS_IMETHODIMP
+nsGlobalChromeWindow::BeginWindowMove(nsIDOMEvent *aMouseDownEvent)
+{
+  nsCOMPtr<nsIWidget> widget = GetMainWidget();
+  if (!widget) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIPrivateDOMEvent> privEvent = do_QueryInterface(aMouseDownEvent);
+  NS_ENSURE_TRUE(privEvent, NS_ERROR_FAILURE);
+  nsEvent *internalEvent = privEvent->GetInternalNSEvent();
+  NS_ENSURE_TRUE(internalEvent &&
+                 internalEvent->eventStructType == NS_MOUSE_EVENT,
+                 NS_ERROR_FAILURE);
+  nsMouseEvent *mouseEvent = static_cast<nsMouseEvent*>(internalEvent);
+
+  return widget->BeginMoveDrag(mouseEvent);
+}
+
 //Note: This call will lock the cursor, it will not change as it moves.
 //To unlock, the cursor must be set back to CURSOR_AUTO.
 NS_IMETHODIMP
 nsGlobalChromeWindow::SetCursor(const nsAString& aCursor)
 {
   FORWARD_TO_OUTER_CHROME(SetCursor, (aCursor), NS_ERROR_NOT_INITIALIZED);
 
   nsresult rv = NS_OK;
--- a/dom/interfaces/base/nsIDOMChromeWindow.idl
+++ b/dom/interfaces/base/nsIDOMChromeWindow.idl
@@ -35,19 +35,20 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "domstubs.idl"
 
 interface nsIBrowserDOMWindow;
 interface nsIDOMElement;
+interface nsIDOMEvent;
 interface nsIChromeFrameMessageManager;
 
-[scriptable, uuid(adf6b19f-459f-4892-93eb-71c527bae2af)]
+[scriptable, uuid(ec38cbaf-372f-4874-bc7a-dbf1f0b3d755)]
 interface nsIDOMChromeWindow : nsISupports
 {
   const unsigned short STATE_MAXIMIZED = 1;
   const unsigned short STATE_MINIMIZED = 2;
   const unsigned short STATE_NORMAL = 3;
   const unsigned short STATE_FULLSCREEN = 4;
 
   readonly attribute unsigned short              windowState;
@@ -71,9 +72,20 @@ interface nsIDOMChromeWindow : nsISuppor
 
   /**
    * Notify a default button is loaded on a dialog or a wizard.
    * defaultButton is the default button.
    */
   void notifyDefaultButtonLoaded(in nsIDOMElement defaultButton);
 
   readonly attribute nsIChromeFrameMessageManager messageManager;
+
+  /**
+   * On some operating systems, we must allow the window manager to
+   * handle window dragging. This function tells the window manager to
+   * start dragging the window. This function will fail unless called
+   * while the left mouse button is held down, callers must check this.
+   *
+   * Returns NS_ERROR_NOT_IMPLEMENTED (and thus throws in JS) if the OS
+   * doesn't support this.
+   */
+  void beginWindowMove(in nsIDOMEvent mouseDownEvent);
 };
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -995,16 +995,21 @@ InitSystemMetrics()
     sSystemMetrics->AppendElement(nsGkAtoms::images_in_menus);
   }
 
   lookAndFeel->GetMetric(nsILookAndFeel::eMetric_ImagesInButtons, metricResult);
   if (metricResult) {
     sSystemMetrics->AppendElement(nsGkAtoms::images_in_buttons);
   }
 
+  lookAndFeel->GetMetric(nsILookAndFeel::eMetric_MenuBarDrag, metricResult);
+  if (metricResult) {
+    sSystemMetrics->AppendElement(nsGkAtoms::menubar_drag);
+  }
+
   rv = lookAndFeel->GetMetric(nsILookAndFeel::eMetric_WindowsDefaultTheme, metricResult);
   if (NS_SUCCEEDED(rv) && metricResult) {
     sSystemMetrics->AppendElement(nsGkAtoms::windows_default_theme);
   }
 
   rv = lookAndFeel->GetMetric(nsILookAndFeel::eMetric_MacGraphiteTheme, metricResult);
   if (NS_SUCCEEDED(rv) && metricResult) {
     sSystemMetrics->AppendElement(nsGkAtoms::mac_graphite_theme);
--- a/layout/style/nsMediaFeatures.cpp
+++ b/layout/style/nsMediaFeatures.cpp
@@ -463,16 +463,23 @@ nsMediaFeatures::features[] = {
     },
     {
         &nsGkAtoms::_moz_maemo_classic,
         nsMediaFeature::eMinMaxNotAllowed,
         nsMediaFeature::eBoolInteger,
         { &nsGkAtoms::maemo_classic },
         GetSystemMetric
     },
+    {
+        &nsGkAtoms::_moz_menubar_drag,
+        nsMediaFeature::eMinMaxNotAllowed,
+        nsMediaFeature::eBoolInteger,
+        { &nsGkAtoms::menubar_drag },
+        GetSystemMetric
+    },
 
     // Null-mName terminator:
     {
         nsnull,
         nsMediaFeature::eMinMaxAllowed,
         nsMediaFeature::eInteger,
         { nsnull },
         nsnull
--- a/toolkit/content/WindowDraggingUtils.jsm
+++ b/toolkit/content/WindowDraggingUtils.jsm
@@ -83,27 +83,35 @@ WindowDraggingElement.prototype = {
     if (this.shouldDrag(aEvent))
       aEvent.preventDefault();
 #else
     switch (aEvent.type) {
       case "mousedown":
         if (!this.shouldDrag(aEvent))
           return;
 
+#ifdef MOZ_WIDGET_GTK2
+        // On GTK, there is a toolkit-level function which handles
+        // window dragging, which must be used.
+        this._window.beginWindowMove(aEvent);
+#else
         this._deltaX = aEvent.screenX - this._window.screenX;
         this._deltaY = aEvent.screenY - this._window.screenY;
         this._draggingWindow = true;
         this._window.addEventListener("mousemove", this, false);
         this._window.addEventListener("mouseup", this, false);
+#endif
         break;
       case "mousemove":
         if (this._draggingWindow)
           this._window.moveTo(aEvent.screenX - this._deltaX, aEvent.screenY - this._deltaY);
         break;
       case "mouseup":
-        this._draggingWindow = false;
-        this._window.removeEventListener("mousemove", this, false);
-        this._window.removeEventListener("mouseup", this, false);
+        if (this._draggingWindow) {
+          this._draggingWindow = false;
+          this._window.removeEventListener("mousemove", this, false);
+          this._window.removeEventListener("mouseup", this, false);
+        }
         break;
     }
 #endif
   }
 }
--- a/toolkit/themes/gnomestripe/global/global.css
+++ b/toolkit/themes/gnomestripe/global/global.css
@@ -62,16 +62,20 @@ menulist > menupopup,
 .menulist-compact {
   -moz-binding: url("chrome://global/skin/globalBindings.xml#menulist-compact");
 }
 
 progressmeter[mode="undetermined"] {
   -moz-binding: url("chrome://global/content/bindings/progressmeter.xml#progressmeter-undetermined");
 }
 
+toolbar[type="menubar"]:not(-moz-lwtheme):-moz-system-metric(menubar-drag) {
+  -moz-binding: url("chrome://global/content/bindings/toolbar.xml#toolbar-drag");
+}
+
 /* ::::: root elements ::::: */
 
 window,
 page,
 dialog,
 wizard,
 prefwindow {
   -moz-appearance: window;
--- a/widget/public/nsILookAndFeel.h
+++ b/widget/public/nsILookAndFeel.h
@@ -317,17 +317,21 @@ public:
 
     /**
      * If this metric != 0, show icons in menus.
      */
     eMetric_ImagesInMenus,
     /**
      * If this metric != 0, show icons in buttons.
      */
-    eMetric_ImagesInButtons
+    eMetric_ImagesInButtons,
+    /**
+     * If this metric != 0, support window dragging on the menubar.
+     */
+    eMetric_MenuBarDrag
   } nsMetricID;
 
   enum {
     eMetric_ScrollArrowStartBackward = 0x1000,
     eMetric_ScrollArrowStartForward = 0x0100,
     eMetric_ScrollArrowEndBackward = 0x0010,
     eMetric_ScrollArrowEndForward = 0x0001,
     eMetric_ScrollArrowStyleSingle =                      // single arrow at each end
--- a/widget/public/nsIWidget.h
+++ b/widget/public/nsIWidget.h
@@ -105,19 +105,20 @@ typedef nsEventStatus (* EVENT_CALLBACK)
 #define NS_NATIVE_PLUGIN_PORT_CG    101
 #endif
 #ifdef XP_WIN
 #define NS_NATIVE_TSF_THREAD_MGR       100
 #define NS_NATIVE_TSF_CATEGORY_MGR     101
 #define NS_NATIVE_TSF_DISPLAY_ATTR_MGR 102
 #endif
 
+// b7ec5f61-57df-4355-81f3-41ced52e8026
 #define NS_IWIDGET_IID \
-{ 0x271ac413, 0xa202, 0x46dc, \
-{ 0xbc, 0xd5, 0x67, 0xa1, 0xfb, 0x58, 0x89, 0x7f } }
+{ 0xb7ec5f61, 0x57df, 0x4355, \
+  { 0x81, 0xf3, 0x41, 0xce, 0xd5, 0x2e, 0x80, 0x26 } }
 
 /*
  * Window shadow styles
  * Also used for the -moz-window-shadow CSS property
  */
 
 #define NS_STYLE_WINDOW_SHADOW_NONE             0
 #define NS_STYLE_WINDOW_SHADOW_DEFAULT          1
@@ -960,16 +961,21 @@ class nsIWidget : public nsISupports {
      */
     virtual nsIContent* GetLastRollup() = 0;
 
     /**
      * Begin a window resizing drag, based on the event passed in.
      */
     NS_IMETHOD BeginResizeDrag(nsGUIEvent* aEvent, PRInt32 aHorizontal, PRInt32 aVertical) = 0;
 
+    /**
+     * Begin a window moving drag, based on the event passed in.
+     */
+    NS_IMETHOD BeginMoveDrag(nsMouseEvent* aEvent) = 0;
+
     enum Modifiers {
         CAPS_LOCK = 0x01, // when CapsLock is active
         NUM_LOCK = 0x02, // when NumLock is active
         SHIFT_L = 0x0100,
         SHIFT_R = 0x0200,
         CTRL_L = 0x0400,
         CTRL_R = 0x0800,
         ALT_L = 0x1000, // includes Option
--- a/widget/src/gtk2/nsLookAndFeel.cpp
+++ b/widget/src/gtk2/nsLookAndFeel.cpp
@@ -65,16 +65,17 @@ nscolor   nsLookAndFeel::sButtonText = 0
 nscolor   nsLookAndFeel::sButtonOuterLightBorder = 0;
 nscolor   nsLookAndFeel::sButtonInnerDarkBorder = 0;
 nscolor   nsLookAndFeel::sOddCellBackground = 0;
 nscolor   nsLookAndFeel::sNativeHyperLinkText = 0;
 nscolor   nsLookAndFeel::sComboBoxText = 0;
 nscolor   nsLookAndFeel::sComboBoxBackground = 0;
 PRUnichar nsLookAndFeel::sInvisibleCharacter = PRUnichar('*');
 float     nsLookAndFeel::sCaretRatio = 0;
+PRBool    nsLookAndFeel::sMenuSupportsDrag = PR_FALSE;
 
 //-------------------------------------------------------------------------
 //
 // Query interface implementation
 //
 //-------------------------------------------------------------------------
 nsLookAndFeel::nsLookAndFeel() : nsXPLookAndFeel()
 {
@@ -626,16 +627,19 @@ NS_IMETHODIMP nsLookAndFeel::GetMetric(c
         aMetric = NS_UNDERLINE_STYLE_WAVY;
         break;
     case eMetric_ImagesInMenus:
         aMetric = moz_gtk_images_in_menus();
         break;
     case eMetric_ImagesInButtons:
         aMetric = moz_gtk_images_in_buttons();
         break;
+    case eMetric_MenuBarDrag:
+        aMetric = sMenuSupportsDrag;
+        break;
     default:
         aMetric = 0;
         res     = NS_ERROR_FAILURE;
     }
 
     return res;
 }
 
@@ -792,16 +796,23 @@ nsLookAndFeel::InitLookAndFeel()
     }
 
     style = gtk_widget_get_style(menuBar);
     if (style) {
         sMenuBarText = GDK_COLOR_TO_NS_RGB(style->fg[GTK_STATE_NORMAL]);
         sMenuBarHoverText = GDK_COLOR_TO_NS_RGB(style->fg[GTK_STATE_SELECTED]);
     }
 
+    // Some themes have a unified menu bar, and support window dragging on it
+    gboolean supports_menubar_drag = FALSE;
+    gtk_widget_style_get(menuBar,
+                         "window-dragging", &supports_menubar_drag,
+                         NULL);
+    sMenuSupportsDrag = supports_menubar_drag;
+
     // GTK's guide to fancy odd row background colors:
     // 1) Check if a theme explicitly defines an odd row color
     // 2) If not, check if it defines an even row color, and darken it
     //    slightly by a hardcoded value (gtkstyle.c)
     // 3) If neither are defined, take the base background color and
     //    darken that by a hardcoded value
     GdkColor colorValue;
     GdkColor *colorValuePtr = NULL;
--- a/widget/src/gtk2/nsLookAndFeel.h
+++ b/widget/src/gtk2/nsLookAndFeel.h
@@ -73,16 +73,17 @@ protected:
     static nscolor sButtonOuterLightBorder;
     static nscolor sButtonInnerDarkBorder;
     static nscolor sOddCellBackground;
     static nscolor sNativeHyperLinkText;
     static nscolor sComboBoxText;
     static nscolor sComboBoxBackground;
     static PRUnichar sInvisibleCharacter;
     static float   sCaretRatio;
+    static PRBool  sMenuSupportsDrag;
 
     static void InitLookAndFeel();
     void InitWidget() {
         NS_ASSERTION(!mStyle, "already initialized");
         // GtkInvisibles come with a refcount that is not floating
         // (since their initialization code calls g_object_ref_sink) and
         // their destroy code releases that reference (which means they
         // have to be explicitly destroyed, since calling unref enough
--- a/widget/src/gtk2/nsWindow.cpp
+++ b/widget/src/gtk2/nsWindow.cpp
@@ -6732,31 +6732,93 @@ nsWindow::GetThebesSurface()
         mThebesSurface = nsnull;
     } else {
         mThebesSurface->SetDeviceOffset(gfxPoint(-x_offset, -y_offset));
     }
 
     return mThebesSurface;
 }
 
+// Code shared begin BeginMoveDrag and BeginResizeDrag
+PRBool
+nsWindow::GetDragInfo(nsMouseEvent* aMouseEvent,
+                      GdkWindow** aWindow, gint* aButton,
+                      gint* aRootX, gint* aRootY)
+{
+    if (aMouseEvent->button != nsMouseEvent::eLeftButton) {
+        // we can only begin a move drag with the left mouse button
+        return PR_FALSE;
+    }
+    *aButton = 1;
+
+    // get the gdk window for this widget
+    GdkWindow* gdk_window = mGdkWindow;
+    if (!gdk_window) {
+        return PR_FALSE;
+    }
+    NS_ABORT_IF_FALSE(GDK_IS_WINDOW(gdk_window), "must really be window");
+
+    // find the top-level window
+    gdk_window = gdk_window_get_toplevel(gdk_window);
+    NS_ABORT_IF_FALSE(gdk_window,
+                      "gdk_window_get_toplevel should not return null");
+    *aWindow = gdk_window;
+
+    if (!aMouseEvent->widget) {
+        return PR_FALSE;
+    }
+
+    // FIXME: It would be nice to have the widget position at the time
+    // of the event, but it's relatively unlikely that the widget has
+    // moved since the mousedown.  (On the other hand, it's quite likely
+    // that the mouse has moved, which is why we use the mouse position
+    // from the event.)
+    nsIntPoint offset = aMouseEvent->widget->WidgetToScreenOffset();
+    *aRootX = aMouseEvent->refPoint.x + offset.x;
+    *aRootY = aMouseEvent->refPoint.y + offset.y;
+
+    return PR_TRUE;
+}
+
+NS_IMETHODIMP
+nsWindow::BeginMoveDrag(nsMouseEvent* aEvent)
+{
+    NS_ABORT_IF_FALSE(aEvent, "must have event");
+    NS_ABORT_IF_FALSE(aEvent->eventStructType == NS_MOUSE_EVENT,
+                      "event must have correct struct type");
+
+    GdkWindow *gdk_window;
+    gint button, screenX, screenY;
+    if (!GetDragInfo(aEvent, &gdk_window, &button, &screenX, &screenY)) {
+        return NS_ERROR_FAILURE;
+    }
+
+    // tell the window manager to start the move
+    gdk_window_begin_move_drag(gdk_window, button, screenX, screenY,
+                               aEvent->time);
+
+    return NS_OK;
+}
+
 NS_IMETHODIMP
 nsWindow::BeginResizeDrag(nsGUIEvent* aEvent, PRInt32 aHorizontal, PRInt32 aVertical)
 {
     NS_ENSURE_ARG_POINTER(aEvent);
 
     if (aEvent->eventStructType != NS_MOUSE_EVENT) {
-      // you can only begin a resize drag with a mouse event
-      return NS_ERROR_INVALID_ARG;
+        // you can only begin a resize drag with a mouse event
+        return NS_ERROR_INVALID_ARG;
     }
 
     nsMouseEvent* mouse_event = static_cast<nsMouseEvent*>(aEvent);
 
-    if (mouse_event->button != nsMouseEvent::eLeftButton) {
-      // you can only begin a resize drag with the left mouse button
-      return NS_ERROR_INVALID_ARG;
+    GdkWindow *gdk_window;
+    gint button, screenX, screenY;
+    if (!GetDragInfo(mouse_event, &gdk_window, &button, &screenX, &screenY)) {
+        return NS_ERROR_FAILURE;
     }
 
     // work out what GdkWindowEdge we're talking about
     GdkWindowEdge window_edge;
     if (aVertical < 0) {
         if (aHorizontal < 0) {
             window_edge = GDK_WINDOW_EDGE_NORTH_WEST;
         } else if (aHorizontal == 0) {
@@ -6777,44 +6839,14 @@ nsWindow::BeginResizeDrag(nsGUIEvent* aE
             window_edge = GDK_WINDOW_EDGE_SOUTH_WEST;
         } else if (aHorizontal == 0) {
             window_edge = GDK_WINDOW_EDGE_SOUTH;
         } else {
             window_edge = GDK_WINDOW_EDGE_SOUTH_EAST;
         }
     }
 
-    // get the gdk window for this widget
-    GdkWindow* gdk_window = mGdkWindow;
-    if (!GDK_IS_WINDOW(gdk_window)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    // find the top-level window
-    gdk_window = gdk_window_get_toplevel(gdk_window);
-    if (!GDK_IS_WINDOW(gdk_window)) {
-      return NS_ERROR_FAILURE;
-    }
-
-
-    // get the current (default) display
-    GdkDisplay* display = gdk_display_get_default();
-    if (!GDK_IS_DISPLAY(display)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    // get the current pointer position and button state
-    GdkScreen* screen = NULL;
-    gint screenX, screenY;
-    GdkModifierType mask;
-    gdk_display_get_pointer(display, &screen, &screenX, &screenY, &mask);
-
-    // we only support resizing with button 1
-    if (!(mask & GDK_BUTTON1_MASK)) {
-        return NS_ERROR_FAILURE;
-    }
-
     // tell the window manager to start the resize
-    gdk_window_begin_resize_drag(gdk_window, window_edge, 1,
-            screenX, screenY, aEvent->time);
+    gdk_window_begin_resize_drag(gdk_window, window_edge, button,
+                                 screenX, screenY, aEvent->time);
 
     return NS_OK;
 }
--- a/widget/src/gtk2/nsWindow.h
+++ b/widget/src/gtk2/nsWindow.h
@@ -297,17 +297,18 @@ public:
 
 #ifdef MOZ_X11
     Window             mOldFocusWindow;
 #endif /* MOZ_X11 */
 
     static guint32     sLastButtonPressTime;
     static guint32     sLastButtonReleaseTime;
 
-    NS_IMETHOD         BeginResizeDrag   (nsGUIEvent* aEvent, PRInt32 aHorizontal, PRInt32 aVertical);
+    NS_IMETHOD         BeginResizeDrag(nsGUIEvent* aEvent, PRInt32 aHorizontal, PRInt32 aVertical);
+    NS_IMETHOD         BeginMoveDrag(nsMouseEvent* aEvent);
 
     MozContainer*      GetMozContainer() { return mContainer; }
     GdkWindow*         GetGdkWindow() { return mGdkWindow; }
     PRBool             IsDestroyed() { return mIsDestroyed; }
 
     // If this dispatched the keydown event actually, this returns TRUE,
     // otherwise, FALSE.
     PRBool             DispatchKeyDownEvent(GdkEventKey *aEvent,
@@ -371,16 +372,19 @@ private:
     void               SetUrgencyHint(GtkWidget *top_window, PRBool state);
     void              *SetupPluginPort(void);
     nsresult           SetWindowIconList(const nsTArray<nsCString> &aIconList);
     void               SetDefaultIcon(void);
     void               InitButtonEvent(nsMouseEvent &aEvent, GdkEventButton *aGdkEvent);
     PRBool             DispatchCommandEvent(nsIAtom* aCommand);
     void               SetWindowClipRegion(const nsTArray<nsIntRect>& aRects,
                                            PRBool aIntersectWithExisting);
+    PRBool             GetDragInfo(nsMouseEvent* aMouseEvent,
+                                   GdkWindow** aWindow, gint* aButton,
+                                   gint* aRootX, gint* aRootY);
 
     GtkWidget          *mShell;
     MozContainer       *mContainer;
     GdkWindow          *mGdkWindow;
 
     GtkWindowGroup     *mWindowGroup;
 
     PRUint32            mHasMappedToplevel : 1,
--- a/widget/src/xpwidgets/nsBaseWidget.cpp
+++ b/widget/src/xpwidgets/nsBaseWidget.cpp
@@ -1036,16 +1036,22 @@ nsBaseWidget::ResolveIconName(const nsAS
     NS_ADDREF(*aResult = file);
 }
 
 NS_IMETHODIMP 
 nsBaseWidget::BeginResizeDrag(nsGUIEvent* aEvent, PRInt32 aHorizontal, PRInt32 aVertical)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
+
+NS_IMETHODIMP
+nsBaseWidget::BeginMoveDrag(nsMouseEvent* aEvent)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
  
 //////////////////////////////////////////////////////////////
 //
 // Code to sort rectangles for scrolling.
 //
 // The algorithm used here is similar to that described at
 // http://weblogs.mozillazine.org/roc/archives/2009/08/homework_answer.html
 //
--- a/widget/src/xpwidgets/nsBaseWidget.h
+++ b/widget/src/xpwidgets/nsBaseWidget.h
@@ -127,16 +127,17 @@ public:
   NS_IMETHOD              SetIcon(const nsAString &anIconSpec);
   NS_IMETHOD              BeginSecureKeyboardInput();
   NS_IMETHOD              EndSecureKeyboardInput();
   NS_IMETHOD              SetWindowTitlebarColor(nscolor aColor, PRBool aActive);
   virtual void            SetDrawsInTitlebar(PRBool aState) {}
   virtual PRBool          ShowsResizeIndicator(nsIntRect* aResizerRect);
   virtual void            FreeNativeData(void * data, PRUint32 aDataType) {}
   NS_IMETHOD              BeginResizeDrag(nsGUIEvent* aEvent, PRInt32 aHorizontal, PRInt32 aVertical);
+  NS_IMETHOD              BeginMoveDrag(nsMouseEvent* aEvent);
   virtual nsresult        ActivateNativeMenuItemAt(const nsAString& indexString) { return NS_ERROR_NOT_IMPLEMENTED; }
   virtual nsresult        ForceUpdateNativeMenuAt(const nsAString& indexString) { return NS_ERROR_NOT_IMPLEMENTED; }
   NS_IMETHOD              ResetInputState() { return NS_OK; }
   NS_IMETHOD              SetIMEOpenState(PRBool aState) { return NS_ERROR_NOT_IMPLEMENTED; }
   NS_IMETHOD              GetIMEOpenState(PRBool* aState) { return NS_ERROR_NOT_IMPLEMENTED; }
   NS_IMETHOD              SetIMEEnabled(PRUint32 aState) { return NS_ERROR_NOT_IMPLEMENTED; }
   NS_IMETHOD              GetIMEEnabled(PRUint32* aState) { return NS_ERROR_NOT_IMPLEMENTED; }
   NS_IMETHOD              CancelIMEComposition() { return NS_OK; }