Bug 449734 part 1 - Preserve presentation when dragging a tab between browser windows. r=roc a=blocking2.0:betaN
authorMats Palmgren <matspal@gmail.com>
Sat, 18 Sep 2010 13:28:49 +0200
changeset 54309 8d3d6240f81e5930f34dae220aa99d10c35ce2e3
parent 54308 3c43aa9d0051359e8efbd75b243e5904f70d2388
child 54310 f0993738b5990be7d4277f9b2c0bae802d877906
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, blocking2
bugs449734
milestone2.0b7pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 449734 part 1 - Preserve presentation when dragging a tab between browser windows. r=roc a=blocking2.0:betaN
browser/base/content/test/Makefile.in
browser/base/content/test/browser_tab_dragdrop.js
content/base/src/nsFrameLoader.cpp
layout/base/FrameLayerBuilder.h
layout/generic/nsObjectFrame.cpp
layout/generic/nsObjectFrame.h
layout/generic/nsSubDocumentFrame.cpp
view/public/nsIView.h
widget/src/gtk2/nsWindow.cpp
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -179,16 +179,17 @@ endif
                  browser_popupUI.js \
                  browser_relatedTabs.js \
                  browser_sanitize-passwordDisabledHosts.js \
                  browser_sanitize-sitepermissions.js \
                  browser_sanitize-timespans.js \
                  browser_sanitizeDialog.js \
                  browser_scope.js \
                  browser_selectTabAtIndex.js \
