Bug 575294. part=3/4 r=roc
authorMats Palmgren <matspal@gmail.com>
Tue, 29 May 2012 02:50:42 +0200
changeset 99642 8622c5680fb32ccb93cd879437255f58f640529a
parent 99641 205d2e0d297cea4c9487818a68473269651dd486
child 99643 deac5d31bc126203fcc50c9ff8f62baf4611cbe7
push id173
push userlsblakk@mozilla.com
push dateFri, 24 Aug 2012 15:39:16 +0000
treeherdermozilla-release@bcc45eb1fb41 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs575294
milestone15.0a1
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 575294. part=3/4 r=roc
layout/forms/nsComboboxControlFrame.cpp
layout/forms/nsComboboxControlFrame.h
layout/xul/base/public/nsXULPopupManager.h
widget/cocoa/nsChildView.mm
widget/gtk2/nsWindow.cpp
widget/nsIRollupListener.h
widget/os2/nsWindow.cpp
widget/qt/nsWindow.cpp
widget/windows/nsWindow.cpp
widget/xpwidgets/nsBaseWidget.h
--- a/layout/forms/nsComboboxControlFrame.cpp
+++ b/layout/forms/nsComboboxControlFrame.cpp
@@ -444,34 +444,49 @@ nsComboboxControlFrame::ShowList(bool aS
         }
       }
     }
   }
 
   return weakFrame.IsAlive();
 }
 
-class nsResizeDropdownAtFinalPosition : public nsIReflowCallback
+class nsResizeDropdownAtFinalPosition
+  : public nsIReflowCallback, public nsRunnable
 {
 public:
   nsResizeDropdownAtFinalPosition(nsComboboxControlFrame* aFrame)
-    : mFrame(aFrame) {}
+    : mFrame(aFrame)
+  {
+    MOZ_COUNT_CTOR(nsResizeDropdownAtFinalPosition);
+  }
+  ~nsResizeDropdownAtFinalPosition()
+  {
+    MOZ_COUNT_DTOR(nsResizeDropdownAtFinalPosition);
+  }
 
   virtual bool ReflowFinished()
   {
+    Run();
+    NS_RELEASE_THIS();
+    return false;
+  }
+
+  virtual void ReflowCallbackCanceled()
+  {
+    NS_RELEASE_THIS();
+  }
+
+  NS_IMETHODIMP Run()
+  {
     if (mFrame.IsAlive()) {
       static_cast<nsComboboxControlFrame*>(mFrame.GetFrame())->
         AbsolutelyPositionDropDown();
     }
-    return false;
-  }
-
-  virtual void ReflowCallbackCanceled()
-  {
-    delete this;
+    return NS_OK;
   }
 
   nsWeakFrame mFrame;
 };
 
 nsresult
 nsComboboxControlFrame::ReflowDropdown(nsPresContext*  aPresContext, 
                                        const nsHTMLReflowState& aReflowState)
@@ -705,16 +720,33 @@ nsComboboxControlFrame::AbsolutelyPositi
     // Align the right edge of the drop-down with the right edge of the control.
     dropdownPosition.x = GetRect().width - dropdownSize.width;
   }
   mDropdownFrame->SetPosition(dropdownPosition + translation);
   nsContainerFrame::PositionFrameView(mDropdownFrame);
   return eDropDownPositionFinal;
 }
 
