Bug 63895 (Part 2) - Support table parts as absolute containing blocks. r=dbaron
☠☠ backed out by 969a7e228596 ☠ ☠
authorSeth Fowler <seth@mozilla.com>
Wed, 19 Feb 2014 16:03:30 -0800
changeset 169974 80bd36a3d4f1f8387be6ac0ebed6ef27d1e50880
parent 169973 6609365ee797da12a7789db922ab4237ba375d6b
child 169975 02e057fea56cce910b132af7087562edb77aa8eb
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersdbaron
bugs63895
milestone30.0a1
Bug 63895 (Part 2) - Support table parts as absolute containing blocks. r=dbaron
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/tables/nsTableCellFrame.cpp
layout/tables/nsTableCellFrame.h
layout/tables/nsTableFrame.cpp
layout/tables/nsTableFrame.h
layout/tables/nsTableRowFrame.cpp
layout/tables/nsTableRowFrame.h
layout/tables/nsTableRowGroupFrame.cpp
layout/tables/nsTableRowGroupFrame.h
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -1977,37 +1977,74 @@ nsCSSFrameConstructor::ConstructTable(ns
   // Set the outer table frame's secondary childlist lists
   if (captionItems.NotEmpty()) {
     newFrame->SetInitialChildList(nsIFrame::kCaptionList, captionItems);
   }
 
   return newFrame;
 }
 
+static void
+MakeTablePartAbsoluteContainingBlockIfNeeded(nsFrameConstructorState&     aState,
+                                             const nsStyleDisplay*        aDisplay,
+                                             nsFrameConstructorSaveState& aAbsSaveState,
+                                             nsIFrame*                    aFrame)
+{
+  // If we're positioned, then we need to become an absolute containing block
+  // for any absolutely positioned children and register for post-reflow fixup.
+  //
+  // Note that usually if a frame type can be an absolute containing block, we
+  // always set NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN, whether it actually is or not. 
+  // However, in this case flag serves the additional purpose of indicating that
+  // the frame was registered with its table frame. This allows us to avoid the
+  // overhead of unregistering the frame in most cases.
+  if (aDisplay->IsRelativelyPositionedStyle() ||
+      aDisplay->IsAbsolutelyPositionedStyle() ||
+      aDisplay->HasTransform(aFrame)) {
+    aFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
+    aState.PushAbsoluteContainingBlock(aFrame, aFrame, aAbsSaveState);
+    nsTableFrame::RegisterPositionedTablePart(aFrame);
+  }
+}
+
 nsIFrame*
-nsCSSFrameConstructor::ConstructTableRow(nsFrameConstructorState& aState,
-                                         FrameConstructionItem&   aItem,
-                                         nsIFrame*                aParentFrame,
-                                         const nsStyleDisplay*    aDisplay,
-                                         nsFrameItems&            aFrameItems)
-{
-  NS_PRECONDITION(aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_ROW,
+nsCSSFrameConstructor::ConstructTableRowOrRowGroup(nsFrameConstructorState& aState,
+                                                   FrameConstructionItem&   aItem,
+                                                   nsIFrame*                aParentFrame,
+                                                   const nsStyleDisplay*    aDisplay,
+                                                   nsFrameItems&            aFrameItems)
+{
+  NS_PRECONDITION(aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_ROW ||
+                  aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_ROW_GROUP ||
+                  aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP ||
+                  aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_HEADER_GROUP,
                   "Unexpected call");
+  MOZ_ASSERT(aItem.mStyleContext->StyleDisplay() == aDisplay,
+             "Display style doesn't match style context");
   nsIContent* const content = aItem.mContent;
   nsStyleContext* const styleContext = aItem.mStyleContext;
   const uint32_t nameSpaceID = aItem.mNameSpaceID;
 
   nsIFrame* newFrame;
-  if (kNameSpaceID_MathML == nameSpaceID)
-    newFrame = NS_NewMathMLmtrFrame(mPresShell, styleContext);
-  else
-    newFrame = NS_NewTableRowFrame(mPresShell, styleContext);
+  if (aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_ROW) {
+    if (kNameSpaceID_MathML == nameSpaceID)
+      newFrame = NS_NewMathMLmtrFrame(mPresShell, styleContext);
+    else
+      newFrame = NS_NewTableRowFrame(mPresShell, styleContext);
+  } else {
+    newFrame = NS_NewTableRowGroupFrame(mPresShell, styleContext);
+  }
 
   InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
 
+  nsFrameConstructorSaveState absoluteSaveState;
+  MakeTablePartAbsoluteContainingBlockIfNeeded(aState, aDisplay,
+                                               absoluteSaveState,
+                                               newFrame);
+
   nsFrameItems childItems;
   NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
                "nsIAnonymousContentCreator::CreateAnonymousContent "
                "implementations for table frames are not currently expected "
                "to output a list where the items have their own children");
   if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
     ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame,
                                 childItems);
@@ -2100,16 +2137,21 @@ nsCSSFrameConstructor::ConstructTableCel
     isBlock = false;
   } else {
     cellInnerFrame = NS_NewBlockFormattingContext(mPresShell, innerPseudoStyle);
     isBlock = true;
   }
 
   InitAndRestoreFrame(aState, content, newFrame, cellInnerFrame);
 
