Bug 395334, allow noautohide panels to have a parent so they that float and move with their parent, also fixes bug 395123, r=josh,roc,sr=roc
authorenndeakin@sympatico.ca
Wed, 28 Nov 2007 12:18:11 -0800
changeset 8408 ede47a2bbbf8aeeee8420900e7a5b4da90120a17
parent 8407 c295c87142cd1a34126b941bd7f258b69449a99b
child 8409 723aaeba56a17e5a6f9ad238bd8b37db1f52cfdb
push idunknown
push userunknown
push dateunknown
reviewersjosh, roc, roc
bugs395334, 395123
milestone1.9b2pre
Bug 395334, allow noautohide panels to have a parent so they that float and move with their parent, also fixes bug 395123, r=josh,roc,sr=roc
layout/build/nsLayoutCID.h
layout/build/nsLayoutModule.cpp
layout/xul/base/public/nsXULPopupManager.h
layout/xul/base/src/nsMenuPopupFrame.cpp
layout/xul/base/src/nsMenuPopupFrame.h
layout/xul/base/src/nsPopupBoxObject.cpp
layout/xul/base/src/nsXULPopupManager.cpp
view/public/nsIView.h
view/src/nsView.cpp
widget/public/nsIMenuRollup.idl
widget/src/cocoa/nsCocoaWindow.mm
widget/src/gtk2/nsWindow.cpp
widget/src/windows/nsWindow.cpp
xpfe/appshell/src/nsWebShellWindow.cpp
--- a/layout/build/nsLayoutCID.h
+++ b/layout/build/nsLayoutCID.h
@@ -213,9 +213,13 @@
 // {8b449142-1eab-4bfa-9830-fab6ebb09774}
 #define NS_DOMSTORAGE_CID \
 { 0x8b449142, 0x1eab, 0x4bfa, { 0x98, 0x30, 0xfa, 0xb6, 0xeb, 0xb0, 0x97, 0x74 } }
 
 // {b88a4712-eb52-4c10-9b85-bf5894b510f0}
 #define NS_DOMSTORAGEMANAGER_CID               \
 { 0xb88a4712, 0xeb52, 0x4c10, { 0x9b, 0x85, 0xbf, 0x58, 0x94, 0xb5, 0x10, 0xf0 } }
 
+// {14632191-AC21-4BDF-83E7-2363AD17E838}
+#define NS_XULPOPUPMANAGER_CID \
+{ 0x14632191, 0xac21, 0x4bdf, { 0x83, 0xe7, 0x23, 0x63, 0xad, 0x17, 0xe8, 0x38 } }
+
 #endif /* nsLayoutCID_h__ */
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -94,16 +94,17 @@
 #include "nsContentAreaDragDrop.h"
 #include "nsContentList.h"
 #include "nsSyncLoadService.h"
 #include "nsBox.h"
 #include "nsIFrameTraversal.h"
 #include "nsLayoutCID.h"
 #include "nsILanguageAtomService.h"
 #include "nsStyleSheetService.h"
+#include "nsXULPopupManager.h"
 
 // Transformiix stuff
 #include "nsXPathEvaluator.h"
 #include "txMozillaXSLTProcessor.h"
 #include "txNodeSetAdaptor.h"
 #include "nsXPath1Scheme.h"
 
 #include "nsDOMParser.h"
@@ -501,16 +502,17 @@ MAKE_CTOR(CreateContentPolicy,          
 MAKE_CTOR(CreateComputedDOMStyle,         nsIComputedDOMStyle,         NS_NewComputedDOMStyle)
 #ifdef MOZ_XUL
 MAKE_CTOR(CreateXULSortService,           nsIXULSortService,           NS_NewXULSortService)
 // NS_NewXULContentBuilder
 // NS_NewXULTreeBuilder
 MAKE_CTOR(CreateXULDocument,              nsIXULDocument,              NS_NewXULDocument)
 // NS_NewXULControllers
 // NS_NewXULPrototypeCache
+MAKE_CTOR(CreateXULPopupManager,      nsISupports,      NS_NewXULPopupManager)
 #endif
 #ifdef MOZ_XTF
 MAKE_CTOR(CreateXTFService,               nsIXTFService,               NS_NewXTFService)
 MAKE_CTOR(CreateXMLContentBuilder,        nsIXMLContentBuilder,        NS_NewXMLContentBuilder)
 #endif
 MAKE_CTOR(CreateContentDLF,               nsIDocumentLoaderFactory,    NS_NewContentDocumentLoaderFactory)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsCSSOMFactory)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsInspectorCSSUtils)
