Bug 1343771. Fix stylo to properly update styles on the anonymous block inside a table cell. r?emilio draft
authorBoris Zbarsky <bzbarsky@mit.edu>
Fri, 03 Mar 2017 16:14:39 -0500
changeset 493394 c1a0d1477a6b188dab330cee9d353db91dc0975d
parent 493393 53a53f671871e4d7a854bd2c9d04471dd307b533
child 493396 73a1aee82391a2efa96d54d66b631bc3a3013706
push id47743
push userbzbarsky@mozilla.com
push dateFri, 03 Mar 2017 21:15:16 +0000
reviewersemilio
bugs1343771
milestone54.0a1
Bug 1343771. Fix stylo to properly update styles on the anonymous block inside a table cell. r?emilio MozReview-Commit-ID: 8LnPTKVxxVc
layout/base/nsCSSFrameConstructor.cpp
layout/generic/nsFrame.cpp
layout/generic/nsFrame.h
layout/tables/nsTableCellFrame.cpp
layout/tables/nsTableCellFrame.h
layout/tables/reftests/dynamic-text-indent-table-cell-ref.html
layout/tables/reftests/dynamic-text-indent-table-cell.html
layout/tables/reftests/dynamic-text-overflow-table-cell-notref.html
layout/tables/reftests/dynamic-text-overflow-table-cell-ref.html
layout/tables/reftests/dynamic-text-overflow-table-cell.html
layout/tables/reftests/reftest-stylo.list
layout/tables/reftests/reftest.list
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -2288,16 +2288,17 @@ nsCSSFrameConstructor::ConstructTableCel
     // Warning: If you change this and add a wrapper frame around table cell
     // frames, make sure Bug 368554 doesn't regress!
     // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp.
     newFrame = NS_NewTableCellFrame(mPresShell, styleContext, tableFrame);
   }
 
   // Initialize the table cell frame
   InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
+  newFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
 
   // Resolve pseudo style and initialize the body cell frame
   RefPtr<nsStyleContext> innerPseudoStyle;
   innerPseudoStyle = mPresShell->StyleSet()->
     ResolveAnonymousBoxStyle(nsCSSAnonBoxes::cellContent, styleContext);
 
   // Create a block frame that will format the cell's content
   bool isBlock;
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -84,26 +84,28 @@
 
 #include "gfxContext.h"
 #include "nsRenderingContext.h"
 #include "nsAbsoluteContainingBlock.h"
 #include "StickyScrollContainer.h"
 #include "nsFontInflationData.h"
 #include "nsRegion.h"
 #include "nsIFrameInlines.h"
+#include "nsStyleChangeList.h"
 
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/EffectCompositor.h"
 #include "mozilla/EffectSet.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/MouseEvents.h"
+#include "mozilla/ServoStyleSet.h"
 #include "mozilla/css/ImageLoader.h"
 #include "mozilla/gfx/Tools.h"
 #include "nsPrintfCString.h"
 #include "ActiveLayerTracker.h"
 
 #include "nsITheme.h"
 #include "nsThemeConstants.h"
 