+                 browser_tab_dragdrop.js \
                  browser_tabfocus.js \
                  browser_tabs_owner.js \
                  browser_visibleTabs.js \
                  browser_visibleTabs_contextMenu.js \
                  browser_visibleTabs_bookmarkAllPages.js \
                  browser_visibleTabs_bookmarkAllTabs.js \
                  browser_visibleTabs_tabPreview.js \
                  bug592338.html \
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_tab_dragdrop.js
@@ -0,0 +1,114 @@
+function test()
+{
+  var embed = '<embed type="application/x-test" allowscriptaccess="always" allowfullscreen="true" wmode="window" width="640" height="480"></embed>'
+
+  waitForExplicitFinish();
+
+  // create a few tabs
+  var tabs = [
+    gBrowser.tabs[0],
+    gBrowser.addTab("about:blank", {skipAnimation: true}),
+    gBrowser.addTab("about:blank", {skipAnimation: true}),
+    gBrowser.addTab("about:blank", {skipAnimation: true}),
+    gBrowser.addTab("about:blank", {skipAnimation: true})
+  ];
+
+  function setLocation(i, url) {
+    gBrowser.getBrowserForTab(tabs[i]).contentWindow.location = url;
+  }
+  function moveTabTo(a, b) {
+    gBrowser.swapBrowsersAndCloseOther(gBrowser.tabs[b], gBrowser.tabs[a]);
+  }
+  function clickTest(doc, win) {
+    var clicks = doc.defaultView.clicks;
+    EventUtils.synthesizeMouse(doc.body, 100, 600, {}, win);
+    is(doc.defaultView.clicks, clicks+1, "adding 1 more click on BODY");
+  }
+  function test1() {
+    moveTabTo(2, 3); // now: 0 1 2 4
+    is(gBrowser.tabs[1], tabs[1], "tab1");
+    is(gBrowser.tabs[2], tabs[3], "tab3");
+
+    var plugin = gBrowser.getBrowserForTab(tabs[4]).docShell.contentViewer.DOMDocument.wrappedJSObject.body.firstChild;
+    var tab4_plugin_object = plugin.getObjectValue();
+
+    gBrowser.selectedTab = gBrowser.tabs[2];
+    moveTabTo(3, 2); // now: 0 1 4
+    gBrowser.selectedTab = tabs[4];
+    var doc = gBrowser.getBrowserForTab(gBrowser.tabs[2]).docShell.contentViewer.DOMDocument.wrappedJSObject;
+    plugin = doc.body.firstChild;
+    ok(plugin && plugin.checkObjectValue(tab4_plugin_object), "same plugin instance");
+    is(gBrowser.tabs[1], tabs[1], "tab1");
+    is(gBrowser.tabs[2], tabs[3], "tab4");
+    is(doc.defaultView.clicks, 0, "no click on BODY so far");
+    clickTest(doc, window);
+
+    moveTabTo(2, 1); // now: 0 4
+    is(gBrowser.tabs[1], tabs[1], "tab1");
+    doc = gBrowser.getBrowserForTab(gBrowser.tabs[1]).docShell.contentViewer.DOMDocument.wrappedJSObject;
+    plugin = doc.body.firstChild;
+    ok(plugin && plugin.checkObjectValue(tab4_plugin_object), "same plugin instance");
+    clickTest(doc, window);
+
+    // Load a new document (about:blank) in tab4, then detach that tab into a new window.
+    // In the new window, navigate back to the original document and click on its <body>,
+    // verify that its onclick was called.
+    var t = tabs[1];
+    var b = gBrowser.getBrowserForTab(t);
+    gBrowser.selectedTab = t;
+    b.addEventListener("load", function() {
+      b.removeEventListener("load", arguments.callee, true);
+
+      executeSoon(function () {
+        var win = gBrowser.replaceTabWithWindow(t);
+        win.addEventListener("load", function () {
+          win.removeEventListener("load", arguments.callee, true);
+
+          // Verify that the original window now only has the initial tab left in it.
+          is(gBrowser.tabs[0], tabs[0], "tab0");
+          is(gBrowser.getBrowserForTab(gBrowser.tabs[0]).contentWindow.location, "about:blank", "tab0 uri");
+
+          executeSoon(function () {
+            win.gBrowser.addEventListener("pageshow", function () {
+              win.gBrowser.removeEventListener("pageshow", arguments.callee, false);
+              executeSoon(function () {
+                t = win.gBrowser.tabs[0];
+                b = win.gBrowser.getBrowserForTab(t);
+                var doc = b.docShell.contentViewer.DOMDocument.wrappedJSObject;
+                clickTest(doc, win);
+                win.close();
+                finish();
+              });
+            }, false);
+            win.gBrowser.goBack();
+          });
+        }, true);
+      });
+    }, true);
+    b.loadURI("about:blank");
+
+  }
+
+  var loads = 0;
+  function waitForLoad(tab) {
+    gBrowser.getBrowserForTab(gBrowser.tabs[tab]).removeEventListener("load", arguments.callee, true);
+    ++loads;
+    if (loads == tabs.length - 1) {
+      executeSoon(test1);
+    }
+  }
+
+  function fn(f, arg) {
+    return function () { return f(arg); };
+  }
+  for (var i = 1; i < tabs.length; ++i) {
+    gBrowser.getBrowserForTab(tabs[i]).addEventListener("load", fn(waitForLoad,i), true);
+  }
+
+  setLocation(1, "data:text/html,<title>tab1</title><body>tab1<iframe>");
+  setLocation(2, "data:text/plain,tab2");
+  setLocation(3, "data:text/html,<title>tab3</title><body>tab3<iframe>");
+  setLocation(4, "data:text/html,<body onload='clicks=0' onclick='++clicks'>"+embed);
+  gBrowser.selectedTab = tabs[3];
+
+}
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -1042,25 +1042,25 @@ nsFrameLoader::SwapWithOtherLoader(nsFra
     do_QueryInterface(otherHistory);
   if (ourInternalHistory) {
     ourInternalHistory->EvictAllContentViewers();
   }
   if (otherInternalHistory) {
     otherInternalHistory->EvictAllContentViewers();
   }
 
-  // We shouldn't have changed frames, but be really careful about it
-  if (ourFrame == ourContent->GetPrimaryFrame() &&
-      otherFrame == otherContent->GetPrimaryFrame()) {
-    ourFrameFrame->EndSwapDocShells(otherFrame);
-  }
+  NS_ASSERTION(ourFrame == ourContent->GetPrimaryFrame() &&
+               otherFrame == otherContent->GetPrimaryFrame(),
+               "changed primary frame");
+
+  ourFrameFrame->EndSwapDocShells(otherFrame);
 
   ourParentDocument->FlushPendingNotifications(Flush_Layout);
   otherParentDocument->FlushPendingNotifications(Flush_Layout);
-  
+
   FirePageShowEvent(ourTreeItem, otherChromeEventHandler, PR_TRUE);
   FirePageShowEvent(otherTreeItem, ourChromeEventHandler, PR_TRUE);
 
   mInSwap = aOther->mInSwap = PR_FALSE;
   return NS_OK;
 }
 
 void
