Allow the float manager to record that a float has been pushed past a break. (Bug 563584, patch 9) r=roc
authorL. David Baron <dbaron@dbaron.org>
Thu, 05 Aug 2010 21:59:19 -0700
changeset 48985 96e0c7bad07dc0d9fd81d5f15796604545c0a13c
parent 48984 1244f0203b8747c3b9b9c43013a3c9fdce97a72e
child 48986 81b8361ed910c5240165b97a3941d54524fca2f5
push id14884
push userdbaron@mozilla.com
push dateFri, 06 Aug 2010 05:01:26 +0000
treeherderautoland@8ab7ef79b673 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs563584
milestone2.0b4pre
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
Allow the float manager to record that a float has been pushed past a break. (Bug 563584, patch 9) r=roc
layout/generic/nsFloatManager.cpp
layout/generic/nsFloatManager.h
layout/reftests/bugs/563584-10-ref.html
layout/reftests/bugs/563584-10a.html
layout/reftests/bugs/563584-10b.html
layout/reftests/bugs/reftest.list
--- a/layout/generic/nsFloatManager.cpp
+++ b/layout/generic/nsFloatManager.cpp
@@ -67,17 +67,19 @@ PSArenaFreeCB(size_t aSize, void* aPtr, 
   static_cast<nsIPresShell*>(aClosure)->FreeMisc(aSize, aPtr);
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // nsFloatManager
 
 nsFloatManager::nsFloatManager(nsIPresShell* aPresShell)
   : mX(0), mY(0),
-    mFloatDamage(PSArenaAllocCB, PSArenaFreeCB, aPresShell)
+    mFloatDamage(PSArenaAllocCB, PSArenaFreeCB, aPresShell),
+    mPushedLeftFloatPastBreak(PR_FALSE),
+    mPushedRightFloatPastBreak(PR_FALSE)
 {
   MOZ_COUNT_CTOR(nsFloatManager);
 }
 
 nsFloatManager::~nsFloatManager()
 {
   MOZ_COUNT_DTOR(nsFloatManager);
 }
@@ -410,35 +412,42 @@ nsFloatManager::PushState(SavedState* aS
   // since that could lead to bugs where damage is missed/dropped when
   // we move from position A to B (during the intermediate incremental
   // reflow mentioned above) and then from B to C during the subsequent
   // reflow. In the typical case A and C will be the same, but not always.
   // Allowing mFloatDamage to accumulate the damage incurred during both
   // reflows ensures that nothing gets missed.
   aState->mX = mX;
   aState->mY = mY;
+  aState->mPushedLeftFloatPastBreak = mPushedLeftFloatPastBreak;
+  aState->mPushedRightFloatPastBreak = mPushedRightFloatPastBreak;
   aState->mFloatInfoCount = mFloats.Length();
 }
 
 void
 nsFloatManager::PopState(SavedState* aState)
 {
   NS_PRECONDITION(aState, "No state to restore?");
 
   mX = aState->mX;
   mY = aState->mY;
+  mPushedLeftFloatPastBreak = aState->mPushedLeftFloatPastBreak;
+  mPushedRightFloatPastBreak = aState->mPushedRightFloatPastBreak;
 
   NS_ASSERTION(aState->mFloatInfoCount <= mFloats.Length(),
                "somebody misused PushState/PopState");
   mFloats.TruncateLength(aState->mFloatInfoCount);
 }
 
 nscoord
 nsFloatManager::GetLowestFloatTop() const
 {
+  if (mPushedLeftFloatPastBreak || mPushedRightFloatPastBreak) {
+    return nscoord_MAX;
+  }
   if (!HasAnyFloats()) {
     return nscoord_MIN;
   }
   return mFloats[mFloats.Length() - 1].mRect.y - mY;
 }
 
 #ifdef DEBUG
 void
@@ -462,16 +471,19 @@ nsFloatManager::List(FILE* out) const
   }
   return NS_OK;
 }
 #endif
 
 nscoord
 nsFloatManager::ClearFloats(nscoord aY, PRUint8 aBreakType) const
 {
+  if (ClearContinues(aBreakType)) {
+    return nscoord_MAX;
+  }
   if (!HasAnyFloats()) {
     return aY;
   }
 
   nscoord bottom = aY + mY;
 
   const FloatInfo &tail = mFloats[mFloats.Length() - 1];
   switch (aBreakType) {
@@ -493,18 +505,29 @@ nsFloatManager::ClearFloats(nscoord aY, 
   bottom -= mY;
 
   return bottom;
 }
 
 PRBool
 nsFloatManager::ClearContinues(PRUint8 aBreakType) const
 {
+  if ((mPushedLeftFloatPastBreak &&
+       (aBreakType == NS_STYLE_CLEAR_LEFT_AND_RIGHT ||
+        aBreakType == NS_STYLE_CLEAR_LEFT)) ||
+      (mPushedRightFloatPastBreak &&
+       (aBreakType == NS_STYLE_CLEAR_LEFT_AND_RIGHT ||
+        aBreakType == NS_STYLE_CLEAR_RIGHT))) {
+    return PR_TRUE;
+  }
   if (!HasAnyFloats() || aBreakType == NS_STYLE_CLEAR_NONE)
     return PR_FALSE;
+  // FIXME: We could make this faster by recording whenever we split a
+  // float (in addition to recording whenever we push a float in its
+  // entirety).
   for (PRUint32 i = mFloats.Length(); i > 0; i--) {
     nsIFrame* f = mFloats[i-1].mFrame;
     if (f->GetNextInFlow()) {
       if (aBreakType == NS_STYLE_CLEAR_LEFT_AND_RIGHT)
         return PR_TRUE;
       PRUint8 floatSide = f->GetStyleDisplay()->mFloats;
       if ((aBreakType == NS_STYLE_CLEAR_LEFT &&
            floatSide == NS_STYLE_FLOAT_LEFT) ||
--- a/layout/generic/nsFloatManager.h
+++ b/layout/generic/nsFloatManager.h
@@ -106,17 +106,19 @@ public:
   // Structure that stores the current state of a frame manager for
   // Save/Restore purposes.
   struct SavedState;
   friend struct SavedState;
   struct SavedState {
   private:
     PRUint32 mFloatInfoCount;
     nscoord mX, mY;
-    
+    PRPackedBool mPushedLeftFloatPastBreak;
+    PRPackedBool mPushedRightFloatPastBreak;
+
     friend class nsFloatManager;
   };
 
   /**
    * Translate the current origin by the specified (dx, dy). This
    * creates a new local coordinate space relative to the current
    * coordinate space.
    */
@@ -177,16 +179,27 @@ public:
    * must be even with or below the top of all previous floats.
    *
    * aMarginRect is relative to the current translation.  The caller
    * must ensure aMarginRect.height >= 0 and aMarginRect.width >= 0.
    */
   nsresult AddFloat(nsIFrame* aFloatFrame, const nsRect& aMarginRect);
 
   /**
+   * Notify that we tried to place a float that could not fit at all and
+   * had to be pushed to the next page/column?  (If so, we can't place
+   * any more floats in this page/column because of the rule that the
+   * top of a float cannot be above the top of an earlier float.)
+   */
+  void SetPushedLeftFloatPastBreak()
+    { mPushedLeftFloatPastBreak = PR_TRUE; }
+  void SetPushedRightFloatPastBreak()
+    { mPushedRightFloatPastBreak = PR_TRUE; }
+
+  /**
    * Remove the regions associated with this floating frame and its
    * next-sibling list.  Some of the frames may never have been added;
    * we just skip those. This is not fully general; it only works as
    * long as the N frames to be removed are the last N frames to have
    * been added; if there's a frame in the middle of them that should
    * not be removed, YOU LOSE.
    */
   nsresult RemoveTrailingRegions(nsIFrame* aFrameList);
@@ -254,16 +267,20 @@ public:
    * Checks if clear would pass into the floats' BFC's next-in-flow,
    * i.e. whether floats affecting this clear have continuations.
    */
   PRBool ClearContinues(PRUint8 aBreakType) const;
 
   void AssertStateMatches(SavedState *aState) const
   {
     NS_ASSERTION(aState->mX == mX && aState->mY == mY &&
+                 aState->mPushedLeftFloatPastBreak ==
+                   mPushedLeftFloatPastBreak &&
+                 aState->mPushedRightFloatPastBreak ==
+                   mPushedRightFloatPastBreak &&
                  aState->mFloatInfoCount == mFloats.Length(),
                  "float manager state should match saved state");
   }
 
 #ifdef DEBUG
   /**
    * Dump the state of the float manager out to a file.
    */
@@ -284,16 +301,25 @@ private:
     ~FloatInfo();
 #endif
   };
 
   nscoord         mX, mY;     // translation from local to global coordinate space
   nsTArray<FloatInfo> mFloats;
   nsIntervalSet   mFloatDamage;
 
+  // Did we try to place a float that could not fit at all and had to be
+  // pushed to the next page/column?  If so, we can't place any more
+  // floats in this page/column because of the rule that the top of a
+  // float cannot be above the top of an earlier float.  And we also
+  // need to apply this information to 'clear', and thus need to
+  // separate left and right floats.
+  PRPackedBool mPushedLeftFloatPastBreak;
+  PRPackedBool mPushedRightFloatPastBreak;
+
   static PRInt32 sCachedFloatManagerCount;
   static void* sCachedFloatManagers[NS_FLOAT_MANAGER_CACHE_SIZE];
 
   nsFloatManager(const nsFloatManager&);  // no implementation
   void operator=(const nsFloatManager&);  // no implementation
 };
 
 /**
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/563584-10-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<html class="reftest-print">
+<title>Test that nsFloatManager::ClearFloats calls ClearContinues</title>
+<body style="margin: 0">
+<div style="background: aqua; height: 1in; width: 1in"></div>
+<div style="page-break-before: always; background: fuchsia; height: 1.5in; width: 1in"></div>
+<div style="height: 0.25in; background: yellow; width: 1in"></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/563584-10a.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html class="reftest-print">
+<title>Test that nsFloatManager::ClearFloats calls ClearContinues</title>
+<body style="margin: 0">
+<div style="float: left;">
+  <div>
+    <div style="display:inline-block; vertical-align: top; background: aqua; height: 1in; width: 1in"></div>
+  </div>
+  <div>
+    <div style="display:inline-block; vertical-align: top; background: fuchsia; height: 1.5in; width: 1in"></div>
+  </div>
+</div>
+<div style="float: left; clear: left; height: 0.25in; background: yellow; width: 1in"></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/563584-10b.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html class="reftest-print">
+<title>Test that nsFloatManager::ClearFloats calls ClearContinues</title>
+<body style="margin: 0">
+<div style="float: left;">
+  <div>
+    <div style="display:inline-block; vertical-align: top; background: aqua; height: 1in; width: 1in"></div>
+  </div>
+  <div>
+    <div style="display:inline-block; vertical-align: top; background: fuchsia; height: 1.5in; width: 1in"></div>
+  </div>
+</div>
+<div style="clear: left; height: 0.25in; background: yellow; width: 1in"></div>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1451,16 +1451,18 @@ random-if(!haveTestPlugin) == 546071-1.h
 == 563584-8a.html 563584-8a-ref.html
 == 563584-8b.html 563584-8b-ref.html
 == 563584-8c.html 563584-8c-ref.html
 == 563584-8d.html 563584-8d-ref.html
 == 563584-9a.html 563584-9a-ref.html
 == 563584-9b.html 563584-9b-ref.html
 == 563584-9c.html 563584-9cd-ref.html
 == 563584-9d.html 563584-9cd-ref.html
+== 563584-10a.html 563584-10-ref.html
+== 563584-10b.html 563584-10-ref.html
 == 564054-1.html 564054-1-ref.html
 == 564991-1.html 564991-1-ref.html
 == 565819-1.html 565819-ref.html
 == 565819-2.html 565819-ref.html
 == 569006-1.html 569006-1-ref.html
 == 571281-1a.html 571281-1-ref.html
 == 571281-1b.html 571281-1-ref.html
 == 571281-1c.html 571281-1-ref.html