@@ -10045,16 +10047,60 @@ nsFrame::BoxReflow(nsBoxLayoutState&    
 nsBoxLayoutMetrics*
 nsFrame::BoxMetrics() const
 {
   nsBoxLayoutMetrics* metrics = Properties().Get(BoxMetricsProperty());
   NS_ASSERTION(metrics, "A box layout method was called but InitBoxMetrics was never called");
   return metrics;
 }
 
+void
+nsFrame::UpdateStyleOfChildAnonBox(nsIFrame* aChildFrame,
+                                   ServoStyleSet& aStyleSet,
+                                   nsStyleChangeList& aChangeList,
+                                   nsChangeHint aHintForThisFrame)
+{
+  MOZ_ASSERT(aChildFrame->GetParent() == this,
+             "This should only be used for children!");
+  MOZ_ASSERT(aChildFrame->GetContent() == GetContent(),
+             "What content node is it a frame for?");
+
+  // We could force the caller to pass in the pseudo, since some callers know it
+  // statically...  But this API is a bit nicer.
+  nsIAtom* pseudo = aChildFrame->StyleContext()->GetPseudo();
+  MOZ_ASSERT(nsCSSAnonBoxes::IsAnonBox(pseudo), "Child is not an anon box?");
+
+  // Anon boxes inherit from their parent; that's us.
+  RefPtr<nsStyleContext> newContext =
+    aStyleSet.ResolveAnonymousBoxStyle(pseudo, StyleContext());
+
+  // Figure out whether we have an actual change.  It's important that we do
+  // this, even though all the child's changes are due to properties it inherits
+  // from us, because it's possible that no one ever asked us for those style
+  // structs and hence changes to them aren't reflected in aHintForThisFrame at
+  // all.
+  uint32_t equalStructs, samePointerStructs; // Not used, actually.
+  nsChangeHint childHint = aChildFrame->StyleContext()->CalcStyleDifference(
+    newContext,
+    NS_HintsNotHandledForDescendantsIn(aHintForThisFrame),
+    &equalStructs,
+    &samePointerStructs);
+  if (childHint) {
+    aChangeList.AppendChange(aChildFrame, aChildFrame->GetContent(), childHint);
+  }
+
+  for (nsIFrame* kid = aChildFrame; kid; kid = kid->GetNextContinuation()) {
+    kid->SetStyleContext(newContext);
+  }
+
+  // Now that we've updated the style on aChildFrame, check whether it itself
+  // has anon boxes to deal with.
+  aChildFrame->UpdateStyleOfOwnedAnonBoxes(aStyleSet, aChangeList, childHint);
+}
+
 /* static */ void
 nsIFrame::AddInPopupStateBitToDescendants(nsIFrame* aFrame)
 {
   if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) &&
       aFrame->TrackingVisibility()) {
     // Assume all frames in popups are visible.
     aFrame->IncApproximateVisibleCount();
   }
--- a/layout/generic/nsFrame.h
+++ b/layout/generic/nsFrame.h
@@ -683,16 +683,23 @@ protected:
   void GetBoxName(nsAutoString& aName) override;
 #endif
 
   nsBoxLayoutMetrics* BoxMetrics() const;
 
   // Fire DOM event. If no aContent argument use frame's mContent.
   void FireDOMEvent(const nsAString& aDOMEventName, nsIContent *aContent = nullptr);
 