+void
+nsComboboxControlFrame::NotifyGeometryChange()
+{
+  // We don't need to resize if we're not dropped down since ShowDropDown
+  // does that, or if we're dirty then the reflow callback does it,
+  // or if we have a delayed ShowDropDown pending.
+  if (IsDroppedDown() &&
+      !(GetStateBits() & NS_FRAME_IS_DIRTY) &&
+      !mDelayedShowDropDown) {
+    // Async because we're likely in a middle of a scroll here so
+    // frame/view positions are in flux.
+    nsRefPtr<nsResizeDropdownAtFinalPosition> resize =
+      new nsResizeDropdownAtFinalPosition(this);
+    NS_DispatchToCurrentThread(resize);
+  }
+}
+
 //----------------------------------------------------------
 // 
 //----------------------------------------------------------
 #ifdef DO_REFLOW_DEBUG
 static int myCounter = 0;
 
 static void printSize(char * aDesc, nscoord aSize) 
 {
@@ -830,18 +862,23 @@ nsComboboxControlFrame::Reflow(nsPresCon
     mListControlFrame->GetOptionText(selectedIndex, selectedOptionText);
   }
   if (mDisplayedOptionText != selectedOptionText) {
     RedisplayText(selectedIndex);
   }
 
   // First reflow our dropdown so that we know how tall we should be.
   ReflowDropdown(aPresContext, aReflowState);
-  nsIReflowCallback* cb = new nsResizeDropdownAtFinalPosition(this);
-  aPresContext->PresShell()->PostReflowCallback(cb);
+  nsRefPtr<nsResizeDropdownAtFinalPosition> resize =
+    new nsResizeDropdownAtFinalPosition(this);
+  if (NS_SUCCEEDED(aPresContext->PresShell()->PostReflowCallback(resize))) {
+    // The reflow callback queue doesn't AddRef so we keep it alive until
+    // it's released in its ReflowFinished / ReflowCallbackCanceled.
+    resize.forget();
+  }
 
   // Get the width of the vertical scrollbar.  That will be the width of the
   // dropdown button.
   nscoord buttonWidth;
   const nsStyleDisplay *disp = GetStyleDisplay();
   if (IsThemed(disp) && !aPresContext->GetTheme()->ThemeNeedsComboboxDropmarker()) {
     buttonWidth = 0;
   }
--- a/layout/forms/nsComboboxControlFrame.h
+++ b/layout/forms/nsComboboxControlFrame.h
@@ -163,16 +163,17 @@ public:
   NS_IMETHOD OnSetSelectedIndex(PRInt32 aOldIndex, PRInt32 aNewIndex);
 
   //nsIRollupListener
   /**
    * Hide the dropdown menu and stop capturing mouse events.
    * @note This method might destroy |this|.
    */
   virtual nsIContent* Rollup(PRUint32 aCount, bool aGetLastRolledUp = false);
+  virtual void NotifyGeometryChange();
 
   /**
    * A combobox should roll up if a mousewheel event happens outside of
    * the popup area.
    */
   virtual bool ShouldRollupOnMouseWheelEvent()
     { return true; }
 
--- a/layout/xul/base/public/nsXULPopupManager.h
+++ b/layout/xul/base/public/nsXULPopupManager.h
@@ -284,16 +284,17 @@ public:
   NS_DECL_NSITIMERCALLBACK
   NS_DECL_NSIDOMEVENTLISTENER
 
   // nsIRollupListener
   virtual nsIContent* Rollup(PRUint32 aCount, bool aGetLastRolledUp = false);
   virtual bool ShouldRollupOnMouseWheelEvent();
   virtual bool ShouldRollupOnMouseActivate();
   virtual PRUint32 GetSubmenuWidgetChain(nsTArray<nsIWidget*> *aWidgetChain);
+  virtual void NotifyGeometryChange() {}
 
   static nsXULPopupManager* sInstance;
 
   // initialize and shutdown methods called by nsLayoutStatics
   static nsresult Init();
   static void Shutdown();
 
   // returns a weak reference to the popup manager instance, could return null
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -830,16 +830,17 @@ NS_IMETHODIMP nsChildView::Move(PRInt32 
 
   NSRect r;
   nsCocoaUtils::GeckoRectToNSRect(mBounds, r);
   [mView setFrame:r];
 
   if (mVisible)
     [mView setNeedsDisplay:YES];
 
+  NotifyRollupGeometryChange(gRollupListener);
   ReportMoveEvent();
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 NS_IMETHODIMP nsChildView::Resize(PRInt32 aWidth, PRInt32 aHeight, bool aRepaint)
@@ -854,16 +855,17 @@ NS_IMETHODIMP nsChildView::Resize(PRInt3
 
   NSRect r;
   nsCocoaUtils::GeckoRectToNSRect(mBounds, r);
   [mView setFrame:r];
 
   if (mVisible && aRepaint)
     [mView setNeedsDisplay:YES];
 
+  NotifyRollupGeometryChange(gRollupListener);
   ReportSizeEvent();
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 NS_IMETHODIMP nsChildView::Resize(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight, bool aRepaint)
@@ -886,16 +888,17 @@ NS_IMETHODIMP nsChildView::Resize(PRInt3
 
   NSRect r;
   nsCocoaUtils::GeckoRectToNSRect(mBounds, r);
   [mView setFrame:r];
 
   if (mVisible && aRepaint)
     [mView setNeedsDisplay:YES];
 
+  NotifyRollupGeometryChange(gRollupListener);
   if (isMoving) {
     ReportMoveEvent();
     if (mOnDestroyCalled)
       return NS_OK;
   }
   if (isResizing)
     ReportSizeEvent();
 
--- a/widget/gtk2/nsWindow.cpp
+++ b/widget/gtk2/nsWindow.cpp
@@ -1059,16 +1059,18 @@ nsWindow::Resize(PRInt32 aWidth, PRInt32
             // dunno why, but apparently we're lame like that.
             NativeResize(aWidth, aHeight, aRepaint);
         }
         else {
             mNeedsResize = true;
         }
     }
 
+    NotifyRollupGeometryChange(gRollupListener);
+
     // synthesize a resize event if this isn't a toplevel
     if (mIsTopLevel || mListenForResizes) {
         nsIntRect rect(mBounds.x, mBounds.y, aWidth, aHeight);
         nsEventStatus status;
         DispatchResizeEvent(rect, status);
     }
 
     return NS_OK;
@@ -1123,16 +1125,18 @@ nsWindow::Resize(PRInt32 aX, PRInt32 aY,
             // dunno why, but apparently we're lame like that.
             NativeResize(aX, aY, aWidth, aHeight, aRepaint);
         }
         else {
             mNeedsResize = true;
         }
     }
 
+    NotifyRollupGeometryChange(gRollupListener);
+
     if (mIsTopLevel || mListenForResizes) {
         // synthesize a resize event
         nsIntRect rect(aX, aY, aWidth, aHeight);
         nsEventStatus status;
         DispatchResizeEvent(rect, status);
     }
 
     return NS_OK;
@@ -1186,16 +1190,17 @@ nsWindow::Move(PRInt32 aX, PRInt32 aY)
 
     if (mIsTopLevel) {
         gtk_window_move(GTK_WINDOW(mShell), aX, aY);
     }
     else if (mGdkWindow) {
         gdk_window_move(mGdkWindow, aX, aY);
     }
 
+    NotifyRollupGeometryChange(gRollupListener);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement  aPlacement,
                       nsIWidget                  *aWidget,
                       bool                        aActivate)
 {
--- a/widget/nsIRollupListener.h
+++ b/widget/nsIRollupListener.h
@@ -40,11 +40,16 @@ class nsIRollupListener {
    * Retrieve the widgets for open menus and store them in the array
    * aWidgetChain. The number of menus of the same type should be returned,
    * for example, if a context menu is open, return only the number of menus
    * that are part of the context menu chain. This allows closing up only
    * those menus in different situations. The returned value should be exactly
    * the same number of widgets added to aWidgetChain.
    */
   virtual PRUint32 GetSubmenuWidgetChain(nsTArray<nsIWidget*> *aWidgetChain) = 0;
+
+  /**
+   * Notify the RollupListener that the widget did a Move or Resize.
+   */
+  virtual void NotifyGeometryChange() = 0;
 };
 
 #endif /* __nsIRollupListener_h__ */
--- a/widget/os2/nsWindow.cpp
+++ b/widget/os2/nsWindow.cpp
@@ -761,40 +761,46 @@ void nsWindow::NS2PM_PARENT(POINTL& ptl)
   }
 }
 
 //-----------------------------------------------------------------------------
 
 NS_METHOD nsWindow::Move(PRInt32 aX, PRInt32 aY)
 {
   if (mFrame) {
-    return mFrame->Move(aX, aY);
+    nsresult rv = mFrame->Move(aX, aY);
+    NotifyRollupGeometryChange(gRollupListener);
+    return rv;
   }
   Resize(aX, aY, mBounds.width, mBounds.height, false);
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 
 NS_METHOD nsWindow::Resize(PRInt32 aWidth, PRInt32 aHeight, bool aRepaint)
 {
   if (mFrame) {
-    return mFrame->Resize(aWidth, aHeight, aRepaint);
+    nsresult rv = mFrame->Resize(aWidth, aHeight, aRepaint);
+    NotifyRollupGeometryChange(gRollupListener);
+    return rv;
   }
   Resize(mBounds.x, mBounds.y, aWidth, aHeight, aRepaint);
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 
 NS_METHOD nsWindow::Resize(PRInt32 aX, PRInt32 aY,
                            PRInt32 aWidth, PRInt32 aHeight, bool aRepaint)
 {
   if (mFrame) {
-    return mFrame->Resize(aX, aY, aWidth, aHeight, aRepaint);
+    nsresult rv = mFrame->Resize(aX, aY, aWidth, aHeight, aRepaint);
+    NotifyRollupGeometryChange(gRollupListener);
+    return rv;
   }
 
   // For mWnd & eWindowType_child set the cached values upfront, see bug 286555.
   // For other mWnd types we defer transfer of values to mBounds to
   // WinSetWindowPos(), see bug 391421.
 
   if (!mWnd ||
       mWindowType == eWindowType_child ||
@@ -822,16 +828,17 @@ NS_METHOD nsWindow::Resize(PRInt32 aX, P
     }
 
     if (!WinSetWindowPos(mWnd, 0, ptl.x, ptl.y, aWidth, aHeight,
                          SWP_MOVE | SWP_SIZE) && aRepaint) {
       WinInvalidateRect(mWnd, 0, FALSE);
     }
   }
 
+  NotifyRollupGeometryChange(gRollupListener);
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 
 NS_METHOD nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
                                 nsIWidget* aWidget, bool aActivate)
 {
--- a/widget/qt/nsWindow.cpp
+++ b/widget/qt/nsWindow.cpp
@@ -541,17 +541,17 @@ nsWindow::Move(PRInt32 aX, PRInt32 aY)
         pos = mWidget->mapFromScene(pos);
         pos = mWidget->mapToParent(pos);
         mWidget->setPos(pos);
     }
 
     mBounds.x = pos.x();
     mBounds.y = pos.y();
 
-
+    NotifyRollupGeometryChange(gRollupListener);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement  aPlacement,
                       nsIWidget                  *aWidget,
                       bool                        aActivate)
 {
@@ -3006,16 +3006,17 @@ nsWindow::Resize(PRInt32 aWidth, PRInt32
 
     // synthesize a resize event if this isn't a toplevel
     if (mIsTopLevel || mListenForResizes) {
         nsIntRect rect(mBounds.x, mBounds.y, aWidth, aHeight);
         nsEventStatus status;
         DispatchResizeEvent(rect, status);
     }
 
+    NotifyRollupGeometryChange(gRollupListener);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWindow::Resize(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight,
                  bool aRepaint)
 {
     mBounds.x = aX;
@@ -3069,16 +3070,17 @@ nsWindow::Resize(PRInt32 aX, PRInt32 aY,
         nsIntRect rect(aX, aY, aWidth, aHeight);
         nsEventStatus status;
         DispatchResizeEvent(rect, status);
     }
 
     if (aRepaint)
         mWidget->update();
 
+    NotifyRollupGeometryChange(gRollupListener);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWindow::Enable(bool aState)
 {
     mEnabled = aState;
 
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -1323,16 +1323,17 @@ NS_METHOD nsWindow::Move(PRInt32 aX, PRI
         mClipRects &&
         (mClipRectCount != 1 || !mClipRects[0].IsEqualInterior(nsIntRect(0, 0, mBounds.width, mBounds.height)))) {
       flags |= SWP_NOCOPYBITS;
     }
     VERIFY(::SetWindowPos(mWnd, NULL, aX, aY, 0, 0, flags));
 
     SetThemeRegion();
   }
+  NotifyRollupGeometryChange(sRollupListener);
   return NS_OK;
 }
 
 // Resize this component
 NS_METHOD nsWindow::Resize(PRInt32 aWidth, PRInt32 aHeight, bool aRepaint)
 {
   NS_ASSERTION((aWidth >=0 ) , "Negative width passed to nsWindow::Resize");
   NS_ASSERTION((aHeight >=0 ), "Negative height passed to nsWindow::Resize");
@@ -1360,16 +1361,17 @@ NS_METHOD nsWindow::Resize(PRInt32 aWidt
     ClearThemeRegion();
     VERIFY(::SetWindowPos(mWnd, NULL, 0, 0, aWidth, GetHeight(aHeight), flags));
     SetThemeRegion();
   }
 
   if (aRepaint)
     Invalidate();
 
+  NotifyRollupGeometryChange(sRollupListener);
   return NS_OK;
 }
 
 // Resize this component
 NS_METHOD nsWindow::Resize(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight, bool aRepaint)
 {
   NS_ASSERTION((aWidth >=0 ),  "Negative width passed to nsWindow::Resize");
   NS_ASSERTION((aHeight >=0 ), "Negative height passed to nsWindow::Resize");
@@ -1399,16 +1401,17 @@ NS_METHOD nsWindow::Resize(PRInt32 aX, P
     ClearThemeRegion();
     VERIFY(::SetWindowPos(mWnd, NULL, aX, aY, aWidth, GetHeight(aHeight), flags));
     SetThemeRegion();
   }
 
   if (aRepaint)
     Invalidate();
 
+  NotifyRollupGeometryChange(sRollupListener);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWindow::BeginResizeDrag(nsGUIEvent* aEvent, PRInt32 aHorizontal, PRInt32 aVertical)
 {
   NS_ENSURE_ARG_POINTER(aEvent);
 
--- a/widget/xpwidgets/nsBaseWidget.h
+++ b/widget/xpwidgets/nsBaseWidget.h
@@ -9,16 +9,17 @@
 #include "nsIWidget.h"
 #include "nsWidgetsCID.h"
 #include "nsILocalFile.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsGUIEvent.h"
 #include "nsAutoPtr.h"
 #include "BasicLayers.h"
+#include "nsIRollupListener.h"
 
 class nsIContent;
 class nsAutoRollup;
 class gfxContext;
 
 namespace mozilla {
 namespace layers {
 class CompositorChild;
@@ -260,16 +261,23 @@ protected:
   {
     static NS_DEFINE_IID(kCPopUpCID, NS_CHILD_CID);
     nsCOMPtr<nsIWidget> widget = do_CreateInstance(kCPopUpCID);
     return widget.forget();
   }
 
   BasicLayerManager* CreateBasicLayerManager();
 
+  void NotifyRollupGeometryChange(nsIRollupListener* aRollupListener)
+  {
+    if (aRollupListener) {
+      aRollupListener->NotifyGeometryChange();
+    }
+  }
+
 protected:
   /**
    * Starts the OMTC compositor destruction sequence.
    *
    * When this function returns, the compositor should not be 
    * able to access the opengl context anymore.
    * It is safe to call it several times if platform implementations
    * require the compositor to be destroyed before ~nsBaseWidget is