Bug 575294. part=4/5 r=roc
authorMats Palmgren <matspal@gmail.com>
Sat, 23 Jun 2012 03:13:56 +0200
changeset 97477 b47364c051d26f83e5267dd43403b13e6deb38ed
parent 97476 612ee5c6300d25d733a732e52fbd913a7ceb2063
child 97478 b2e5b06b0c2483fe98096182cf76f29be02f6575
push id22974
push userryanvm@gmail.com
push dateSat, 23 Jun 2012 12:36:09 +0000
treeherdermozilla-central@e21173ed2c38 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs575294
milestone16.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=4/5 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
@@ -443,34 +443,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)
@@ -704,16 +719,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) 
 {
@@ -829,18 +861,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
@@ -285,16 +285,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
@@ -833,16 +833,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)
@@ -857,16 +858,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)
@@ -889,16 +891,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
@@ -1058,16 +1058,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;
@@ -1122,16 +1124,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;
@@ -1185,16 +1189,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
@@ -1329,16 +1329,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");
@@ -1366,16 +1367,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");
@@ -1405,16 +1407,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 "nsIFile.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;
@@ -262,16 +263,23 @@ protected:
     nsCOMPtr<nsIWidget> widget = do_CreateInstance(kCPopUpCID);
     return widget.forget();
   }
 
   BasicLayerManager* CreateBasicLayerManager();
 
   nsPopupType PopupType() const { return mPopupType; }
 
+  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