Bug 374570, constrain popups to frame, r+sr=roc
authorenndeakin@sympatico.ca
Fri, 20 Apr 2007 11:20:03 -0700
changeset 677 00c8a925b0b8c54fec2e662bb2825df51a3db455
parent 676 b92b640de096fbc3fa26ef3956906d46ef329189
child 678 1691d10c93f5559130a5c0aa312148de4d38e09e
push idunknown
push userunknown
push dateunknown
bugs374570
milestone1.9a4pre
Bug 374570, constrain popups to frame, r+sr=roc
dom/src/base/nsGlobalWindow.cpp
layout/base/nsDocumentViewer.cpp
layout/base/nsIPresShell.h
layout/base/nsPresShell.cpp
layout/xul/base/src/nsMenuPopupFrame.cpp
layout/xul/base/src/nsMenuPopupFrame.h
--- a/dom/src/base/nsGlobalWindow.cpp
+++ b/dom/src/base/nsGlobalWindow.cpp
@@ -3022,16 +3022,24 @@ nsGlobalWindow::SetScreenY(PRInt32 aScre
                     NS_ERROR_FAILURE);
 
   return NS_OK;
 }
 
 nsresult
 nsGlobalWindow::CheckSecurityWidthAndHeight(PRInt32* aWidth, PRInt32* aHeight)
 {
+  if (!nsContentUtils::IsCallerTrustedForWrite()) {
+    // if attempting to resize the window, hide any open popups
+    nsCOMPtr<nsIPresShell> presShell;
+    mDocShell->GetPresShell(getter_AddRefs(presShell));
+    if (presShell)
+      presShell->HidePopups();
+  }
+
   // This one is easy. Just ensure the variable is greater than 100;
   if ((aWidth && *aWidth < 100) || (aHeight && *aHeight < 100)) {
     // Check security state for use in determing window dimensions
 
     if (!nsContentUtils::IsCallerTrustedForWrite()) {
       //sec check failed
       if (aWidth && *aWidth < 100) {
         *aWidth = 100;
@@ -3048,16 +3056,22 @@ nsGlobalWindow::CheckSecurityWidthAndHei
 nsresult
 nsGlobalWindow::CheckSecurityLeftAndTop(PRInt32* aLeft, PRInt32* aTop)
 {
   // This one is harder. We have to get the screen size and window dimensions.
 
   // Check security state for use in determing window dimensions
 
   if (!nsContentUtils::IsCallerTrustedForWrite()) {
+    // if attempting to move the window, hide any open popups
+    nsCOMPtr<nsIPresShell> presShell;
+    mDocShell->GetPresShell(getter_AddRefs(presShell));
+    if (presShell)
+      presShell->HidePopups();
+
     PRInt32 screenLeft, screenTop, screenWidth, screenHeight;
     PRInt32 winLeft, winTop, winWidth, winHeight;
 
     nsGlobalWindow* rootWindow =
       NS_STATIC_CAST(nsGlobalWindow*, GetPrivateRoot());
     if (rootWindow) {
       rootWindow->FlushPendingNotifications(Flush_Layout);
     }
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -360,18 +360,16 @@ private:
                         PRBool aInPrintPreview,
                         PRBool aNeedMakeCX = PR_TRUE);
   nsresult InitPresentationStuff(PRBool aDoInitialReflow);
 
   nsresult GetPopupNode(nsIDOMNode** aNode);
   nsresult GetPopupLinkNode(nsIDOMNode** aNode);
   nsresult GetPopupImageNode(nsIImageLoadingContent** aNode);
 
-  void HideViewIfPopup(nsIView* aView);
-
   void DumpContentToPPM(const char* aFileName);
 
   void PrepareToStartLoad(void);
 
   nsresult SyncParentSubDocMap();
 
   nsresult GetDocumentSelection(nsISelection **aSelection);
 
@@ -1256,50 +1254,20 @@ DocumentViewerImpl::PageHide(PRBool aIsU
 
     // Never permit popups from the unload handler, no matter how we get
     // here.
     nsAutoPopupStatePusher popupStatePusher(openAbused, PR_TRUE);
 
     nsEventDispatcher::Dispatch(window, mPresContext, &event, nsnull, &status);
   }
 
-  if (mPresShell) {
-    // look for open menupopups and close them after the unload event, in case
-    // the unload event listeners open any new popups
-    nsIViewManager *vm = mPresShell->GetViewManager();
-    if (vm) {
-      nsIView *rootView = nsnull;
-      vm->GetRootView(rootView);
-      if (rootView)
-        HideViewIfPopup(rootView);
-    }
-  }
-
-  return NS_OK;
-}
-
-void
-DocumentViewerImpl::HideViewIfPopup(nsIView* aView)
-{
-  nsIFrame* frame = NS_STATIC_CAST(nsIFrame*, aView->GetClientData());
-  if (frame) {
-    nsIMenuParent* parent;
-    CallQueryInterface(frame, &parent);
-    if (parent) {
-      parent->HideChain();
-      // really make sure the view is hidden
-      mViewManager->SetViewVisibility(aView, nsViewVisibility_kHide);
-    }
-  }
-
-  nsIView* child = aView->GetFirstChild();
-  while (child) {
-    HideViewIfPopup(child);
-    child = child->GetNextSibling();
-  }
+  // look for open menupopups and close them after the unload event, in case
+  // the unload event listeners open any new popups
+  if (mPresShell)
+    mPresShell->HidePopups();
 }
 
 static void
 AttachContainerRecurse(nsIDocShell* aShell)
 {
   nsCOMPtr<nsIContentViewer> viewer;
   aShell->GetContentViewer(getter_AddRefs(viewer));
   nsCOMPtr<nsIDocumentViewer> docViewer = do_QueryInterface(viewer);
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -95,20 +95,20 @@ class nsCSSFrameConstructor;
 class nsISelection;
 template<class E> class nsCOMArray;
 class nsWeakFrame;
 class nsIScrollableFrame;
 class gfxASurface;
 
 typedef short SelectionType;
 
-// A68CFCE1-2A15-47F0-B61B-F2C34079A909
+// DC543B71-6F1A-4B9F-B4CF-693AEC4BA24A
 #define NS_IPRESSHELL_IID \
-{ 0xa78cfce1, 0x2a15, 0x47f0, \
-  { 0xb6, 0x1b, 0xf2, 0xc3, 0x40, 0x79, 0xa9, 0x09 } }
+{ 0xdc543b71, 0x6f1a, 0x4b9f, \
+  { 0xb4, 0xcf, 0x69, 0x3a, 0xec, 0x4b, 0xa2, 0x4a } }
 
 // Constants for ScrollContentIntoView() function
 #define NS_PRESSHELL_SCROLL_TOP      0
 #define NS_PRESSHELL_SCROLL_BOTTOM   100
 #define NS_PRESSHELL_SCROLL_LEFT     0
 #define NS_PRESSHELL_SCROLL_RIGHT    100
 #define NS_PRESSHELL_SCROLL_CENTER   50
 #define NS_PRESSHELL_SCROLL_ANYWHERE -1
@@ -710,16 +710,18 @@ public:
    * such that the mouse is over the same point in the scaled image as in
    * the original. When scaling does not occur, the mouse point isn't used
    * as the position can be determined from the displayed frames.
    */
   virtual already_AddRefed<gfxASurface> RenderSelection(nsISelection* aSelection,
                                                         nsPoint& aPoint,
                                                         nsRect* aScreenRect) = 0;
 
+  virtual void HidePopups() = 0;
+
   void AddWeakFrame(nsWeakFrame* aWeakFrame);
   void RemoveWeakFrame(nsWeakFrame* aWeakFrame);
 
 #ifdef NS_DEBUG
   nsIFrame* GetDrawEventTargetFrame() { return mDrawEventTargetFrame; }
 #endif
 
 protected:
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -189,16 +189,17 @@
 
 // For style data reconstruction
 #include "nsStyleChangeList.h"
 #include "nsCSSFrameConstructor.h"
 #ifdef MOZ_XUL
 #include "nsIMenuFrame.h"
 #include "nsITreeBoxObject.h"
 #endif
+#include "nsIMenuParent.h"
 #include "nsPlaceholderFrame.h"
 
 // Content viewer interfaces
 #include "nsIContentViewer.h"
 
 #include "nsContentCID.h"
 static NS_DEFINE_CID(kCSSStyleSheetCID, NS_CSS_STYLESHEET_CID);
 static NS_DEFINE_IID(kRangeCID,     NS_RANGE_CID);
@@ -928,16 +929,18 @@ public:
                                                    nsIRegion* aRegion,
                                                    nsPoint& aPoint,
                                                    nsRect* aScreenRect);
 
   virtual already_AddRefed<gfxASurface> RenderSelection(nsISelection* aSelection,
                                                         nsPoint& aPoint,
                                                         nsRect* aScreenRect);
 
+  virtual void HidePopups();
+
   //nsIViewObserver interface
 
   NS_IMETHOD Paint(nsIView *aView,
                    nsIRenderingContext* aRenderingContext,
                    const nsRegion& aDirtyRegion);
   NS_IMETHOD ComputeRepaintRegionForCopy(nsIView*      aRootView,
                                          nsIView*      aMovingView,
                                          nsPoint       aDelta,
@@ -1145,16 +1148,19 @@ protected:
   /**
    * Methods to handle changes to user and UA sheet lists that we get
    * notified about.
    */
   void AddUserSheet(nsISupports* aSheet);
   void AddAgentSheet(nsISupports* aSheet);
   void RemoveSheet(nsStyleSet::sheetType aType, nsISupports* aSheet);
 
+  // Hide a view if it is a popup
+  void HideViewIfPopup(nsIView* aView);
+
   nsICSSStyleSheet*         mPrefStyleSheet; // mStyleSet owns it but we maintain a ref, may be null
 #ifdef DEBUG
   PRUint32                  mUpdateCount;
 #endif
   // reflow roots that need to be reflowed, as both a queue and a hashtable
   nsVoidArray mDirtyRoots;
 
   PRPackedBool mDocumentLoading;
@@ -6116,16 +6122,28 @@ PresShell::Thaw()
   }
 
   if (mDocument)
     mDocument->EnumerateSubDocuments(ThawSubDocument, nsnull);
 
   UnsuppressPainting();
 }
 
+void
+PresShell::HidePopups()
+{
+  nsIViewManager *vm = GetViewManager();
+  if (vm) {
+    nsIView *rootView = nsnull;
+    vm->GetRootView(rootView);
+    if (rootView)
+      HideViewIfPopup(rootView);
+  }
+}
+
 //--------------------------------------------------------
 // Start of protected and private methods on the PresShell
 //--------------------------------------------------------
 
 //-------------- Begin Reflow Event Definition ------------------------
 
 NS_IMETHODIMP
 PresShell::ReflowEvent::Run() {    
@@ -6584,16 +6602,37 @@ PresShell::EnumeratePlugins(nsIDOMDocume
     nodes->Item(i, getter_AddRefs(node));
 
     nsCOMPtr<nsIContent> content = do_QueryInterface(node);
     if (content)
       aCallback(this, content);
   }
 }
 
+void
+PresShell::HideViewIfPopup(nsIView* aView)
+{
+  nsIFrame* frame = NS_STATIC_CAST(nsIFrame*, aView->GetClientData());
+  if (frame) {
+    nsIMenuParent* parent;
+    CallQueryInterface(frame, &parent);
+    if (parent) {
+      parent->HideChain();
+      // really make sure the view is hidden
+      mViewManager->SetViewVisibility(aView, nsViewVisibility_kHide);
+    }
+  }
+
+  nsIView* child = aView->GetFirstChild();
+  while (child) {
+    HideViewIfPopup(child);
+    child = child->GetNextSibling();
+  }
+}
+
 //------------------------------------------------------
 // End of protected and private methods on the PresShell
 //------------------------------------------------------
 
 // Start of DEBUG only code
 
 #ifdef NS_DEBUG
 #include "nsViewsCID.h"
--- a/layout/xul/base/src/nsMenuPopupFrame.cpp
+++ b/layout/xul/base/src/nsMenuPopupFrame.cpp
@@ -137,17 +137,18 @@ NS_INTERFACE_MAP_END_INHERITING(nsBoxFra
 nsMenuPopupFrame::nsMenuPopupFrame(nsIPresShell* aShell, nsStyleContext* aContext)
   :nsBoxFrame(aShell, aContext),
   mCurrentMenu(nsnull),
   mTimerMenu(nsnull),
   mCloseTimer(nsnull),
   mMenuCanOverlapOSBar(PR_FALSE),
   mShouldAutoPosition(PR_TRUE),
   mShouldRollup(PR_TRUE),
-  mConsumeRollupEvent(nsIPopupBoxObject::ROLLUP_DEFAULT)
+  mConsumeRollupEvent(nsIPopupBoxObject::ROLLUP_DEFAULT),
+  mInContentShell(PR_TRUE)
 {
   SetIsContextMenu(PR_FALSE);   // we're not a context menu by default
 } // ctor
 
 
 NS_IMETHODIMP
 nsMenuPopupFrame::Init(nsIContent*      aContent,
                        nsIFrame*        aParent,
@@ -186,16 +187,23 @@ nsMenuPopupFrame::Init(nsIContent*      
   viewManager->SetViewZIndex(ourView, PR_FALSE, kMaxZ);
   viewManager->InsertChild(rootView, ourView, nsnull, PR_TRUE);
 
   // XXX Hack. The menu's view should float above all other views,
   // so we use the nsIView::SetFloating() to tell the view manager
   // about that constraint.
   viewManager->SetViewFloating(ourView, PR_TRUE);
 
+  nsCOMPtr<nsISupports> cont = PresContext()->GetContainer();
+  nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(cont);
+  PRInt32 type = -1;
+  if (dsti && NS_SUCCEEDED(dsti->GetItemType(&type)) &&
+      type == nsIDocShellTreeItem::typeChrome)
+    mInContentShell = PR_FALSE;
+
   // XXX make sure we are hidden (shouldn't this be done automatically?)
   viewManager->SetViewVisibility(ourView, nsViewVisibility_kHide);
   if (!ourView->HasWidget()) {
     CreateWidgetForView(ourView);
   }
 
   MoveToAttributePosition();
 
@@ -212,25 +220,17 @@ nsMenuPopupFrame::CreateWidgetForView(ns
   widgetData.clipSiblings = PR_TRUE;
 
   PRBool isCanvas;
   const nsStyleBackground* bg;
   PRBool hasBG =
     nsCSSRendering::FindBackground(PresContext(), this, &bg, &isCanvas);
   PRBool viewHasTransparentContent = hasBG &&
     (bg->mBackgroundFlags & NS_STYLE_BG_COLOR_TRANSPARENT) &&
-    !GetStyleDisplay()->mAppearance;
-  if (viewHasTransparentContent) {
-    nsCOMPtr<nsISupports> cont = PresContext()->GetContainer();
-    nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(cont);
-    PRInt32 type = -1;
-    if (!dsti || NS_FAILED(dsti->GetItemType(&type)) ||
-        type != nsIDocShellTreeItem::typeChrome)
-      viewHasTransparentContent = PR_FALSE;
-  }
+    !GetStyleDisplay()->mAppearance && !mInContentShell;
 
   nsIContent* parentContent = GetContent()->GetParent();
   nsIAtom *tag = nsnull;
   if (parentContent)
     tag = parentContent->Tag();
   widgetData.mDropShadow = !(viewHasTransparentContent || tag == nsGkAtoms::menulist);
   
 #if defined(XP_MACOSX) || defined(XP_BEOS)
@@ -652,17 +652,17 @@ nsMenuPopupFrame::SyncViewWithFrame(nsPr
                                     const nsString& aPopupAnchor,
                                     const nsString& aPopupAlign,
                                     nsIFrame* aFrame, 
                                     PRInt32 aXPos, PRInt32 aYPos)
 {
   NS_ENSURE_ARG(aPresContext);
   NS_ENSURE_ARG(aFrame);
 
-  if (!mShouldAutoPosition) 
+  if (!mShouldAutoPosition && !mInContentShell) 
     return NS_OK;
 
   // |containingView|
   //   The view that contains the frame that is invoking this popup. This is 
   //   the canvas view inside the scrollport view. It can have negative bounds
   //   if the canvas is scrolled so that part is off screen.
   nsIView* containingView = nsnull;
   nsPoint offset;
@@ -751,16 +751,23 @@ nsMenuPopupFrame::SyncViewWithFrame(nsPr
   else {
     devContext->GetClientRect(rect);
   }
 
   // keep 3px margin to the right and bottom of the screen for WinXP dropshadow
   rect.width  -= nsPresContext::CSSPixelsToAppUnits(3);
   rect.height -= nsPresContext::CSSPixelsToAppUnits(3);
 
+  // for content shells, clip to the client area rather than the screen area
+  if (mInContentShell) {
+    nsRect rootScreenRect = presShell->GetRootFrame()->GetScreenRect();
+    rootScreenRect.ScaleRoundIn(aPresContext->AppUnitsPerDevPixel());
+    rect.IntersectRect(rect, rootScreenRect);
+  }
+
   PRInt32 screenLeftTwips   = rect.x;
   PRInt32 screenTopTwips    = rect.y;
   PRInt32 screenWidthTwips  = rect.width;
   PRInt32 screenHeightTwips = rect.height;
   PRInt32 screenRightTwips  = rect.XMost();
   PRInt32 screenBottomTwips = rect.YMost();
   
   // Recall that |xpos| and |ypos| are in the coordinate system of the parent view. In
@@ -2023,16 +2030,20 @@ nsMenuPopupFrame::MoveTo(PRInt32 aLeft, 
   }
 
   MoveToInternal(aLeft, aTop);
 }
 
 void
 nsMenuPopupFrame::MoveToInternal(PRInt32 aLeft, PRInt32 aTop)
 {
+  // just don't support moving popups for content shells
+  if (mInContentShell)
+    return;
+
   nsIView* view = GetView();
   NS_ASSERTION(view->GetParent(), "Must have parent!");
   
   // Retrieve screen position of parent view
   nsIntPoint screenPos = view->GetParent()->GetScreenPosition();
 
   // Move the widget
   // XXXbz don't we want screenPos to be the parent _widget_'s position, then?
--- a/layout/xul/base/src/nsMenuPopupFrame.h
+++ b/layout/xul/base/src/nsMenuPopupFrame.h
@@ -252,14 +252,15 @@ protected:
 
   PRPackedBool mIsContextMenu;  // is this a context menu?
   
   PRPackedBool mMenuCanOverlapOSBar;    // can we appear over the taskbar/menubar?
 
   PRPackedBool mShouldAutoPosition; // Should SyncViewWithFrame be allowed to auto position popup?
   PRPackedBool mShouldRollup; // Should this menupopup be allowed to dismiss automatically?
   PRPackedBool mConsumeRollupEvent; // Should the rollup event be consumed?
+  PRPackedBool mInContentShell; // True if the popup is in a content shell
 
   nsString     mIncrementalString;  // for incremental typing navigation
 
 }; // class nsMenuPopupFrame
 
 #endif