Bug 1340723 part 3. Fix stylo to properly update styles on the anonymous blocks we create in a block-inside-inline situation when the style of the inline changes. r=emilio
authorBoris Zbarsky <bzbarsky@mit.edu>
Fri, 03 Mar 2017 15:45:40 -0500
changeset 345905 8335b277da3fb5c8407d694d87203830e2475bc4
parent 345904 f6370635b0906d84ac6880139ceb05dedc08bae1
child 345906 53a53f671871e4d7a854bd2c9d04471dd307b533
push id31451
push usercbook@mozilla.com
push dateMon, 06 Mar 2017 09:52:09 +0000
treeherdermozilla-central@7099e03837e8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs1340723
milestone54.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 1340723 part 3. Fix stylo to properly update styles on the anonymous blocks we create in a block-inside-inline situation when the style of the inline changes. r=emilio MozReview-Commit-ID: JYz6g1ZJInT
layout/base/ServoRestyleManager.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/generic/nsInlineFrame.cpp
layout/generic/nsInlineFrame.h
layout/reftests/ib-split/reftest-stylo.list
layout/reftests/ib-split/reftest.list
layout/reftests/ib-split/relpos-inline-1-ref.html
layout/reftests/ib-split/relpos-inline-1a.html
layout/reftests/ib-split/relpos-inline-1b.html
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -230,16 +230,19 @@ ServoRestyleManager::RecreateStyleContex
       aStyleSet->GetContext(computedValues.forget(), aParentContext, nullptr,
                             CSSPseudoElementType::NotPseudo, aElement);
 
     newContext->EnsureStructsForServo(oldStyleContext);
 
     // XXX This could not always work as expected: there are kinds of content
     // with the first split and the last sharing style, but others not. We
     // should handle those properly.