@@ -1171,16 +1173,21 @@ static const nsModuleComponentInfo gComp
     "@mozilla.org/xul/xul-template-builder;1",
     NS_NewXULContentBuilder },
 
   { "XUL Tree Builder",
     NS_XULTREEBUILDER_CID,
     "@mozilla.org/xul/xul-tree-builder;1",
     NS_NewXULTreeBuilder },
 
+  { "XUL Popup Manager",
+    NS_XULPOPUPMANAGER_CID,
+    "@mozilla.org/xul/xul-popup-manager;1",
+    CreateXULPopupManager },
+
   { "XUL Document",
     NS_XULDOCUMENT_CID,
     "@mozilla.org/xul/xul-document;1",
     CreateXULDocument },
 
   { "XUL Prototype Cache",
     NS_XULPROTOTYPECACHE_CID,
     "@mozilla.org/xul/xul-prototype-cache;1",
--- a/layout/xul/base/public/nsXULPopupManager.h
+++ b/layout/xul/base/public/nsXULPopupManager.h
@@ -732,9 +732,12 @@ protected:
 
   // timer used for HidePopupAfterDelay
   nsCOMPtr<nsITimer> mCloseTimer;
 
   // a popup that is waiting on the timer
   nsMenuPopupFrame* mTimerMenu;
 };
 
+nsresult
+NS_NewXULPopupManager(nsISupports** aResult);
+
 #endif
--- a/layout/xul/base/src/nsMenuPopupFrame.cpp
+++ b/layout/xul/base/src/nsMenuPopupFrame.cpp
@@ -75,16 +75,18 @@
 #include "nsUnicharUtils.h"
 #include "nsLayoutUtils.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsIEventStateManager.h"
 #include "nsIBoxLayout.h"
 #include "nsIPopupBoxObject.h"
 #include "nsIReflowCallback.h"
 #include "nsBindingManager.h"
+#include "nsIDocShellTreeOwner.h"
+#include "nsIBaseWindow.h"
 #ifdef XP_WIN
 #include "nsISound.h"
 #endif
 
 const PRInt32 kMaxZ = 0x7fffffff; //XXX: Shouldn't there be a define somewhere for MaxInt for PRInt32
 
 // NS_NewMenuPopupFrame
 //
@@ -169,16 +171,27 @@ nsMenuPopupFrame::Init(nsIContent*      
   // the popup opens.
   if (!IsLeaf() && !ourView->HasWidget()) {
     CreateWidgetForView(ourView);
   }
 
   return rv;
 }
 