+  nsFrameConstructorSaveState absoluteSaveState;
+  MakeTablePartAbsoluteContainingBlockIfNeeded(aState, aDisplay,
+                                               absoluteSaveState,
+                                               newFrame);
+
   nsFrameItems childItems;
   NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
                "nsIAnonymousContentCreator::CreateAnonymousContent "
                "implementations for table frames are not currently expected "
                "to output a list where the items have their own children");
   if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
     // Need to push ourselves as a float containing block.
     // XXXbz it might be nice to work on getting the parent
@@ -4370,43 +4412,40 @@ nsCSSFrameConstructor::FindDisplayData(c
     // a desired-parent-type (& hence triggers table fixup), we'll need to also
     // update the flexbox chunk in nsStyleContext::ApplyStyleFixups().
     { NS_STYLE_DISPLAY_TABLE_CAPTION,
       FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_ALLOW_BLOCK_STYLES |
                   FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH |
                   FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
                   NS_NewTableCaptionFrame) },
     { NS_STYLE_DISPLAY_TABLE_ROW_GROUP,
-      FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW |
-                  FCDATA_SKIP_ABSPOS_PUSH |
-                  FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
-                  NS_NewTableRowGroupFrame) },
+      FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
+                       FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
+                       &nsCSSFrameConstructor::ConstructTableRowOrRowGroup) },
     { NS_STYLE_DISPLAY_TABLE_HEADER_GROUP,
-      FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW |
-                  FCDATA_SKIP_ABSPOS_PUSH |
-                  FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
-                  NS_NewTableRowGroupFrame) },
+      FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
+                       FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
+                       &nsCSSFrameConstructor::ConstructTableRowOrRowGroup) },
     { NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP,
-      FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW |
-                  FCDATA_SKIP_ABSPOS_PUSH |
-                  FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
-                  NS_NewTableRowGroupFrame) },
+      FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
+                       FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
+                       &nsCSSFrameConstructor::ConstructTableRowOrRowGroup) },
     { NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP,
       FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW |
                   FCDATA_SKIP_ABSPOS_PUSH |
                   FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
                   NS_NewTableColGroupFrame) },
     { NS_STYLE_DISPLAY_TABLE_COLUMN,
       FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
                        FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeColGroup),
                        &nsCSSFrameConstructor::ConstructTableCol) },
     { NS_STYLE_DISPLAY_TABLE_ROW,
       FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
                        FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup),
-                       &nsCSSFrameConstructor::ConstructTableRow) },
+                       &nsCSSFrameConstructor::ConstructTableRowOrRowGroup) },
     { NS_STYLE_DISPLAY_TABLE_CELL,
       FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
                        FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow),
                        &nsCSSFrameConstructor::ConstructTableCell) }
   };
 
   return FindDataByInt(aDisplay->mDisplay,
                        aElement, aStyleContext, sDisplayData,
@@ -8813,25 +8852,24 @@ nsCSSFrameConstructor::sPseudoParentData
                      FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow),
                      &nsCSSFrameConstructor::ConstructTableCell),
     &nsCSSAnonBoxes::tableCell
   },
   { // Row
     FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
                      FCDATA_USE_CHILD_ITEMS |
                      FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup),