--- a/layout/base/FrameLayerBuilder.h
+++ b/layout/base/FrameLayerBuilder.h
@@ -266,16 +266,24 @@ public:
    * Try to determine whether the ThebesLayer aLayer paints an opaque
    * single color everywhere it's visible in aRect.
    * If successful, return that color, otherwise return NS_RGBA(0,0,0,0).
    */
   nscolor FindOpaqueColorCovering(nsDisplayListBuilder* aBuilder,
                                   ThebesLayer* aLayer, const nsRect& aRect);
 
   /**
+   * Destroy any stored DisplayItemDataProperty for aFrame.
+   */
+  static void DestroyDisplayItemDataFor(nsIFrame* aFrame)
+  {
+    aFrame->Properties().Delete(DisplayItemDataProperty());
+  }
+
+  /**
    * Clip represents the intersection of an optional rectangle with a
    * list of rounded rectangles.
    */
   struct Clip {
     struct RoundedRect {
       nsRect mRect;
       // Indices into mRadii are the NS_CORNER_* constants in nsStyleConsts.h
       nscoord mRadii[8];
--- a/layout/generic/nsObjectFrame.cpp
+++ b/layout/generic/nsObjectFrame.cpp
@@ -23,17 +23,17 @@
  * Contributor(s):
  *   Pierre Phaneuf <pp@ludusdesign.com>
  *   Jacek Piskozub <piskozub@iopan.gda.pl>
  *   Leon Sha <leon.sha@sun.com>
  *   Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
  *   Robert O'Callahan <roc+moz@cs.cmu.edu>
  *   Christian Biesinger <cbiesinger@web.de>
  *   Josh Aas <josh@mozilla.com>
- *   Mats Palmgren <mats.palmgren@bredband.net>
+ *   Mats Palmgren <matspal@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of the GNU General Public License Version 2 or later (the "GPL"),
  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -2629,16 +2629,63 @@ nsObjectFrame::GetNextObjectFrame(nsPres
     if (outFrame)
       return outFrame;
     child = child->GetNextSibling();
   }
 
   return nsnull;
 }
 
+/*static*/ void
+nsObjectFrame::BeginSwapDocShells(nsIContent* aContent, void*)
+{
+  NS_PRECONDITION(aContent, "");
+
+  // This function is called from a document content enumerator so we need
+  // to filter out the nsObjectFrames and ignore the rest.
+  nsIObjectFrame* obj = do_QueryFrame(aContent->GetPrimaryFrame());
+  if (!obj)
+    return;
+
+  nsObjectFrame* objectFrame = static_cast<nsObjectFrame*>(obj);
+  NS_ASSERTION(!objectFrame->mWidget || objectFrame->mWidget->GetParent(),
+               "Plugin windows must not be toplevel");
+  nsRootPresContext* rootPC = objectFrame->PresContext()->GetRootPresContext();
+  NS_ASSERTION(rootPC, "unable to unregister the plugin frame");
+  rootPC->UnregisterPluginForGeometryUpdates(objectFrame);
+}
+
+/*static*/ void
+nsObjectFrame::EndSwapDocShells(nsIContent* aContent, void*)
+{
+  NS_PRECONDITION(aContent, "");
+
+  // This function is called from a document content enumerator so we need
+  // to filter out the nsObjectFrames and ignore the rest.
+  nsIObjectFrame* obj = do_QueryFrame(aContent->GetPrimaryFrame());
+  if (!obj)
+    return;
+
+  nsObjectFrame* objectFrame = static_cast<nsObjectFrame*>(obj);
+  nsRootPresContext* rootPC = objectFrame->PresContext()->GetRootPresContext();
+  NS_ASSERTION(rootPC, "unable to register the plugin frame");
+  nsIWidget* widget = objectFrame->GetWidget();
+  if (widget) {
+    // Reparent the widget.
+    nsIWidget* parent =
+      rootPC->PresShell()->GetRootFrame()->GetNearestWidget();
+    widget->SetParent(parent);
+    objectFrame->CallSetWindow();
+
+    // Register for geometry updates and make a request.
+    rootPC->RegisterPluginForGeometryUpdates(objectFrame);
+    rootPC->RequestUpdatePluginGeometry(objectFrame);
+  }
+}
+
 nsIFrame*
 NS_NewObjectFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsObjectFrame(aContext);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsObjectFrame)
 