+PRBool
+nsMenuPopupFrame::IsNoAutoHide()
+{
+  // Panels with noautohide="true" don't hide when the mouse is clicked
+  // outside of them, or when another application is made active. Non-autohide
+  // panels cannot be used in content windows.
+  return (!mInContentShell && mPopupType == ePopupTypePanel &&
+           mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::noautohide,
+                                 nsGkAtoms::_true, eIgnoreCase));
+}
+
 void
 nsMenuPopupFrame::EnsureWidget()
 {
   nsIView* ourView = GetView();
   if (!ourView->HasWidget()) {
     NS_ASSERTION(!mGeneratedChildren && !GetFirstChild(nsnull),
                  "Creating widget for MenuPopupFrame with children");
     CreateWidgetForView(ourView);
@@ -196,24 +209,44 @@ nsMenuPopupFrame::CreateWidgetForView(ns
 
   PRBool viewHasTransparentContent = !mInContentShell &&
                                      nsLayoutUtils::FrameHasTransparency(this);
   nsIContent* parentContent = GetContent()->GetParent();
   nsIAtom *tag = nsnull;
   if (parentContent)
     tag = parentContent->Tag();
   widgetData.mDropShadow = !(viewHasTransparentContent || tag == nsGkAtoms::menulist);
-  
+
+  // panels which don't auto-hide need a parent widget. This allows them
+  // to always appear in front of the parent window but behind other windows
+  // that should be in front of it.
+  nsCOMPtr<nsIWidget> parentWidget;
+  if (IsNoAutoHide()) {
+    nsCOMPtr<nsISupports> cont = PresContext()->GetContainer();
+    nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(cont);
+    if (!dsti)
+      return NS_ERROR_FAILURE;
+
+    nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
+    dsti->GetTreeOwner(getter_AddRefs(treeOwner));
+    if (!treeOwner) return NS_ERROR_FAILURE;
+
+    nsCOMPtr<nsIBaseWindow> baseWindow(do_QueryInterface(treeOwner));
+    if (baseWindow)
+      baseWindow->GetMainWidget(getter_AddRefs(parentWidget));
+  }
+
 #if defined(XP_MACOSX) || defined(XP_BEOS)
   static NS_DEFINE_IID(kCPopupCID,  NS_POPUP_CID);
   aView->CreateWidget(kCPopupCID, &widgetData, nsnull, PR_TRUE, PR_TRUE, 
-                      eContentTypeUI);
+                      eContentTypeUI, parentWidget);
 #else
   static NS_DEFINE_IID(kCChildCID,  NS_CHILD_CID);
-  aView->CreateWidget(kCChildCID, &widgetData, nsnull, PR_TRUE, PR_TRUE);
+  aView->CreateWidget(kCChildCID, &widgetData, nsnull, PR_TRUE, PR_TRUE,
+                      eContentTypeInherit, parentWidget);
 #endif
   aView->GetWidget()->SetWindowTranslucency(viewHasTransparentContent);
   return NS_OK;
 }
 
 // this class is used for dispatching popupshowing events asynchronously.
 class nsXULPopupShownEvent : public nsRunnable
 {
@@ -1633,20 +1666,20 @@ nsMenuPopupFrame::MoveToInternal(PRInt32
   // Move the widget. The widget will be null if it hasn't been created yet,
   // but that's OK as the popup won't be open in this case.
   // XXXbz don't we want screenPos to be the parent _widget_'s position, then?
   nsIWidget* widget = view->GetWidget();
   if (widget) 
     widget->Move(aLeft - screenPos.x, aTop - screenPos.y);
 }
 
-void 
-nsMenuPopupFrame::GetAutoPosition(PRBool* aShouldAutoPosition)
+PRBool
+nsMenuPopupFrame::GetAutoPosition()
 {
-  *aShouldAutoPosition = mShouldAutoPosition;
+  return mShouldAutoPosition;
 }
 
 void
 nsMenuPopupFrame::SetAutoPosition(PRBool aShouldAutoPosition)
 {
   mShouldAutoPosition = aShouldAutoPosition;
 }
 
--- a/layout/xul/base/src/nsMenuPopupFrame.h
+++ b/layout/xul/base/src/nsMenuPopupFrame.h
@@ -173,16 +173,20 @@ public:
                               PRInt32 aModType);
 
   virtual void Destroy();
 
   virtual void InvalidateInternal(const nsRect& aDamageRect,
                                   nscoord aX, nscoord aY, nsIFrame* aForChild,
                                   PRBool aImmediate);
 
+  // returns true if the popup is a panel with the noautohide attribute set to
+  // true. These panels do not roll up automatically.
+  PRBool IsNoAutoHide();
+
   void EnsureWidget();
 
   virtual nsresult CreateWidgetForView(nsIView* aView);
 
   NS_IMETHOD SetInitialChildList(nsIAtom*        aListName,
                                  nsIFrame*       aChildList);
 
   virtual PRBool IsLeaf() const;
@@ -259,17 +263,17 @@ public:
 #endif
 
   void EnsureMenuItemIsVisible(nsMenuFrame* aMenuFrame);
 
   // This sets 'left' and 'top' attributes.
   // May kill the frame.
   void MoveTo(PRInt32 aLeft, PRInt32 aTop);
 
-  void GetAutoPosition(PRBool* aShouldAutoPosition);
+  PRBool GetAutoPosition();
   void SetAutoPosition(PRBool aShouldAutoPosition);
   void SetConsumeRollupEvent(PRUint32 aConsumeMode);
 
   nsIScrollableView* GetScrollableView(nsIFrame* aStart);
   
 protected:
   // Move without updating attributes.                                          
   void MoveToInternal(PRInt32 aLeft, PRInt32 aTop);                             
--- a/layout/xul/base/src/nsPopupBoxObject.cpp
+++ b/layout/xul/base/src/nsPopupBoxObject.cpp
@@ -170,17 +170,17 @@ nsPopupBoxObject::SizeTo(PRInt32 aWidth,
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPopupBoxObject::GetAutoPosition(PRBool* aShouldAutoPosition)
 {
   nsMenuPopupFrame *menuPopupFrame = GetMenuPopupFrame();
   if (menuPopupFrame) {
-    menuPopupFrame->GetAutoPosition(aShouldAutoPosition);
+    *aShouldAutoPosition = menuPopupFrame->GetAutoPosition();
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPopupBoxObject::SetAutoPosition(PRBool aShouldAutoPosition)
 {
--- a/layout/xul/base/src/nsXULPopupManager.cpp
+++ b/layout/xul/base/src/nsXULPopupManager.cpp
@@ -206,16 +206,33 @@ nsXULPopupManager::GetSubmenuWidgetChain
                    item->IsContextMenu() != parent->IsContextMenu())
       break;
     item = parent;
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsXULPopupManager::AdjustPopupsOnWindowChange()
+{
+  // Panels are moved and kept aligned with the anchor when the parent window
+  // moves. We only look through the panels list, as menus are expected to
+  // roll up when a window is moved, so menus don't need to be moved.
+  nsMenuChainItem* item = mPanels;
+  while (item) {
+    // if the auto positioning has been disabled, don't move the popup
+    if (item->Frame()->GetAutoPosition())
+      item->Frame()->SetPopupPosition(nsnull);
+    item = item->GetParent();
+  }
+
+  return NS_OK;
+}
+
 nsIFrame*
 nsXULPopupManager::GetFrameOfTypeForContent(nsIContent* aContent,
                                             nsIAtom* aFrameType,
                                             PRBool aShouldFlush)
 {
   nsIDocument *document = aContent->GetCurrentDoc();
   if (document) {
     nsCOMPtr<nsIPresShell> presShell = document->GetPrimaryShell();
@@ -463,20 +480,17 @@ nsXULPopupManager::ShowPopupCallback(nsI
   nsWeakFrame weakFrame(aPopupFrame);
   PRBool hasChildren = aPopupFrame->ShowPopup(aIsContextMenu, aSelectFirstItem);
   ENSURE_TRUE(weakFrame.IsAlive());
 
   // popups normally hide when an outside click occurs. Panels may use
   // the noautohide attribute to disable this behaviour. It is expected
   // that the application will hide these popups manually. The tooltip
   // listener will handle closing the tooltip also.
-  if (popupType == ePopupTypeTooltip ||
-      (popupType == ePopupTypePanel &&
-       aPopup->AttrValueIs(kNameSpaceID_None, nsGkAtoms::noautohide,
-                            nsGkAtoms::_true, eIgnoreCase))) {
+  if (aPopupFrame->IsNoAutoHide() || popupType == ePopupTypeTooltip) {
     item->SetParent(mPanels);
     mPanels = item;
   }
   else {
     nsIContent* oldmenu = nsnull;
     if (mCurrentMenu)
       oldmenu = mCurrentMenu->Content();
     item->SetParent(mCurrentMenu);
@@ -1937,8 +1951,17 @@ nsXULMenuCommandEvent::Run()
     shell->HandleDOMEventWithTarget(mMenu, &commandEvent, &status);
   }
 
   if (popup && mCloseMenuMode != CloseMenuMode_None)
     pm->HidePopup(popup, mCloseMenuMode == CloseMenuMode_Auto, PR_TRUE, PR_FALSE);
 
   return NS_OK;
 }
+
+nsresult
+NS_NewXULPopupManager(nsISupports** aResult)
+{
+  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+  NS_IF_ADDREF(pm);
+  *aResult = static_cast<nsIMenuRollup *>(pm);
+  return NS_OK;
+}
--- a/view/public/nsIView.h
+++ b/view/public/nsIView.h
@@ -54,20 +54,20 @@ class nsWeakView;
 // show - the layer is shown irrespective of the visibility of 
 //        the layer's parent.
 enum nsViewVisibility {
   nsViewVisibility_kHide = 0,
   nsViewVisibility_kShow = 1
 };
 
 // IID for the nsIView interface
-// 1b0215f7-f5d1-4574-9ab9-abdcceddb82e
+// 1377A30E-99E6-42FA-9A2E-EEEC6B31B7B6
 #define NS_IVIEW_IID    \
-{ 0x1b0215f7, 0xf5d1, 0x4574, \
-  { 0x9a, 0xb9, 0xab, 0xdc, 0xce, 0xdd, 0xb8, 0x2e } }
+{ 0x1377ae0e, 0x99e6, 0x42fa, \
+{ 0x9a, 0x2e, 0xee, 0xec, 0x6b, 0x31, 0xb7, 0xb6 } }
 
 // Public view flags are defined in this file
 #define NS_VIEW_FLAGS_PUBLIC              0x00FF
 // Private view flags are private to the view module,
 // and are defined in nsView.h
 #define NS_VIEW_FLAGS_PRIVATE             0xFF00
 
 // Public view flags
@@ -276,24 +276,27 @@ public:
    * @param aWidgetInitData data used to initialize this view's widget before
    *        its create is called.
    * @param aNative native window that will be used as parent of
    *        aWindowIID. if nsnull, then parent will be derived from
    *        parent view and it's ancestors
    * @param aWindowType is either content, UI or inherit from parent window.
    *        This is used to expose what type of window this is to 
    *        assistive technology like screen readers.
+   * @param aParentWidget alternative parent to aNative used for popups. Must
+   *        be null for non-popups.
    * @return error status
    */
   nsresult CreateWidget(const nsIID &aWindowIID,
                         nsWidgetInitData *aWidgetInitData = nsnull,
                         nsNativeWidget aNative = nsnull,
                         PRBool aEnableDragDrop = PR_TRUE,
                         PRBool aResetVisibility = PR_TRUE,
-                        nsContentType aWindowType = eContentTypeInherit);
+                        nsContentType aWindowType = eContentTypeInherit,
+                        nsIWidget* aParentWidget = nsnull);
 
   /**
    * In 4.0, the "cutout" nature of a view is queryable.
    * If we believe that all cutout view have a native widget, this
    * could be a replacement.
    * @param aWidget out parameter for widget that this view contains,
    *        or nsnull if there is none.
    */
--- a/view/src/nsView.cpp
+++ b/view/src/nsView.cpp
@@ -601,17 +601,18 @@ static PRInt32 FindNonAutoZIndex(nsView*
   return 0;
 }
 
 nsresult nsIView::CreateWidget(const nsIID &aWindowIID,
                                nsWidgetInitData *aWidgetInitData,
                                nsNativeWidget aNative,
                                PRBool aEnableDragDrop,
                                PRBool aResetVisibility,
-                               nsContentType aContentType)
+                               nsContentType aContentType,
+                               nsIWidget* aParentWidget)
 {
   if (NS_UNLIKELY(mWindow)) {
     NS_ERROR("We already have a window for this view? BAD");
     ViewWrapper* wrapper = GetWrapperFor(mWindow);
     NS_IF_RELEASE(wrapper);
     mWindow->SetClientData(nsnull);
     NS_RELEASE(mWindow);
   }
@@ -638,35 +639,43 @@ nsresult nsIView::CreateWidget(const nsI
         // Create initData to pass in params
         initDataPassedIn = PR_FALSE;
         initData.clipChildren = PR_TRUE; // Clip child window's children
         initData.clipSiblings = PR_TRUE; // Clip child window's siblings
         aWidgetInitData = &initData;
       }
       aWidgetInitData->mContentType = aContentType;
 
-      if (aNative)
+      if (aNative && aWidgetInitData->mWindowType != eWindowType_popup)
         mWindow->Create(aNative, trect, ::HandleEvent, dx, nsnull, nsnull, aWidgetInitData);
       else
       {
         if (!initDataPassedIn && GetParent() && 
           GetParent()->GetViewManager() != mViewManager)
           initData.mListenForResizes = PR_TRUE;
-        nsIWidget* parentWidget = GetParent() ? GetParent()->GetNearestWidget(nsnull)
-                                              : nsnull;
-        if (aWidgetInitData->mWindowType == eWindowType_popup) {
-          // Without a parent, we can't make a popup.  This can happen
-          // when printing
-          if (!parentWidget)
-            return NS_ERROR_FAILURE;
-          mWindow->Create(parentWidget->GetNativeData(NS_NATIVE_WIDGET), trect,
+        if (aParentWidget) {
+          NS_ASSERTION(aWidgetInitData->mWindowType == eWindowType_popup,
+                       "popup widget type expected");
+          mWindow->Create(aParentWidget, trect,
                           ::HandleEvent, dx, nsnull, nsnull, aWidgetInitData);
-        } else {
-          mWindow->Create(parentWidget, trect,
-                          ::HandleEvent, dx, nsnull, nsnull, aWidgetInitData);
+        }
+        else {
+          nsIWidget* parentWidget = GetParent() ? GetParent()->GetNearestWidget(nsnull)
+                                                : nsnull;
+          if (aWidgetInitData->mWindowType == eWindowType_popup) {
+            // Without a parent, we can't make a popup.  This can happen
+            // when printing
+            if (!parentWidget)
+              return NS_ERROR_FAILURE;
+            mWindow->Create(parentWidget->GetNativeData(NS_NATIVE_WIDGET), trect,
+                            ::HandleEvent, dx, nsnull, nsnull, aWidgetInitData);
+          } else {
+            mWindow->Create(parentWidget, trect,
+                            ::HandleEvent, dx, nsnull, nsnull, aWidgetInitData);
+          }
         }
       }
       if (aEnableDragDrop) {
         mWindow->EnableDragDrop(PR_TRUE);
       }
       
       // propagate the z-index to the widget.
       UpdateNativeWidgetZIndexes(v, FindNonAutoZIndex(v));
--- a/widget/public/nsIMenuRollup.idl
+++ b/widget/public/nsIMenuRollup.idl
@@ -36,17 +36,20 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 #include "nsISupportsArray.idl"
 
 
-[uuid(05c48880-0fcf-11d4-bb6f-d9f289fe803c)]
+[uuid(10C69225-2C5A-4948-8690-0BC589D145B4)]
 interface nsIMenuRollup : nsISupports
 { 
   // Walks up the menu parent chain of a submenu pulling out the widgets and
   // places them into a list. Useful for determining if a click is in a
   // parent menu.
   nsISupportsArray GetSubmenuWidgetChain ( ) ;
-  
+
+  // adjust the position of any open popups when the position or size of a
+  // window has been changed.
+  void AdjustPopupsOnWindowChange ( );
 };
--- a/widget/src/cocoa/nsCocoaWindow.mm
+++ b/widget/src/cocoa/nsCocoaWindow.mm
@@ -542,16 +542,24 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool
       // other programs) that a menu has opened.  This is how the OS knows to
       // close other programs' context menus when ours open.
       if ([mWindow isKindOfClass:[PopupWindow class]] &&
           [(PopupWindow*) mWindow isContextMenu]) {
         [[NSDistributedNotificationCenter defaultCenter]
           postNotificationName:@"com.apple.HIToolbox.beginMenuTrackingNotification"
                         object:@"org.mozilla.gecko.PopupWindow"];
       }
+
+      // if a parent was supplied, set its child window. This will cause the
+      // child window to appear above the parent and move when the parent
+      // does. Setting this needs to happen after the _setWindowNumber calls
+      // above, otherwise the window doesn't focus properly.
+      if (nativeParentWindow)
+        [nativeParentWindow addChildWindow:mWindow
+                            ordered:NSWindowAbove];
     }
     else {
       mVisible = PR_TRUE;
       [mWindow setAcceptsMouseMovedEvents:YES];
       [mWindow makeKeyAndOrderFront:nil];
       SendSetZLevelEvent();
     }
   }
@@ -619,18 +627,25 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool
       // window will keep receiving mouse-moved events even after it's been
       // "ordered out" (instead of the browser window that was underneath it,
       // until you click on that window).  This is bmo bug 378645, but it's
       // surely an Apple bug.  The "window cache" is an undocumented subsystem,
       // all of whose methods are included in the NSWindowCache category of
       // the NSApplication class (in header files generated using class-dump).
       // This workaround was "borrowed" from the Java Embedding Plugin (which
       // uses it for a different purpose).
-      if (mWindowType == eWindowType_popup)
+      if (mWindowType == eWindowType_popup) {
+        // remove the window as a child of its parent again. This will just
+        // do nothing if the popup was never added as a child.
+        if (nativeParentWindow)
+          [nativeParentWindow removeChildWindow:mWindow];
+
         [NSApp _removeWindowFromCache:mWindow];
+      }
+
       // it's very important to turn off mouse moved events when hiding a window, otherwise
       // the windows' tracking rects will interfere with each other. (bug 356528)
       [mWindow setAcceptsMouseMovedEvents:NO];
       mVisible = PR_FALSE;
       // If our popup window is a non-native context menu, tell the OS (and
       // other programs) that a menu has closed.
       if ([mWindow isKindOfClass:[PopupWindow class]] &&
           [(PopupWindow*) mWindow isContextMenu]) {
@@ -691,27 +706,16 @@ NS_IMETHODIMP nsCocoaWindow::ConstrainPo
 }
 
 
 NS_IMETHODIMP nsCocoaWindow::Move(PRInt32 aX, PRInt32 aY)
 {
   if (!mWindow || (mBounds.x == aX && mBounds.y == aY))
     return NS_OK;
 
-  // if we're a popup, we have to convert from our parent widget's coord
-  // system to the global coord system first because the (x,y) we're given
-  // is in its coordinate system.
-  if (mParent && mWindowType == eWindowType_popup) {
-    nsRect globalRect;
-    nsRect localRect(aX, aY, 0, 0);
-    mParent->WidgetToScreen(localRect, globalRect);
-    aX = globalRect.x;
-    aY = globalRect.y;
-  }
-
   // The point we have is in Gecko coordinates (origin top-left). Convert
   // it to Cocoa ones (origin bottom-left).
   NSPoint coord = {aX, FlippedScreenY(aY)};
   [mWindow setFrameTopLeftPoint:coord];
 
   return NS_OK;
 }
 
--- a/widget/src/gtk2/nsWindow.cpp
+++ b/widget/src/gtk2/nsWindow.cpp
@@ -608,26 +608,17 @@ nsWindow::Move(PRInt32 aX, PRInt32 aY)
 
     mBounds.x = aX;
     mBounds.y = aY;
 
     if (!mCreated)
         return NS_OK;
 
     if (mIsTopLevel) {
-        if (mParent && mWindowType == eWindowType_popup) {
-            nsRect oldrect, newrect;
-            oldrect.x = aX;
-            oldrect.y = aY;
-            mParent->WidgetToScreen(oldrect, newrect);
-            gtk_window_move(GTK_WINDOW(mShell), newrect.x, newrect.y);
-        }
-        else {
-            gtk_window_move(GTK_WINDOW(mShell), aX, aY);
-        }
+        gtk_window_move(GTK_WINDOW(mShell), aX, aY);
     }
     else if (mDrawingarea) {
         moz_drawingarea_move(mDrawingarea, aX, aY);
     }
 
     return NS_OK;
 }
 
@@ -2837,18 +2828,16 @@ nsWindow::NativeCreate(nsIWidget        
     // only set the base parent if we're going to be a dialog or a
     // toplevel
     nsIWidget *baseParent = aInitData &&
         (aInitData->mWindowType == eWindowType_dialog ||
          aInitData->mWindowType == eWindowType_toplevel ||
          aInitData->mWindowType == eWindowType_invisible) ?
         nsnull : aParent;
 
-    NS_ASSERTION(aInitData->mWindowType != eWindowType_popup ||
-                 !aParent, "Popups should not be hooked into nsIWidget hierarchy");
     NS_ASSERTION(!mWindowGroup, "already have window group (leaking it)");
 
     // initialize all the common bits of this class
     BaseCreate(baseParent, aRect, aHandleEventFunction, aContext,
                aAppShell, aToolkit, aInitData);
 
     // Do we need to listen for resizes?
     PRBool listenForResizes = PR_FALSE;;
@@ -2977,18 +2966,26 @@ nsWindow::NativeCreate(nsIWidget        
                     mWindowGroup = parentnsWindow->mWindowGroup;
                     g_object_ref(G_OBJECT(mWindowGroup));
                     LOG(("adding window %p to group %p\n",
                          (void *)mShell, (void *)mWindowGroup));
                 }
             }
         }
         else if (mWindowType == eWindowType_popup) {
-            mShell = gtk_window_new(GTK_WINDOW_POPUP);
-            gtk_window_set_wmclass(GTK_WINDOW(mShell), "Popup", cBrand.get());
+            // treat popups with a parent as top level windows
+            if (mParent) {
+                mShell = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+                gtk_window_set_wmclass(GTK_WINDOW(mShell), "Toplevel", cBrand.get());
+                gtk_window_set_decorated(GTK_WINDOW(mShell), FALSE);
+            }
+            else {
+                mShell = gtk_window_new(GTK_WINDOW_POPUP);
+                gtk_window_set_wmclass(GTK_WINDOW(mShell), "Popup", cBrand.get());
+            }
 
             if (topLevelParent) {
                 gtk_window_set_transient_for(GTK_WINDOW(mShell),
                                             topLevelParent);
                 mTransientParent = topLevelParent;
 
                 if (topLevelParent->group) {
                     gtk_window_group_add_window(topLevelParent->group,
@@ -3292,36 +3289,25 @@ nsWindow::NativeResize(PRInt32 aX, PRInt
     mNeedsMove = PR_FALSE;
 
     LOG(("nsWindow::NativeResize [%p] %d %d %d %d\n", (void *)this,
          aX, aY, aWidth, aHeight));
 
     ResizeTransparencyBitmap(aWidth, aHeight);
 
     if (mIsTopLevel) {
-        if (mParent && mWindowType == eWindowType_popup) {
-            nsRect oldrect, newrect;
-            oldrect.x = aX;
-            oldrect.y = aY;
-            mParent->WidgetToScreen(oldrect, newrect);
-            moz_drawingarea_resize(mDrawingarea, aWidth, aHeight);
-            gtk_window_move(GTK_WINDOW(mShell), newrect.x, newrect.y);
-            gtk_window_resize(GTK_WINDOW(mShell), aWidth, aHeight);
-        }
-        else {
-            // We only move the toplevel window if someone has
-            // actually placed the window somewhere.  If no placement
-            // has taken place, we just let the window manager Do The
-            // Right Thing.
-            if (mPlaced)
-                gtk_window_move(GTK_WINDOW(mShell), aX, aY);
-
-            gtk_window_resize(GTK_WINDOW(mShell), aWidth, aHeight);
-            moz_drawingarea_resize(mDrawingarea, aWidth, aHeight);
-        }
+        // We only move the toplevel window if someone has
+        // actually placed the window somewhere.  If no placement
+        // has taken place, we just let the window manager Do The
+        // Right Thing.
+        if (mPlaced)
+            gtk_window_move(GTK_WINDOW(mShell), aX, aY);
+
+        gtk_window_resize(GTK_WINDOW(mShell), aWidth, aHeight);
+        moz_drawingarea_resize(mDrawingarea, aWidth, aHeight);
     }
     else if (mContainer) {
         GtkAllocation allocation;
         allocation.x = 0;
         allocation.y = 0;
         allocation.width = aWidth;
         allocation.height = aHeight;
         gtk_widget_size_allocate(GTK_WIDGET(mContainer), &allocation);
--- a/widget/src/windows/nsWindow.cpp
+++ b/widget/src/windows/nsWindow.cpp
@@ -1361,19 +1361,20 @@ nsWindow::StandardWindowCreate(nsIWidget
   }
 
   mContentType = aInitData ? aInitData->mContentType : eContentTypeInherit;
 
   DWORD style = WindowStyle();
   DWORD extendedStyle = WindowExStyle();
 
   if (mWindowType == eWindowType_popup) {
-    NS_ASSERTION(!aParent, "Popups should not be hooked into the nsIWidget hierarchy");
-    // Don't set the parent of a popup window.
-    parent = NULL;
+    // if a parent was specified, don't use WS_EX_TOPMOST so that the popup
+    // only appears above the parent, instead of all windows
+    if (aParent)
+      extendedStyle = WS_EX_TOOLWINDOW;
   } else if (nsnull != aInitData) {
     // See if the caller wants to explictly set clip children and clip siblings
     if (aInitData->clipChildren) {
       style |= WS_CLIPCHILDREN;
     } else {
       style &= ~WS_CLIPCHILDREN;
     }
     if (aInitData->clipSiblings) {
@@ -1745,17 +1746,18 @@ NS_METHOD nsWindow::Show(PRBool bState)
 #ifndef WINCE
           // ensure popups are the topmost of the TOPMOST
           // layer. Remember not to set the SWP_NOZORDER
           // flag as that might allow the taskbar to overlap
           // the popup.  However on windows ce, we need to
           // activate the popup or clicks will not be sent.
           flags |= SWP_NOACTIVATE;
 #endif
-          ::SetWindowPos(mWnd, HWND_TOPMOST, 0, 0, 0, 0, flags);
+          HWND owner = ::GetWindow(mWnd, GW_OWNER);
+          ::SetWindowPos(mWnd, owner ? 0 : HWND_TOPMOST, 0, 0, 0, 0, flags);
         } else {
           ::SetWindowPos(mWnd, HWND_TOP, 0, 0, 0, 0, flags);
         }
       }
     } else {
       if (mWindowType != eWindowType_dialog) {
         if (!sIsInEndSession)
           ::ShowWindow(mWnd, SW_HIDE);
--- a/xpfe/appshell/src/nsWebShellWindow.cpp
+++ b/xpfe/appshell/src/nsWebShellWindow.cpp
@@ -70,16 +70,17 @@
 #include "nsIAppShell.h"
 
 #include "nsIAppShellService.h"
 
 #include "nsIDOMCharacterData.h"
 #include "nsIDOMNodeList.h"
 
 #include "nsITimer.h"
+#include "nsXULPopupManager.h"
 
 #include "prmem.h"
 #include "prlock.h"
 
 #include "nsIDOMXULDocument.h"
 
 #include "nsIFocusController.h"
 
@@ -301,22 +302,40 @@ nsWebShellWindow::HandleEvent(nsGUIEvent
 
   if (docShell) {
     switch(aEvent->message) {
       /*
        * For size events, the DocShell must be resized to fill the entire
        * client area of the window...
        */
       case NS_MOVE: {
+#ifndef XP_MACOSX
+        // Move any popups that are attached to their parents. That is, the
+        // popup moves along with the parent window when it moves. This
+        // doesn't need to happen on Mac, as Cocoa provides a nice API
+        // which does this for us.
+        nsCOMPtr<nsIMenuRollup> pm =
+          do_GetService("@mozilla.org/xul/xul-popup-manager;1");
+        if (pm)
+          pm->AdjustPopupsOnWindowChange();
+#endif
+
         // persist position, but not immediately, in case this OS is firing
         // repeated move events as the user drags the window
         eventWindow->SetPersistenceTimer(PAD_POSITION);
         break;
       }
       case NS_SIZE: {
+#ifndef XP_MACOSX
+        nsCOMPtr<nsIMenuRollup> pm =
+          do_GetService("@mozilla.org/xul/xul-popup-manager;1");
+        if (pm)
+          pm->AdjustPopupsOnWindowChange();
+#endif
+ 
         nsSizeEvent* sizeEvent = (nsSizeEvent*)aEvent;
         nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(docShell));
         shellAsWin->SetPositionAndSize(0, 0, sizeEvent->windowSize->width, 
           sizeEvent->windowSize->height, PR_FALSE);  
         // persist size, but not immediately, in case this OS is firing
         // repeated size events as the user drags the sizing handle
         if (!eventWindow->IsLocked())
           eventWindow->SetPersistenceTimer(PAD_SIZE | PAD_MISC);