-                     &nsCSSFrameConstructor::ConstructTableRow),
+                     &nsCSSFrameConstructor::ConstructTableRowOrRowGroup),
     &nsCSSAnonBoxes::tableRow
   },
   { // Row group
-    FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
-                FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_USE_CHILD_ITEMS |
-                FCDATA_SKIP_ABSPOS_PUSH |
-                FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
-                NS_NewTableRowGroupFrame),
+    FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
+                     FCDATA_USE_CHILD_ITEMS |
+                     FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
+                     &nsCSSFrameConstructor::ConstructTableRowOrRowGroup),
     &nsCSSAnonBoxes::tableRowGroup
   },
   { // Column group
     FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
                 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_USE_CHILD_ITEMS |
                 FCDATA_SKIP_ABSPOS_PUSH |
                 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
                 NS_NewTableColGroupFrame),
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -386,23 +386,23 @@ private:
    */
   nsIFrame* ConstructTable(nsFrameConstructorState& aState,
                            FrameConstructionItem&   aItem,
                            nsIFrame*                aParentFrame,
                            const nsStyleDisplay*    aDisplay,
                            nsFrameItems&            aFrameItems);
 
   /**
-   * FrameConstructionData callback used for constructing table rows.
+   * FrameConstructionData callback for constructing table rows and row groups.
    */
-  nsIFrame* ConstructTableRow(nsFrameConstructorState& aState,
-                              FrameConstructionItem&   aItem,
-                              nsIFrame*                aParentFrame,
-                              const nsStyleDisplay*    aStyleDisplay,
-                              nsFrameItems&            aFrameItems);
+  nsIFrame* ConstructTableRowOrRowGroup(nsFrameConstructorState& aState,
+                                        FrameConstructionItem&   aItem,
+                                        nsIFrame*                aParentFrame,
+                                        const nsStyleDisplay*    aStyleDisplay,
+                                        nsFrameItems&            aFrameItems);
 
   /**
    * FrameConstructionData callback used for constructing table columns.
    */
   nsIFrame* ConstructTableCol(nsFrameConstructorState& aState,
                               FrameConstructionItem&   aItem,
                               nsIFrame*                aParentFrame,
                               const nsStyleDisplay*    aStyleDisplay,
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -82,16 +82,26 @@ nsTableCellFrame::Init(nsIContent*      
     // Set the column index
     nsTableCellFrame* cellFrame = (nsTableCellFrame*)aPrevInFlow;
     int32_t           colIndex;
     cellFrame->GetColIndex(colIndex);
     SetColIndex(colIndex);
   }
 }
 
+void
+nsTableCellFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+  if (GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
+    nsTableFrame::UnregisterPositionedTablePart(this, aDestructRoot);
+  }
+
+  nsContainerFrame::DestroyFrom(aDestructRoot);
+}
+
 // nsIPercentHeightObserver methods
 
 void
 nsTableCellFrame::NotifyPercentHeight(const nsHTMLReflowState& aReflowState)
 {
   // nsHTMLReflowState ensures the mCBReflowState of blocks inside a
   // cell is the cell frame, not the inner-cell block, and that the
   // containing block of an inner table is the containing block of its
--- a/layout/tables/nsTableCellFrame.h
+++ b/layout/tables/nsTableCellFrame.h
@@ -42,16 +42,18 @@ public:
 
   nsTableCellFrame(nsStyleContext* aContext);
   ~nsTableCellFrame();
 
   virtual void Init(nsIContent*      aContent,
                     nsIFrame*        aParent,
                     nsIFrame*        aPrevInFlow) MOZ_OVERRIDE;
 
+  virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE;
+
 #ifdef ACCESSIBILITY
   virtual mozilla::a11y::AccType AccessibleType() MOZ_OVERRIDE;
 #endif
 
   virtual nsresult  AttributeChanged(int32_t         aNameSpaceID,
                                      nsIAtom*        aAttribute,
                                      int32_t         aModType) MOZ_OVERRIDE;
 
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -36,16 +36,17 @@
 #include "nsFrameManager.h"
 #include "nsError.h"
 #include "nsAutoPtr.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsStyleSet.h"
 #include "nsDisplayList.h"
 #include "nsIScrollableFrame.h"
 #include "nsCSSProps.h"
+#include "RestyleTracker.h"
 #include <algorithm>
 
 using namespace mozilla;
 using namespace mozilla::layout;
 
 /********************************************************************************
  ** nsTableReflowState                                                         **
  ********************************************************************************/
@@ -247,16 +248,74 @@ nsTableFrame::PageBreakAfter(nsIFrame* a
         (nextRg && nextRg->HasInternalBreakBefore())) &&
         !IsRepeatedFrame(aNextFrame)) {
       return !IsRepeatedFrame(aSourceFrame); // or after
     }
   }
   return false;
 }
 
