Bug 1389029 - Create custom change hint and post restyle event for rowspan, colspan attribute changes. draft
authorNeerja Pancholi <npancholi@mozilla.com>
Tue, 05 Sep 2017 13:30:40 -0700
changeset 659301 c9ba37d29ae2d555e120c8f6072062cf848e792e
parent 659233 973e8b890a62aee4b3170558ac3b608928162ef6
child 729961 eee8319ce7abc445fb5ca2c870c9b7f6580127c4
push id78099
push userbmo:npancholi@mozilla.com
push dateTue, 05 Sep 2017 20:52:00 +0000
bugs1389029
milestone57.0a1
Bug 1389029 - Create custom change hint and post restyle event for rowspan, colspan attribute changes. MozReview-Commit-ID: IwUlgDa3DAj
layout/base/RestyleManager.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/base/nsChangeHint.h
layout/tables/nsTableCellFrame.cpp
layout/tables/nsTableFrame.cpp
layout/tables/nsTableFrame.h
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -427,22 +427,22 @@ RestyleManager::ChangeHintToString(nsCha
     "UpdateSubtreeOverflow", "UpdatePostTransformOverflow",
     "UpdateParentOverflow",
     "ChildrenOnlyTransform", "RecomputePosition", "UpdateContainingBlock",
     "BorderStyleNoneChange", "UpdateTextPath", "SchedulePaint",
     "NeutralChange", "InvalidateRenderingObservers",
     "ReflowChangesSizeOrPosition", "UpdateComputedBSize",
     "UpdateUsesOpacity", "UpdateBackgroundPosition",
     "AddOrRemoveTransform", "CSSOverflowChange",
-    "UpdateWidgetProperties"
+    "UpdateWidgetProperties", "UpdateTableCellSpans"
   };
-  static_assert(nsChangeHint_AllHints == (1 << ArrayLength(names)) - 1,
+  static_assert(nsChangeHint_AllHints == (1u << ArrayLength(names)) - 1,
                 "Name list doesn't match change hints.");
-  uint32_t hint = aHint & ((1 << ArrayLength(names)) - 1);
-  uint32_t rest = aHint & ~((1 << ArrayLength(names)) - 1);
+  uint32_t hint = aHint & ((1u << ArrayLength(names)) - 1);
+  uint32_t rest = aHint & ~((1u << ArrayLength(names)) - 1);
   if ((hint & NS_STYLE_HINT_REFLOW) == NS_STYLE_HINT_REFLOW) {
     result.AppendLiteral("NS_STYLE_HINT_REFLOW");
     hint = hint & ~NS_STYLE_HINT_REFLOW;
     any = true;
   } else if ((hint & nsChangeHint_AllReflowHints) == nsChangeHint_AllReflowHints) {
     result.AppendLiteral("nsChangeHint_AllReflowHints");
     hint = hint & ~nsChangeHint_AllReflowHints;
     any = true;
@@ -1692,16 +1692,19 @@ RestyleManager::ProcessRestyledFrames(ns
       }
       if ((hint & nsChangeHint_UpdateCursor) && !didUpdateCursor) {
         presContext->PresShell()->SynthesizeMouseMove(false);
         didUpdateCursor = true;
       }
       if (hint & nsChangeHint_UpdateWidgetProperties) {
         frame->UpdateWidgetProperties();
       }
+      if (hint & nsChangeHint_UpdateTableCellSpans) {
+        frameConstructor->UpdateTableCellSpans(content);
+      }
     }
   }
 
   frameConstructor->EndUpdate();
 
 #ifdef DEBUG
   // Verify the style tree.  Note that this needs to happen once we've
   // processed the whole list, since until then the tree is not in fact in a
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -9959,16 +9959,31 @@ nsCSSFrameConstructor::MaybeRecreateCont
   }
 #endif
 
   ReframeContainingBlock(parent, aInsertionKind, aFlags);
   return true;
 }
 
 void