--- a/layout/generic/nsObjectFrame.h
+++ b/layout/generic/nsObjectFrame.h
@@ -180,16 +180,29 @@ public:
                                      LayerManager* aManager,
                                      nsDisplayItem* aItem);
 
   virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                    LayerManager* aManager);
 
   ImageContainer* GetImageContainer();
 
+  /**
+   * If aContent has a nsObjectFrame, then prepare it for a DocShell swap.
+   * @see nsSubDocumentFrame::BeginSwapDocShells.
+   * There will be a call to EndSwapDocShells after we were moved to the
+   * new view tree.
+   */
+  static void BeginSwapDocShells(nsIContent* aContent, void*);
+  /**
+   * If aContent has a nsObjectFrame, then set it up after a DocShell swap.
+   * @see nsSubDocumentFrame::EndSwapDocShells.
+   */
+  static void EndSwapDocShells(nsIContent* aContent, void*);
+
 protected:
   nsObjectFrame(nsStyleContext* aContext);
   virtual ~nsObjectFrame();
 
   // NOTE:  This frame class does not inherit from |nsLeafFrame|, so
   // this is not a virtual method implementation.
   void GetDesiredSize(nsPresContext* aPresContext,
                       const nsHTMLReflowState& aReflowState,
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -17,16 +17,17 @@
  * The Initial Developer of the Original Code is
  * Netscape Communications Corporation.
  * Portions created by the Initial Developer are Copyright (C) 1998
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Travis Bogard <travis@netscape.com>
  *   HÂkan Waara <hwaara@chello.se>
+ *   Mats Palmgren <matspal@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of the GNU General Public License Version 2 or later (the "GPL"),
  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -87,27 +88,41 @@ using mozilla::layout::RenderFrameParent
 #include "nsIDOMDocument.h"
 #include "nsIRenderingContext.h"
 #include "nsIDOMNSHTMLDocument.h"
 #include "nsDisplayList.h"
 #include "nsUnicharUtils.h"
 #include "nsIScrollableFrame.h"
 #include "nsIObjectLoadingContent.h"
 #include "nsLayoutUtils.h"
+#include "FrameLayerBuilder.h"
+#include "nsObjectFrame.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #endif
 
 // For Accessibility
 #ifdef ACCESSIBILITY
 #include "nsIAccessibilityService.h"
 #endif
 #include "nsIServiceManager.h"
 
+using namespace mozilla;
+
+static nsIDocument*
+GetDocumentFromView(nsIView* aView)
+{
+  NS_PRECONDITION(aView, "");
+
+  nsIFrame* f = static_cast<nsIFrame*>(aView->GetClientData());
+  nsIPresShell* ps =  f ? f->PresContext()->PresShell() : nsnull;
+  return ps ? ps->GetDocument() : nsnull;
+}
+
 class AsyncFrameInit;
 
 nsSubDocumentFrame::nsSubDocumentFrame(nsStyleContext* aContext)
   : nsLeafFrame(aContext)
   , mIsInline(PR_FALSE)
   , mPostedReflowCallback(PR_FALSE)
   , mDidCreateDoc(PR_FALSE)
   , mCallingShow(PR_FALSE)
@@ -818,44 +833,146 @@ nsresult
 nsSubDocumentFrame::GetDocShell(nsIDocShell **aDocShell)
 {
   *aDocShell = nsnull;
 
   NS_ENSURE_STATE(FrameLoader());
   return mFrameLoader->GetDocShell(aDocShell);
 }
 
+static void
+DestroyDisplayItemDataForFrames(nsIFrame* aFrame)
+{
+  FrameLayerBuilder::DestroyDisplayItemDataFor(aFrame);
+
+  PRInt32 listIndex = 0;
+  nsIAtom* childList = nsnull;
+  do {
+    nsIFrame* child = aFrame->GetFirstChild(childList);
+    while (child) {
+      DestroyDisplayItemDataForFrames(child);
+      child = child->GetNextSibling();
+    }
+    childList = aFrame->GetAdditionalChildListName(listIndex++);
+  } while (childList);
+}
+
+static PRBool
+BeginSwapDocShellsForDocument(nsIDocument* aDocument, void*)
+{
+  NS_PRECONDITION(aDocument, "");
+
+  nsIPresShell* shell = aDocument->GetShell();
+  nsIFrame* rootFrame = shell ? shell->GetRootFrame() : nsnull;
+  if (rootFrame) {
+    ::DestroyDisplayItemDataForFrames(rootFrame);
+  }
+  aDocument->EnumerateFreezableElements(
+    nsObjectFrame::BeginSwapDocShells, nsnull);
+  aDocument->EnumerateSubDocuments(BeginSwapDocShellsForDocument, nsnull);
+  return PR_TRUE;
+}
+
+static nsIView*
+BeginSwapDocShellsForViews(nsIView* aSibling)
+{
+  // Collect the removed sibling views in reverse order in 'removedViews'.
+  nsIView* removedViews = nsnull;
+  while (aSibling) {
+    nsIDocument* doc = ::GetDocumentFromView(aSibling);
+    if (doc) {
+      ::BeginSwapDocShellsForDocument(doc, nsnull);
+    }
+    nsIView* next = aSibling->GetNextSibling();
+    aSibling->GetViewManager()->RemoveChild(aSibling);
+    aSibling->SetNextSibling(removedViews);
+    removedViews = aSibling;
+    aSibling = next;
+  }
+  return removedViews;
+}
+
+static void
+InsertViewsInReverseOrder(nsIView* aSibling, nsIView* aParent)
+{
+  NS_PRECONDITION(aParent, "");
+  NS_PRECONDITION(!aParent->GetFirstChild(), "inserting into non-empty list");
+
+  nsIViewManager* vm = aParent->GetViewManager();
+  while (aSibling) {
+    nsIView* next = aSibling->GetNextSibling();
+    aSibling->SetNextSibling(nsnull);
+    // PR_TRUE means 'after' in document order which is 'before' in view order,
+    // so this call prepends the child, thus reversing the siblings as we go.
+    vm->InsertChild(aParent, aSibling, nsnull, PR_TRUE);
+    aSibling = next;
+  }
+}
+
 nsresult
 nsSubDocumentFrame::BeginSwapDocShells(nsIFrame* aOther)
 {
   if (!aOther || aOther->GetType() != nsGkAtoms::subDocumentFrame) {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   nsSubDocumentFrame* other = static_cast<nsSubDocumentFrame*>(aOther);
   if (!mFrameLoader || !mDidCreateDoc || mCallingShow ||
       !other->mFrameLoader || !other->mDidCreateDoc) {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
-  HideViewer();
-  other->HideViewer();
+  if (mInnerView && other->mInnerView) {
+    nsIView* ourSubdocViews = mInnerView->GetFirstChild();
+    nsIView* ourRemovedViews = ::BeginSwapDocShellsForViews(ourSubdocViews);
+    nsIView* otherSubdocViews = other->mInnerView->GetFirstChild();
+    nsIView* otherRemovedViews = ::BeginSwapDocShellsForViews(otherSubdocViews);
 
+    ::InsertViewsInReverseOrder(ourRemovedViews, other->mInnerView);
+    ::InsertViewsInReverseOrder(otherRemovedViews, mInnerView);
+  }
   mFrameLoader.swap(other->mFrameLoader);
   return NS_OK;
 }
 
+static PRBool
+EndSwapDocShellsForDocument(nsIDocument* aDocument, void*)
+{
+  NS_PRECONDITION(aDocument, "");
+
+  aDocument->EnumerateFreezableElements(
+    nsObjectFrame::EndSwapDocShells, nsnull);
+  aDocument->EnumerateSubDocuments(EndSwapDocShellsForDocument, nsnull);
+  return PR_TRUE;
+}
+
+static void
+EndSwapDocShellsForViews(nsIView* aSibling)
+{
+  for ( ; aSibling; aSibling = aSibling->GetNextSibling()) {
+    nsIDocument* doc = ::GetDocumentFromView(aSibling);
+    if (doc) {
+      ::EndSwapDocShellsForDocument(doc, nsnull);
+    }
+  }
+}
+
 void
 nsSubDocumentFrame::EndSwapDocShells(nsIFrame* aOther)
 {
   nsSubDocumentFrame* other = static_cast<nsSubDocumentFrame*>(aOther);
   nsWeakFrame weakThis(this);
   nsWeakFrame weakOther(aOther);
-  ShowViewer();
-  other->ShowViewer();
+
+  if (mInnerView) {
+    ::EndSwapDocShellsForViews(mInnerView->GetFirstChild());
+  }
+  if (other->mInnerView) {
+    ::EndSwapDocShellsForViews(other->mInnerView->GetFirstChild());
+  }
 
   // Now make sure we reflow both frames, in case their contents
   // determine their size.
   // And repaint them, for good measure, in case there's nothing
   // interesting that happens during reflow.
   if (weakThis.IsAlive()) {
     PresContext()->PresShell()->
       FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
--- a/view/public/nsIView.h
+++ b/view/public/nsIView.h
@@ -243,16 +243,19 @@ public:
    */
   nsIView* GetFirstChild() const { return reinterpret_cast<nsIView*>(mFirstChild); }
 
   /**
    * Called to query the next sibling of the view.
    * @result view's next sibling
    */
   nsIView* GetNextSibling() const { return reinterpret_cast<nsIView*>(mNextSibling); }
+  void SetNextSibling(nsIView *aSibling) {
+    mNextSibling = reinterpret_cast<nsView*>(aSibling);
+  }
 
   /**
    * Set the view's link to client owned data.
    * @param aData - data to associate with view. nsnull to disassociate
    */
   void SetClientData(void *aData) { mClientData = aData; }
 
   /**
--- a/widget/src/gtk2/nsWindow.cpp
+++ b/widget/src/gtk2/nsWindow.cpp
@@ -16,17 +16,17 @@
  *
  * The Original Code is mozilla.org code.
  *
  * The Initial Developer of the Original Code is Christopher Blizzard
  * <blizzard@mozilla.org>.  Portions created by the Initial Developer
  * are Copyright (C) 2001 the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
- *   Mats Palmgren <mats.palmgren@bredband.net>
+ *   Mats Palmgren <matspal@gmail.com>
  *   Masayuki Nakano <masayuki@d-toybox.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
@@ -885,17 +885,17 @@ nsWindow::SetParent(nsIWidget *aNewParen
         Destroy();
     } else {
         if (newContainer != oldContainer) {
             NS_ABORT_IF_FALSE(!GDK_WINDOW_OBJECT(newParentWindow)->destroyed,
                               "destroyed GdkWindow with widget");
             SetWidgetForHierarchy(mGdkWindow, oldContainer, newContainer);
         }
 
-        gdk_window_reparent(mGdkWindow, newParentWindow, 0, 0);
+        gdk_window_reparent(mGdkWindow, newParentWindow, mBounds.x, mBounds.y);
     }
 
     PRBool parentHasMappedToplevel =
         newParent && newParent->mHasMappedToplevel;
     if (mHasMappedToplevel != parentHasMappedToplevel) {
         SetHasMappedToplevel(parentHasMappedToplevel);
     }