+typedef nsTArray<nsIFrame*> FrameTArray;
+
+/* static */ void
+nsTableFrame::DestroyPositionedTablePartArray(void* aPropertyValue)
+{
+  auto positionedObjs = static_cast<FrameTArray*>(aPropertyValue);
+  delete positionedObjs;
+}
+
+/* static */ void
+nsTableFrame::RegisterPositionedTablePart(nsIFrame* aFrame)
+{
+  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(aFrame);
+  MOZ_ASSERT(tableFrame, "Should have a table frame here");
+  tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation());
+
+  // Retrieve the positioned parts array for this table.
+  FrameProperties props = tableFrame->Properties();
+  auto positionedParts =
+    static_cast<FrameTArray*>(props.Get(PositionedTablePartArray()));
+
+  // Lazily create the array if it doesn't exist yet.
+  if (!positionedParts) {
+    positionedParts = new FrameTArray;
+    props.Set(PositionedTablePartArray(), positionedParts);
+  }
+
+  // Add this frame to the list.
+  positionedParts->AppendElement(aFrame);
+}
+
+/* static */ void
+nsTableFrame::UnregisterPositionedTablePart(nsIFrame* aFrame,
+                                            nsIFrame* aDestructRoot)
+{
+  // Retrieve the table frame, and ensure that we hit aDestructRoot on the way.
+  // If we don't, that means that the table frame will be destroyed, so we don't
+  // need to bother with unregistering this frame.
+  nsTableFrame* tableFrame = GetTableFramePassingThrough(aDestructRoot, aFrame);
+  if (!tableFrame) {
+    return;
+  }
+  tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation());
+
+  // Retrieve the positioned parts array for this table.
+  FrameProperties props = tableFrame->Properties();
+  auto positionedParts =
+    static_cast<FrameTArray*>(props.Get(PositionedTablePartArray()));
+
+  // Remove the frame.
+  MOZ_ASSERT(positionedParts &&
+             positionedParts->IndexOf(aFrame) != FrameTArray::NoIndex,
+             "Asked to unregister a positioned table part that wasn't registered");
+  if (positionedParts) {
+    positionedParts->RemoveElement(aFrame);
+  }
+}
+
 // XXX this needs to be cleaned up so that the frame constructor breaks out col group
 // frames into a separate child list, bug 343048.
 nsresult
 nsTableFrame::SetInitialChildList(ChildListID     aListID,
                                   nsFrameList&    aChildList)
 {
 
   if (!mFrames.IsEmpty() || !mColGroups.IsEmpty()) {
@@ -1809,16 +1868,20 @@ nsresult nsTableFrame::Reflow(nsPresCont
 
   nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
   SetColumnDimensions(aDesiredSize.Height(), borderPadding);
   if (NeedToCollapse() &&
       (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableWidth())) {
     AdjustForCollapsingRowsCols(aDesiredSize, borderPadding);
   }
 
+  // If there are any relatively-positioned table parts, we need to reflow their
+  // absolutely-positioned descendants now that their dimensions are final.
+  FixupPositionedTableParts(aPresContext, aReflowState);
+
   // make sure the table overflow area does include the table rect.
   nsRect tableRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height()) ;
 
   if (!ShouldApplyOverflowClipping(this, aReflowState.mStyleDisplay)) {
     // collapsed border may leak out
     nsMargin bcMargin = GetExcludedOuterBCBorder();
     tableRect.Inflate(bcMargin);
   }
@@ -1829,16 +1892,67 @@ nsresult nsTableFrame::Reflow(nsPresCont
       nsIFrame::InvalidateFrame();
   }
 
   FinishAndStoreOverflow(&aDesiredSize);
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
   return rv;
 }
 
+void
+nsTableFrame::FixupPositionedTableParts(nsPresContext* aPresContext,
+                                        const nsHTMLReflowState& aReflowState)
+{
+  auto positionedParts =
+    static_cast<FrameTArray*>(Properties().Get(PositionedTablePartArray()));
+  if (!positionedParts) {
+    return;
+  }
+
+  OverflowChangedTracker overflowTracker;
+  overflowTracker.SetSubtreeRoot(this);
+
+  for (size_t i = 0; i < positionedParts->Length(); ++i) {
+    nsIFrame* positionedPart = positionedParts->ElementAt(i);
+
+    // As we've already finished reflow, positionedParts's size and overflow
+    // areas have already been assigned, so we just pull them back out.
+    nsSize size(positionedPart->GetSize());
+    nsHTMLReflowMetrics desiredSize(aReflowState.GetWritingMode());
+    desiredSize.Width() = size.width;
+    desiredSize.Height() = size.height;
+    desiredSize.mOverflowAreas = positionedPart->GetOverflowAreasRelativeToSelf();
+
+    // Construct a dummy reflow state and reflow status.
+    // XXX(seth): Note that the dummy reflow state doesn't have a correct
+    // chain of parent reflow states. It also doesn't necessarily have a
+    // correct containing block.
+    nsHTMLReflowState reflowState(aPresContext, positionedPart,
+                                  aReflowState.rendContext,
+                                  nsSize(size.width, NS_UNCONSTRAINEDSIZE),
+                                  nsHTMLReflowState::DUMMY_PARENT_REFLOW_STATE);
+    nsReflowStatus reflowStatus = NS_FRAME_COMPLETE;
+
+    // Reflow absolutely-positioned descendants of the positioned part.
+    // FIXME: Unconditionally using NS_UNCONSTRAINEDSIZE for the height and
+    // ignoring any change to the reflow status aren't correct. We'll never
+    // paginate absolutely positioned frames.
+    overflowTracker.AddFrame(positionedPart);
+    nsFrame* positionedFrame = static_cast<nsFrame*>(positionedPart);
+    positionedFrame->FinishReflowWithAbsoluteFrames(PresContext(),
+                                                    desiredSize,
+                                                    reflowState,
+                                                    reflowStatus,
+                                                    true);
+  }
+
+  // Propagate updated overflow areas up the tree.
+  overflowTracker.Flush();
+}
+
 bool
 nsTableFrame::UpdateOverflow()
 {
   nsRect bounds(nsPoint(0, 0), GetSize());
 
   // As above in Reflow, make sure the table overflow area includes the table
   // rect, and check for collapsed borders leaking out.
   if (!ShouldApplyOverflowClipping(this, StyleDisplay())) {
@@ -3439,16 +3553,41 @@ nsTableFrame::GetTableFrame(nsIFrame* aF
     if (nsGkAtoms::tableFrame == ancestor->GetType()) {
       return static_cast<nsTableFrame*>(ancestor);
     }
   }
   NS_RUNTIMEABORT("unable to find table parent");
   return nullptr;
 }
 
+nsTableFrame*
+nsTableFrame::GetTableFramePassingThrough(nsIFrame* aMustPassThrough,
+                                          nsIFrame* aFrame)
+{
+  MOZ_ASSERT(aMustPassThrough == aFrame ||
+             nsLayoutUtils::IsProperAncestorFrame(aMustPassThrough, aFrame),
+             "aMustPassThrough should be an ancestor");
+
+  // Retrieve the table frame, and ensure that we hit aMustPassThrough on the way.
+  // If we don't, just return null.
+  nsTableFrame* tableFrame = nullptr;
+  for (nsIFrame* ancestor = aFrame; ancestor; ancestor = ancestor->GetParent()) {
+    if (nsGkAtoms::tableFrame == ancestor->GetType()) {
+      tableFrame = static_cast<nsTableFrame*>(ancestor);
+      break;
+    }
+    if (ancestor == aMustPassThrough) {
+      return nullptr;
+    }
+  }
+
+  MOZ_ASSERT(tableFrame, "Should have a table frame here");
+  return tableFrame;
+}
+
 bool
 nsTableFrame::IsAutoHeight()
 {
   const nsStyleCoord &height = StylePosition()->mHeight;
   // Don't consider calc() here like this quirk for percent.
   return height.GetUnit() == eStyleUnit_Auto ||
          (height.GetUnit() == eStyleUnit_Percent &&
           height.GetPercentValue() <= 0.0f);
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -106,16 +106,19 @@ private:
   * The principal child list contains row group frames. There is also an
   * additional child list, kColGroupList, which contains the col group frames.
   */
 class nsTableFrame : public nsContainerFrame
 {
 public:
   NS_DECL_FRAMEARENA_HELPERS
 
+  static void DestroyPositionedTablePartArray(void* aPropertyValue);
+  NS_DECLARE_FRAME_PROPERTY(PositionedTablePartArray, DestroyPositionedTablePartArray)
+
   /** nsTableOuterFrame has intimate knowledge of the inner table frame */
   friend class nsTableOuterFrame;
 
   /** instantiate a new instance of nsTableRowFrame.
     * @param aPresShell the pres shell for this frame
     *
     * @return           the frame that was created
     */
@@ -141,16 +144,25 @@ public:
   // Notify the frame and its ancestors (up to the containing table) that a special
   // height reflow will occur. 
   static void RequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState);
 
   static void RePositionViews(nsIFrame* aFrame);
 
   static bool PageBreakAfter(nsIFrame* aSourceFrame,
                                nsIFrame* aNextFrame);
+  
+  // Register a positioned table part with its nsTableFrame. These objects will
+  // be visited by FixupPositionedTableParts after reflow is complete. (See that
+  // function for more explanation.) Should be called during frame construction.
+  static void RegisterPositionedTablePart(nsIFrame* aFrame);
+
+  // Unregister a positioned table part with its nsTableFrame.
+  static void UnregisterPositionedTablePart(nsIFrame* aFrame,
+                                            nsIFrame* aDestructRoot);
 
   nsPoint GetFirstSectionOrigin(const nsHTMLReflowState& aReflowState) const;
   /*
    * Notification that aAttribute has changed for content inside a table (cell, row, etc)
    */
   void AttributeChangedFor(nsIFrame*       aFrame,
                            nsIContent*     aContent, 
                            nsIAtom*        aAttribute); 
@@ -174,16 +186,22 @@ public:
   virtual nsMargin GetUsedMargin() const MOZ_OVERRIDE;
 
   // Get the offset from the border box to the area where the row groups fit
   nsMargin GetChildAreaOffset(const nsHTMLReflowState* aReflowState) const;
 
   /** helper method to find the table parent of any table frame object */
   static nsTableFrame* GetTableFrame(nsIFrame* aSourceFrame);
                                  
+  /* Like GetTableFrame, but will return nullptr if we don't pass through
+   * aMustPassThrough on the way to the table.
+   */
+  static nsTableFrame* GetTableFramePassingThrough(nsIFrame* aMustPassThrough,
+                                                   nsIFrame* aSourceFrame);
+
   typedef void (* DisplayGenericTablePartTraversal)
       (nsDisplayListBuilder* aBuilder, nsFrame* aFrame,
        const nsRect& aDirtyRect, const nsDisplayListSet& aLists);
   static void GenericTraversal(nsDisplayListBuilder* aBuilder, nsFrame* aFrame,
                                const nsRect& aDirtyRect, const nsDisplayListSet& aLists);
 
   /**
    * Helper method to handle display common to table frames, rowgroup frames
@@ -544,16 +562,28 @@ protected:
   /** Adjust the table for visibility.collapse set on rowgroups, rows,
     * colgroups and cols
     * @param aDesiredSize    the metrics of the table
     * @param aBorderPadding  the border and padding of the table
     */
   void AdjustForCollapsingRowsCols(nsHTMLReflowMetrics& aDesiredSize,
                                    nsMargin             aBorderPadding);
 
+  /** FixupPositionedTableParts is called at the end of table reflow to reflow
+   *  the absolutely positioned descendants of positioned table parts. This is
+   *  necessary because the dimensions of table parts may change after they've
+   *  been reflowed (e.g. in AdjustForCollapsingRowsCols).
+   */
+
+  void FixupPositionedTableParts(nsPresContext* aPresContext,
+                                 const nsHTMLReflowState& aReflowState);
+
+  // Clears the list of positioned table parts.
+  void ClearAllPositionedTableParts();
+
   nsITableLayoutStrategy* LayoutStrategy() const {
     return static_cast<nsTableFrame*>(FirstInFlow())->
       mTableLayoutStrategy;
   }
 
   // Helper for InsertFrames.
   void HomogenousInsertFrames(ChildListID     aListID,
                               nsIFrame*       aPrevFrame,
--- a/layout/tables/nsTableRowFrame.cpp
+++ b/layout/tables/nsTableRowFrame.cpp
@@ -145,16 +145,26 @@ nsTableRowFrame::Init(nsIContent*      a
   if (aPrevInFlow) {
     // Set the row index
     nsTableRowFrame* rowFrame = (nsTableRowFrame*)aPrevInFlow;
     
     SetRowIndex(rowFrame->GetRowIndex());
   }
 }
 
+void
+nsTableRowFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+  if (GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
+    nsTableFrame::UnregisterPositionedTablePart(this, aDestructRoot);
+  }
+
+  nsContainerFrame::DestroyFrom(aDestructRoot);
+}
+
 /* virtual */ void
 nsTableRowFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
 {
   nsContainerFrame::DidSetStyleContext(aOldStyleContext);
 
   if (!aOldStyleContext) //avoid this on init
     return;
      
--- a/layout/tables/nsTableRowFrame.h
+++ b/layout/tables/nsTableRowFrame.h
@@ -31,16 +31,19 @@ public:
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS
 
   virtual ~nsTableRowFrame();
 
   virtual void Init(nsIContent*      aContent,
                     nsIFrame*        aParent,
                     nsIFrame*        aPrevInFlow) MOZ_OVERRIDE;
+
+  virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE;
+
   /** @see nsIFrame::DidSetStyleContext */
   virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) MOZ_OVERRIDE;
   
   virtual nsresult AppendFrames(ChildListID     aListID,
                                 nsFrameList&    aFrameList) MOZ_OVERRIDE;
   virtual nsresult InsertFrames(ChildListID     aListID,
                                 nsIFrame*       aPrevFrame,
                                 nsFrameList&    aFrameList) MOZ_OVERRIDE;
--- a/layout/tables/nsTableRowGroupFrame.cpp
+++ b/layout/tables/nsTableRowGroupFrame.cpp
@@ -29,16 +29,26 @@ nsTableRowGroupFrame::nsTableRowGroupFra
 {
   SetRepeatable(false);
 }
 
 nsTableRowGroupFrame::~nsTableRowGroupFrame()
 {
 }
 
+void
+nsTableRowGroupFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+  if (GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
+    nsTableFrame::UnregisterPositionedTablePart(this, aDestructRoot);
+  }
+
+  nsContainerFrame::DestroyFrom(aDestructRoot);
+}
+
 NS_QUERYFRAME_HEAD(nsTableRowGroupFrame)
   NS_QUERYFRAME_ENTRY(nsTableRowGroupFrame)
 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
 
 int32_t
 nsTableRowGroupFrame::GetRowCount()
 {
 #ifdef DEBUG
--- a/layout/tables/nsTableRowGroupFrame.h
+++ b/layout/tables/nsTableRowGroupFrame.h
@@ -62,16 +62,19 @@ public:
 
   /** instantiate a new instance of nsTableRowFrame.
     * @param aPresShell the pres shell for this frame
     *
     * @return           the frame that was created
     */
   friend nsIFrame* NS_NewTableRowGroupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
   virtual ~nsTableRowGroupFrame();
+
+  virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE;
+
   /** @see nsIFrame::DidSetStyleContext */
   virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) MOZ_OVERRIDE;
   
   virtual nsresult AppendFrames(ChildListID     aListID,
                                 nsFrameList&    aFrameList) MOZ_OVERRIDE;
 
   virtual nsresult InsertFrames(ChildListID     aListID,
                                 nsIFrame*       aPrevFrame,