+  // A helper for implementing UpdateStyleOfOwnedAnonBoxes for the specific case
+  // of the owned anon box being a child of this frame.
+  void UpdateStyleOfChildAnonBox(nsIFrame* aChildFrame,
+                                 mozilla::ServoStyleSet& aStyleSet,
+                                 nsStyleChangeList& aChangeList,
+                                 nsChangeHint aHintForThisFrame);
+
 private:
   void BoxReflow(nsBoxLayoutState& aState,
                  nsPresContext*    aPresContext,
                  ReflowOutput&     aDesiredSize,
                  nsRenderingContext* aRenderingContext,
                  nscoord aX,
                  nscoord aY,
                  nscoord aWidth,
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -1099,16 +1099,27 @@ nsTableCellFrame::GetBorderWidth(Writing
 }
 
 nsIAtom*
 nsTableCellFrame::GetType() const
 {
   return nsGkAtoms::tableCellFrame;
 }
 
+void
+nsTableCellFrame::DoUpdateStyleOfOwnedAnonBoxes(ServoStyleSet& aStyleSet,
+                                                nsStyleChangeList& aChangeList,
+                                                nsChangeHint aHintForThisFrame)
+{
+  nsIFrame* kid = mFrames.FirstChild();
+  MOZ_ASSERT(kid && !kid->GetNextSibling(),
+             "Table cells should have just one child");
+  UpdateStyleOfChildAnonBox(kid, aStyleSet, aChangeList, aHintForThisFrame);
+}
+
 #ifdef DEBUG_FRAME_DUMP
 nsresult
 nsTableCellFrame::GetFrameName(nsAString& aResult) const
 {
   return MakeFrameName(NS_LITERAL_STRING("TableCell"), aResult);
 }
 #endif
 
--- a/layout/tables/nsTableCellFrame.h
+++ b/layout/tables/nsTableCellFrame.h
@@ -164,16 +164,22 @@ public:
    * content model or in the style info, and is always >= 1.
    * to get the effective row span (the actual value that applies), use GetEffectiveRowSpan()
    * @see nsTableFrame::GetEffectiveRowSpan()
    */
   virtual int32_t GetRowSpan();
 
   // there is no set row index because row index depends on the cell's parent row only
 
+  // Update the style on the block wrappers around our kids.
+  virtual void DoUpdateStyleOfOwnedAnonBoxes(
+    mozilla::ServoStyleSet& aStyleSet,
+    nsStyleChangeList& aChangeList,
+    nsChangeHint aHintForThisFrame) override;
+
   /*---------------- nsITableCellLayout methods ------------------------*/
 
   /**
    * return the cell's starting row index (starting at 0 for the first row).
    * for continued cell frames the row index is that of the cell's first-in-flow
    * and the column index (starting at 0 for the first column
    */
   NS_IMETHOD GetCellIndexes(int32_t &aRowIndex, int32_t &aColIndex) override;
new file mode 100644
--- /dev/null
+++ b/layout/tables/reftests/dynamic-text-indent-table-cell-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+  <table>
+    <tr>
+      <td style="text-indent: 50px">
+        Some text
+      </td>
+    </tr>
+  </table>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/tables/reftests/dynamic-text-indent-table-cell.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <table>
+    <tr>
+      <td>
+        Some text
+      </td>
+    </tr>
+  </table>
+  <script>
+    onload = function() {
+      var td = document.querySelector("td");
+      // Make sure layout has happened.
+      var width = td.offsetWidth;
+      td.style.textIndent = "50px";
+      document.documentElement.className = "";
+    }
+  </script>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/tables/reftests/dynamic-text-overflow-table-cell-notref.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<table style="table-layout: fixed; width: 130px">
+  <tr>
+    <td style="overflow: hidden; white-space: nowrap;">
+      Some long text that cannot possibly fit in 130 px, because it just can't.
+    </td>
+  </tr>
+</table>
new file mode 100644
--- /dev/null
+++ b/layout/tables/reftests/dynamic-text-overflow-table-cell-ref.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<table style="table-layout: fixed; width: 130px">
+  <tr>
+    <td style="overflow: hidden; white-space: nowrap; text-overflow: ellipsis">
+      Some long text that cannot possibly fit in 130 px, because it just can't.
+    </td>
+  </tr>
+</table>
new file mode 100644
--- /dev/null
+++ b/layout/tables/reftests/dynamic-text-overflow-table-cell.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <table style="table-layout: fixed; width: 130px">
+    <tr>
+      <td style="overflow: hidden; white-space: nowrap;">
+        Some long text that cannot possibly fit in 130 px, because it just can't.
+      </td>
+    </tr>
+  </table>
+  <script>
+    onload = function() {
+      var td = document.querySelector("td");
+      // Make sure layout has happened.
+      var width = td.offsetWidth;
+      td.style.textOverflow = "ellipsis";
+      document.documentElement.className = "";
+    }
+  </script>
+</html>
--- a/layout/tables/reftests/reftest-stylo.list
+++ b/layout/tables/reftests/reftest-stylo.list
@@ -3,8 +3,10 @@
 == 1220621-1a.html 1220621-1a.html
 == 1220621-1b.html 1220621-1b.html
 == 1220621-1c.html 1220621-1c.html
 == 1220621-1d.html 1220621-1d.html
 == 1220621-1e.html 1220621-1e.html
 == 1220621-1f.html 1220621-1f.html
 == 1220621-2a.html 1220621-2a.html
 == 1220621-2b.html 1220621-2b.html
+== dynamic-text-overflow-table-cell.html dynamic-text-overflow-table-cell.html
+== dynamic-text-indent-table-cell.html dynamic-text-indent-table-cell.html
--- a/layout/tables/reftests/reftest.list
+++ b/layout/tables/reftests/reftest.list
@@ -2,8 +2,11 @@
 == 1220621-1a.html 1220621-1-ref.html
 == 1220621-1b.html 1220621-1-ref.html
 == 1220621-1c.html 1220621-1-ref.html
 == 1220621-1d.html 1220621-1-ref.html
 == 1220621-1e.html 1220621-1-ref.html
 == 1220621-1f.html 1220621-1-ref.html
 == 1220621-2a.html 1220621-2-ref.html
 == 1220621-2b.html 1220621-2-ref.html
+== dynamic-text-overflow-table-cell.html dynamic-text-overflow-table-cell-ref.html
+!= dynamic-text-overflow-table-cell.html dynamic-text-overflow-table-cell-notref.html
+== dynamic-text-indent-table-cell.html dynamic-text-indent-table-cell-ref.html