Bug 1324619 part 5. Implement FrameForPseudoElement for ::first-line. r?emilio draft
authorBoris Zbarsky <bzbarsky@mit.edu>
Fri, 28 Jul 2017 21:20:46 -0400
changeset 617970 1cb14620b1725e09b8892ea23889a986b6a72cd5
parent 617969 4a988cbca13b3500b7aa05899fab9b671cdf785c
child 617971 31ad3df05bc4b20fb54dfc6eeadf556210cb63f0
push id71168
push userbzbarsky@mozilla.com
push dateSat, 29 Jul 2017 01:21:16 +0000
reviewersemilio
bugs1324619
milestone56.0a1
Bug 1324619 part 5. Implement FrameForPseudoElement for ::first-line. r?emilio MozReview-Commit-ID: JXcV58peI7T
layout/base/ServoRestyleManager.cpp
layout/generic/nsBlockFrame.cpp
layout/generic/nsBlockFrame.h
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -338,20 +338,20 @@ private:
   ServoStyleContext& mParentContext;
   ServoRestyleState& mParentRestyleState;
   RefPtr<nsStyleContext> mStyle;
   bool mShouldPostHints;
   bool mShouldComputeHints;
   nsChangeHint mComputedHint;
 };
 
-// Find the first-letter frame for the given element, if any.  Returns null to
-// indicate there isn't one.
-static nsIFrame*
-FindFirstLetterFrameForElement(const Element* aElement)
+// Get the nsBlockFrame which might contain ::first-letter/::first-line for the
+// given element.  Will return null if there is no such blockframe.
+static nsBlockFrame*
+GetBlockForElement(const Element* aElement)
 {
   nsIFrame* frame = aElement->GetPrimaryFrame();
   if (!frame) {
     return nullptr;
   }
   // The first-letter frame will always be inside the content insertion frame,
   // which will always be a block if we have a first-letter frame at all.
   frame = frame->GetContentInsertionFrame();
@@ -359,17 +359,35 @@ FindFirstLetterFrameForElement(const Ele
     // We're a leaf; certainly no first-letter frame.
     return nullptr;
   }
 
   if (!frame->IsFrameOfType(nsIFrame::eBlockFrame)) {
     return nullptr;
   }
 
-  return static_cast<nsBlockFrame*>(frame)->GetFirstLetter();
+  return static_cast<nsBlockFrame*>(frame);
+}
+
+// Find the first-letter frame for the given element, if any.  Returns null to
+// indicate there isn't one.
+static nsIFrame*
+FindFirstLetterFrameForElement(const Element* aElement)
+{
+  nsBlockFrame* f = GetBlockForElement(aElement);
+  return f ? f->GetFirstLetter() : nullptr;
+}
+
+// Find the first-line frame for the given element, if any.  Returns null to
+// indicate there isn't one.
+static nsIFrame*
+FindFirstLineFrameForElement(const Element* aElement)
+{
+  nsBlockFrame* f = GetBlockForElement(aElement);
+  return f ? f->GetFirstLineFrame() : nullptr;
 }
 
 static void
 UpdateBackdropIfNeeded(nsIFrame* aFrame,
                        ServoStyleSet& aStyleSet,
                        nsStyleChangeList& aChangeList)
 {
   const nsStyleDisplay* display = aFrame->StyleContext()->StyleDisplay();
@@ -798,18 +816,17 @@ ServoRestyleManager::FrameForPseudoEleme
     return pseudoElement ? nsLayoutUtils::GetStyleFrame(pseudoElement) : nullptr;
   }
 
   if (aPseudoTagOrNull == nsCSSPseudoElements::firstLetter) {
     return FindFirstLetterFrameForElement(aElement);
   }
 
   if (aPseudoTagOrNull == nsCSSPseudoElements::firstLine) {
-    // TODO(emilio, bz): Figure out the best way to diff these styles.
-    return nullptr;
+    return FindFirstLineFrameForElement(aElement);
   }
 
   MOZ_CRASH("Unkown pseudo-element given to "
             "ServoRestyleManager::FrameForPseudoElement");
   return nullptr;
 }
 
 void
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -7597,16 +7597,36 @@ nsBlockFrame::GetFirstLetter() const
   if (!(GetStateBits() & NS_BLOCK_HAS_FIRST_LETTER_STYLE)) {
     // Certainly no first-letter frame.
     return nullptr;
   }
 
   return GetProperty(FirstLetterProperty());
 }
 
+nsIFrame*
+nsBlockFrame::GetFirstLineFrame() const
+{
+  // Our ::first-line frame is either the first thing on our principal child
+  // list, or the second one if we have an inside bullet.
+  nsIFrame* bullet = GetInsideBullet();
+  nsIFrame* maybeFirstLine;
+  if (bullet) {
+    maybeFirstLine = bullet->GetNextSibling();
+  } else {
+    maybeFirstLine = PrincipalChildList().FirstChild();
+  }
+
+  if (maybeFirstLine && maybeFirstLine->IsLineFrame()) {
+    return maybeFirstLine;
+  }
+
+  return nullptr;
+}
+
 #ifdef DEBUG
 void
 nsBlockFrame::VerifyLines(bool aFinalCheckOK)
 {
   if (!gVerifyLines) {
     return;
   }
   if (mLines.empty()) {
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -272,16 +272,21 @@ public:
     return outside ? outside : GetInsideBullet();
   }
 
   /**
    * @return the first-letter frame or nullptr if we don't have one.
    */
   nsIFrame* GetFirstLetter() const;
 
+  /**
+   * @return the ::first-line frame or nullptr if we don't have one.
+   */
+  nsIFrame* GetFirstLineFrame() const;
+
   void MarkIntrinsicISizesDirty() override;
 private:
   void CheckIntrinsicCacheAgainstShrinkWrapState();
 public:
   nscoord GetMinISize(gfxContext *aRenderingContext) override;
   nscoord GetPrefISize(gfxContext *aRenderingContext) override;
 
   nsRect ComputeTightBounds(DrawTarget* aDrawTarget) const override;