+nsCSSFrameConstructor::UpdateTableCellSpans(nsIContent* aContent)
+{
+  nsTableCellFrame* cellFrame = do_QueryFrame(aContent->GetPrimaryFrame());
+
+  // It's possible that this warning could fire if some other style change
+  // simultaneously changes the 'display' of the element and makes it no
+  // longer be a table cell.
+  NS_WARNING_ASSERTION(cellFrame, "Hint should only be posted on table cells!");
+
+  if (cellFrame) {
+    cellFrame->GetTableFrame()->RowOrColSpanChanged(cellFrame);
+  }
+}
+
+void
 nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent,
                                                 InsertionKind aInsertionKind,
                                                 RemoveFlags aFlags)
 {
   MOZ_ASSERT(aInsertionKind != InsertionKind::Async || aContent->IsElement());
   MOZ_ASSERT(aContent);
 
   // If there is no document, we don't want to recreate frames for it.  (You
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -1798,16 +1798,21 @@ private:
    * Recreate frames for aContent.
    * @param aContent the content to recreate frames for
    * @param aFlags normally you want to pass REMOVE_FOR_RECONSTRUCTION here
    */
   void RecreateFramesForContent(nsIContent*   aContent,
                                 InsertionKind aInsertionKind,
                                 RemoveFlags   aFlags);
 
+  /**
+   *  Handles change of rowspan and colspan attributes on table cells.
+   */
+  void UpdateTableCellSpans(nsIContent* aContent);
+
   // If removal of aFrame from the frame tree requires reconstruction of some
   // containing block (either of aFrame or of its parent) due to {ib} splits or
   // table pseudo-frames, recreate the relevant frame subtree.  The return value
   // indicates whether this happened.  aFrame must be the result of a
   // GetPrimaryFrame() call on a content node (which means its parent is also
   // not null).
   bool MaybeRecreateContainerForFrameRemoval(nsIFrame*     aFrame,
                                              InsertionKind aInsertionKind,
--- a/layout/base/nsChangeHint.h
+++ b/layout/base/nsChangeHint.h
@@ -233,32 +233,38 @@ enum nsChangeHint : uint32_t {
   nsChangeHint_CSSOverflowChange = 1 << 28,
 
   /**
    * Indicates that nsIFrame::UpdateWidgetProperties needs to be called.
    * This is used for -moz-window-* properties.
    */
   nsChangeHint_UpdateWidgetProperties = 1 << 29,
 
+  /**
+   *  Indicates that there has been a colspan or rowspan attribute change
+   *  on the cells of a table.
+   */
+  nsChangeHint_UpdateTableCellSpans = 1 << 30,
+
   // IMPORTANT NOTE: When adding a new hint, you will need to add it to
   // one of:
   //
   //   * nsChangeHint_Hints_NeverHandledForDescendants
   //   * nsChangeHint_Hints_AlwaysHandledForDescendants
   //   * nsChangeHint_Hints_SometimesHandledForDescendants
   //
   // and you also may need to handle it in NS_HintsNotHandledForDescendantsIn.
   //
   // Please also add it to RestyleManager::ChangeHintToString and
   // modify nsChangeHint_AllHints below accordingly.
 
   /**
    * Dummy hint value for all hints. It exists for compile time check.
    */
-  nsChangeHint_AllHints = (1 << 30) - 1,
+  nsChangeHint_AllHints = (1u << 31) - 1,
 };
 
 // Redefine these operators to return nothing. This will catch any use
 // of these operators on hints. We should not be using these operators
 // on nsChangeHints
 inline void operator<(nsChangeHint s1, nsChangeHint s2) {}
 inline void operator>(nsChangeHint s1, nsChangeHint s2) {}
 inline void operator!=(nsChangeHint s1, nsChangeHint s2) {}
@@ -348,16 +354,17 @@ inline nsChangeHint operator^=(nsChangeH
   nsChangeHint_UpdateBackgroundPosition |                  \
   nsChangeHint_UpdateComputedBSize |                       \
   nsChangeHint_UpdateContainingBlock |                     \
   nsChangeHint_UpdateEffects |                             \
   nsChangeHint_UpdateOpacityLayer |                        \
   nsChangeHint_UpdateOverflow |                            \
   nsChangeHint_UpdateParentOverflow |                      \
   nsChangeHint_UpdatePostTransformOverflow |               \
+  nsChangeHint_UpdateTableCellSpans |                          \
   nsChangeHint_UpdateTransformLayer |                      \
   nsChangeHint_UpdateUsesOpacity |                         \
   nsChangeHint_AddOrRemoveTransform |                      \
   nsChangeHint_UpdateWidgetProperties                      \
 )
 
 // The change hints that are sometimes considered to be handled for descendants.
 #define nsChangeHint_Hints_SometimesHandledForDescendants (\
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -225,18 +225,22 @@ nsTableCellFrame::AttributeChanged(int32
 {
   // We need to recalculate in this case because of the nowrap quirk in
   // BasicTableLayoutStrategy
   if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::nowrap &&
       PresContext()->CompatibilityMode() == eCompatibility_NavQuirks) {
     PresContext()->PresShell()->
       FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
   }
-  // let the table frame decide what to do
-  GetTableFrame()->AttributeChangedFor(this, mContent, aAttribute);
+
+  if (aAttribute == nsGkAtoms::rowspan || aAttribute == nsGkAtoms::colspan) {
+    nsLayoutUtils::PostRestyleEvent(mContent->AsElement(),
+                                    nsRestyleHint(0),
+                                    nsChangeHint_UpdateTableCellSpans);
+  }
   return NS_OK;
 }
 
 /* virtual */ void
 nsTableCellFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
 {
   nsContainerFrame::DidSetStyleContext(aOldStyleContext);
 
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -350,40 +350,34 @@ nsTableFrame::SetInitialChildList(ChildL
     // calc collapsing borders
     if (IsBorderCollapse()) {
       SetFullBCDamageArea();
     }
   }
 }
 
 void
-nsTableFrame::AttributeChangedFor(nsIFrame*       aFrame,
-                                  nsIContent*     aContent,
-                                  nsIAtom*        aAttribute)
-{
-  nsTableCellFrame *cellFrame = do_QueryFrame(aFrame);
-  if (cellFrame) {
-    if ((nsGkAtoms::rowspan == aAttribute) ||
-        (nsGkAtoms::colspan == aAttribute)) {
-      nsTableCellMap* cellMap = GetCellMap();
-      if (cellMap) {
-        // for now just remove the cell from the map and reinsert it
-        int32_t rowIndex, colIndex;
-        cellFrame->GetRowIndex(rowIndex);
-        cellFrame->GetColIndex(colIndex);
-        RemoveCell(cellFrame, rowIndex);
-        AutoTArray<nsTableCellFrame*, 1> cells;
-        cells.AppendElement(cellFrame);
-        InsertCells(cells, rowIndex, colIndex - 1);
-
-        // XXX Should this use eStyleChange?  It currently doesn't need
-        // to, but it might given more optimization.
-        PresContext()->PresShell()->
-          FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
-      }
+nsTableFrame::RowOrColSpanChanged(nsTableCellFrame* aCellFrame)
+{
+  if (aCellFrame) {
+    nsTableCellMap* cellMap = GetCellMap();
+    if (cellMap) {
+      // for now just remove the cell from the map and reinsert it
+      int32_t rowIndex, colIndex;
+      aCellFrame->GetRowIndex(rowIndex);
+      aCellFrame->GetColIndex(colIndex);
+      RemoveCell(aCellFrame, rowIndex);
+      AutoTArray<nsTableCellFrame*, 1> cells;
+      cells.AppendElement(aCellFrame);
+      InsertCells(cells, rowIndex, colIndex - 1);
+
+      // XXX Should this use eStyleChange?  It currently doesn't need
+      // to, but it might given more optimization.
+      PresContext()->PresShell()->
+        FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
     }
   }
 }
 
 
 /* ****** CellMap methods ******* */
 
 /* return the effective col count */
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -175,22 +175,22 @@ public:
   // 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 ReflowInput& aReflowInput) const;
+
   /*
-   * Notification that aAttribute has changed for content inside a table (cell, row, etc)
+   * Notification that rowspan or colspan has changed for content inside a
+   * table cell
    */
-  void AttributeChangedFor(nsIFrame*       aFrame,
-                           nsIContent*     aContent,
-                           nsIAtom*        aAttribute);
+  void RowOrColSpanChanged(nsTableCellFrame* aCellFrame);
 
   /** @see nsIFrame::DestroyFrom */
   virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
 
   /** @see nsIFrame::DidSetStyleContext */
   virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) override;
 
   virtual void SetInitialChildList(ChildListID     aListID,