+    // XXXbz I think the UpdateStyleOfOwnedAnonBoxes call below handles _that_
+    // right, but not other cases where we happen to have different styles on
+    // different continuations... (e.g. first-line).
     for (nsIFrame* f = styleFrame; f;
          f = GetNextContinuationWithSameStyle(f, oldStyleContext)) {
       f->SetStyleContext(newContext);
     }
 
     if (isTable) {
       nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
       MOZ_ASSERT(primaryFrame->StyleContext()->GetPseudo() ==
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -12124,16 +12124,17 @@ nsCSSFrameConstructor::ConstructInline(n
   // has to be chopped into several pieces, as described above.
 
   // Grab the first inline's kids
   nsFrameList firstInlineKids = childItems.ExtractHead(firstBlockEnumerator);
   newFrame->SetInitialChildList(kPrincipalList, firstInlineKids);
 
   aFrameItems.AddChild(newFrame);
 
+  newFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
   CreateIBSiblings(aState, newFrame, positioned, childItems, aFrameItems);
 
   return newFrame;
 }
 
 void
 nsCSSFrameConstructor::CreateIBSiblings(nsFrameConstructorState& aState,
                                         nsContainerFrame* aInitialInline,
--- a/layout/generic/nsInlineFrame.cpp
+++ b/layout/generic/nsInlineFrame.cpp
@@ -14,18 +14,20 @@
 #include "nsPresContext.h"
 #include "nsRenderingContext.h"
 #include "nsCSSAnonBoxes.h"
 #include "mozilla/RestyleManager.h"
 #include "mozilla/RestyleManagerInlines.h"
 #include "nsDisplayList.h"
 #include "mozilla/Likely.h"
 #include "SVGTextFrame.h"
+#include "nsStyleChangeList.h"
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
+#include "mozilla/ServoStyleSet.h"
 
 #ifdef DEBUG
 #undef NOISY_PUSHING
 #endif
 
 using namespace mozilla;
 using namespace mozilla::layout;
 
@@ -1016,16 +1018,75 @@ nsInlineFrame::AccessibleType()
     return a11y::eHTMLButtonType;
   if (mContent->IsHTMLElement(nsGkAtoms::img))  // Create accessible for broken <img>
     return a11y::eHyperTextType;
 
   return a11y::eNoType;
 }
 #endif
 
+void
+nsInlineFrame::DoUpdateStyleOfOwnedAnonBoxes(ServoStyleSet& aStyleSet,
+                                             nsStyleChangeList& aChangeList,
+                                             nsChangeHint aHintForThisFrame)
+{
+  MOZ_ASSERT(GetStateBits() & NS_FRAME_OWNS_ANON_BOXES,
+             "Why did we get called?");
+  MOZ_ASSERT(GetStateBits() & NS_FRAME_PART_OF_IBSPLIT,
+             "Why did we have the NS_FRAME_OWNS_ANON_BOXES bit set?");
+  // Note: this assert _looks_ expensive, but it's cheap in all the cases when
+  // it passes!
+  MOZ_ASSERT(nsLayoutUtils::FirstContinuationOrIBSplitSibling(this) == this,
+             "Only the primary frame of the inline in a block-inside-inline "
+             "split should have NS_FRAME_OWNS_ANON_BOXES");
+  MOZ_ASSERT(mContent->GetPrimaryFrame() == this,
+             "We should be the primary frame for our element");
+
+  nsPresContext* presContext = PresContext();
+  // Get the FramePropertyTable up front, since we are likely to need it
+  // multiple times.  The other option would be to just call
+  // nsIFrame::Properties() every time we need to do a lookup, but that does
+  // more pointer-chasing.
+  FramePropertyTable* propTable = presContext->PropertyTable();
+  nsIFrame* blockFrame = propTable->Get(this, nsIFrame::IBSplitSibling());
+  MOZ_ASSERT(blockFrame, "Why did we have an IB split?");
+
+  nsIAtom* pseudo = blockFrame->StyleContext()->GetPseudo();
+  MOZ_ASSERT(pseudo == nsCSSAnonBoxes::mozAnonymousBlock ||
+             pseudo == nsCSSAnonBoxes::mozAnonymousPositionedBlock,
+             "Unexpected kind of style context");
+
+  // The anonymous block's style inherits from ours, and we already have our new
+  // style context.
+  RefPtr<nsStyleContext> newContext =
+    aStyleSet.ResolveAnonymousBoxStyle(pseudo, StyleContext());
+
+  // We're guaranteed that newContext only differs from the old style context on
+  // the block in things they might inherit from us.  And changehint processing
+  // guarantees walking the continuation and ib-sibling chains, so our existing
+  // changehint beign in aChangeList is good enough.  So we don't need to touch
+  // aChangeList at all here.
+
+  while (blockFrame) {
+    MOZ_ASSERT(!blockFrame->GetPrevContinuation(),
+               "Must be first continuation");
+    // We _could_ just walk along using GetNextContinuationWithSameStyle here,
+    // but it would involve going back to the first continuation every so often,
+    // which is a bit silly when we can just keep track of our first
+    // continuations.
+    for (nsIFrame* cont = blockFrame; cont; cont = cont->GetNextContinuation()) {
+      cont->SetStyleContext(newContext);
+    }
+
+    nsIFrame* nextInline = propTable->Get(blockFrame, nsIFrame::IBSplitSibling());
+    MOZ_ASSERT(nextInline, "There is always a trailing inline in an IB split");
+    blockFrame = propTable->Get(nextInline, nsIFrame::IBSplitSibling());
+  }
+}
+
 //////////////////////////////////////////////////////////////////////
 
 // nsLineFrame implementation
 
 nsFirstLineFrame*
 NS_NewFirstLineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsFirstLineFrame(aContext);
--- a/layout/generic/nsInlineFrame.h
+++ b/layout/generic/nsInlineFrame.h
@@ -111,16 +111,23 @@ public:
   bool IsLast() const {
     // If the frame's bidi visual state is set, return is-last state
     // else return true if it's the last continuation.
     return (GetStateBits() & NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET)
              ? !!(GetStateBits() & NS_INLINE_FRAME_BIDI_VISUAL_IS_LAST)
              : (!GetNextInFlow());
   }
 
+  // Update the style on the block wrappers around our non-inline-outside kids.
+  // This will only be called when such wrappers in fact exist.
+  virtual void DoUpdateStyleOfOwnedAnonBoxes(
+    mozilla::ServoStyleSet& aStyleSet,
+    nsStyleChangeList& aChangeList,
+    nsChangeHint aHintForThisFrame) override;
+
 protected:
   // Additional reflow state used during our reflow methods
   struct InlineReflowInput {
     nsIFrame* mPrevFrame;
     nsInlineFrame* mNextInFlow;
     nsIFrame*      mLineContainer;
     nsLineLayout*  mLineLayout;
     bool mSetParentPointer;  // when reflowing child frame first set its
--- a/layout/reftests/ib-split/reftest-stylo.list
+++ b/layout/reftests/ib-split/reftest-stylo.list
@@ -79,8 +79,10 @@ fails == float-inside-inline-between-blo
 == ignored-margins-1a.html ignored-margins-1a.html
 == ignored-margins-1b.html ignored-margins-1b.html
 == ignored-margins-2a.html ignored-margins-2a.html
 == ignored-margins-2b.html ignored-margins-2b.html
 == trailing-inline-with-continuations-1.html trailing-inline-with-continuations-1.html
 == append-to-empty-trailing-inline-1.html append-to-empty-trailing-inline-1.html
 == append-to-nested-split-inline-1.html append-to-nested-split-inline-1.html
 == append-to-nested-split-inline-1-ref.html append-to-nested-split-inline-1-ref.html
+== relpos-inline-1a.html relpos-inline-1a.html
+== relpos-inline-1b.html relpos-inline-1b.html
--- a/layout/reftests/ib-split/reftest.list
+++ b/layout/reftests/ib-split/reftest.list
@@ -78,8 +78,10 @@
 == ignored-margins-1a.html ignored-margins-1-ref.html
 == ignored-margins-1b.html ignored-margins-1-ref.html
 == ignored-margins-2a.html ignored-margins-2-ref.html
 == ignored-margins-2b.html ignored-margins-2-ref.html
 == trailing-inline-with-continuations-1.html trailing-inline-with-continuations-1-ref.html
 == append-to-empty-trailing-inline-1.html append-to-empty-trailing-inline-1-ref.html
 == append-to-nested-split-inline-1.html append-to-nested-split-inline-1-ref.html
 == append-to-nested-split-inline-1-ref.html append-to-nested-split-inline-1-noib-ref.html
+== relpos-inline-1a.html relpos-inline-1-ref.html
+== relpos-inline-1b.html relpos-inline-1-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/relpos-inline-1-ref.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      span, div { position: relative; left: 200px }
+      html {
+        overflow: hidden;
+      }
+    </style>
+  </head>
+  <body>
+    <span>All text should be offset 200px.</span>
+    <div>Some more text</div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/relpos-inline-1a.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>CSS 2.1 Test Suite: handling of blocks inside inlines</title>
+    <link rel="author" title="Boris Zbarsky" href="mailto:bzbarsky@mit.edu" />
+    <link rel="author" title="Mozilla Corporation" href="http://mozilla.com/" />
+    <link rel="help" href="http://www.w3.org/TR/CSS21/visuren.html#anonymous-block-level"/>
+    <meta name="flags" content="" />
+    <style>
+      span { position: relative; left: 200px }
+      html {
+        overflow: hidden;
+      }
+    </style>
+  </head>
+  <body>
+    <span style="position: relative; left: 200px">
+      All text should be offset 200px.
+      <div>Some more text</div>
+    </span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/relpos-inline-1b.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <head>
+    <title>CSS 2.1 Test Suite: handling of blocks inside inlines</title>
+    <link rel="author" title="Boris Zbarsky" href="mailto:bzbarsky@mit.edu" />
+    <link rel="author" title="Mozilla Corporation" href="http://mozilla.com/" />
+    <link rel="help" href="http://www.w3.org/TR/CSS21/visuren.html#anonymous-block-level"/>
+    <meta name="flags" content="" />
+    <style>
+      span { position: relative }
+      html {
+        overflow: hidden;
+      }
+    </style>
+  </head>
+  <body>
+    <span style="position: relative; left: 100px">
+      All text should be offset 200px.
+      <div>Some more text</div>
+    </span>
+    <script>
+      onload = function() {
+        var s = document.querySelector("span");
+        s.offsetWidth; // flush layout
+        s.style.left = "200px";
+        document.documentElement.className = "";
+      }
+    </script>
+  </body>
+</html>