Bug 1324618 part 4. Implement a way to get the first-letter frame, if any, for a block. r?heycam draft
authorBoris Zbarsky <bzbarsky@mit.edu>
Mon, 26 Jun 2017 23:35:07 -0700
changeset 600501 e0fb40829ad005eb766594e04fc0232da5935d0b
parent 600500 2ecdbe422959900ca16eee090e63a520d6006913
child 600502 6f8f5eb4e83b43e8fe08e9d12c57430fe0478a35
push id65783
push userbzbarsky@mozilla.com
push dateTue, 27 Jun 2017 06:35:27 +0000
reviewersheycam
bugs1324618
milestone56.0a1
Bug 1324618 part 4. Implement a way to get the first-letter frame, if any, for a block. r?heycam MozReview-Commit-ID: 1o0d5ugJ1hj
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/generic/nsBlockFrame.cpp
layout/generic/nsBlockFrame.h
layout/generic/nsContainerFrame.h
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -11625,17 +11625,17 @@ static bool IsFirstLetterContent(nsICont
 {
   return aContent->TextLength() &&
          !aContent->TextIsOnlyWhitespace();
 }
 
 /**
  * Create a letter frame, only make it a floating frame.
  */
-void
+nsFirstLetterFrame*
 nsCSSFrameConstructor::CreateFloatingLetterFrame(
   nsFrameConstructorState& aState,
   nsIContent* aTextContent,
   nsIFrame* aTextFrame,
   nsContainerFrame* aParentFrame,
   nsStyleContext* aStyleContext,
   nsFrameItems& aResult)
 {
@@ -11690,16 +11690,18 @@ nsCSSFrameConstructor::CreateFloatingLet
 
   aState.AddChild(letterFrame, aResult, letterContent, aStyleContext,
                   aParentFrame, false, true, false, true,
                   link.PrevFrame());
 
   if (nextTextFrame) {
     aResult.AddChild(nextTextFrame);
   }
+
+  return letterFrame;
 }
 
 /**
  * Create a new letter frame for aTextFrame. The letter frame will be
  * a child of aParentFrame.
  */
 void
 nsCSSFrameConstructor::CreateLetterFrame(nsContainerFrame* aBlockFrame,
@@ -11746,41 +11748,46 @@ nsCSSFrameConstructor::CreateLetterFrame
     nsFrameConstructorState state(mPresShell,
                                   matchContext,
                                   GetAbsoluteContainingBlock(aParentFrame, FIXED_POS),
                                   GetAbsoluteContainingBlock(aParentFrame, ABS_POS),
                                   aBlockContinuation);
 
     // Create the right type of first-letter frame
     const nsStyleDisplay* display = sc->StyleDisplay();
+    nsFirstLetterFrame* letterFrame;
     if (display->IsFloatingStyle() &&
         !nsSVGUtils::IsInSVGTextSubtree(aParentFrame)) {
       // Make a floating first-letter frame
-      CreateFloatingLetterFrame(state, aTextContent, textFrame,
-                                aParentFrame, sc, aResult);
+      letterFrame = CreateFloatingLetterFrame(state, aTextContent, textFrame,
+                                              aParentFrame, sc, aResult);
     }
     else {
       // Make an inflow first-letter frame
-      nsFirstLetterFrame* letterFrame = NS_NewFirstLetterFrame(mPresShell, sc);
+      letterFrame = NS_NewFirstLetterFrame(mPresShell, sc);
 
       // Initialize the first-letter-frame.  We don't want to use a text
       // content for a non-text frame (because we want its primary frame to
       // be a text frame).  So use its parent for the first-letter.
       nsIContent* letterContent = aTextContent->GetParent();
       letterFrame->Init(letterContent, aParentFrame, nullptr);
 
       InitAndRestoreFrame(state, aTextContent, letterFrame, textFrame);
 
       SetInitialSingleChild(letterFrame, textFrame);
       aResult.Clear();
       aResult.AddChild(letterFrame);
       NS_ASSERTION(!aBlockFrame->GetPrevContinuation(),
                    "should have the first continuation here");
       aBlockFrame->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD);
     }
+    MOZ_ASSERT(!aBlockFrame->GetPrevContinuation(),
+               "Setting up a first-letter frame on a non-first block continuation?");
+    aBlockFrame->SetProperty(nsContainerFrame::FirstLetterProperty(),
+                             letterFrame);
     aTextContent->SetPrimaryFrame(textFrame);
   }
 }
 
 void
 nsCSSFrameConstructor::WrapFramesInFirstLetterFrame(
   nsContainerFrame*        aBlockFrame,
   nsFrameItems&            aBlockFrames)
@@ -12047,16 +12054,17 @@ nsCSSFrameConstructor::RemoveFirstLetter
 }
 
 void
 nsCSSFrameConstructor::RemoveLetterFrames(nsIPresShell* aPresShell,
                                           nsContainerFrame* aBlockFrame)
 {
   aBlockFrame =
     static_cast<nsContainerFrame*>(aBlockFrame->FirstContinuation());
+  aBlockFrame->RemoveProperty(nsContainerFrame::FirstLetterProperty());
   nsContainerFrame* continuation = aBlockFrame;
 
   bool stopLooking = false;
   do {
     RemoveFloatingFirstLetterFrames(aPresShell, continuation);
     RemoveFirstLetterFrames(aPresShell, continuation, aBlockFrame,
                             &stopLooking);
     if (stopLooking) {
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -26,16 +26,17 @@
 
 struct nsFrameItems;
 class nsStyleContext;
 struct nsStyleDisplay;
 struct nsGenConInitializer;
 
 class nsContainerFrame;
 class nsFirstLineFrame;
+class nsFirstLetterFrame;
 class nsICSSAnonBoxPseudo;
 class nsIDocument;
 class nsPageContentFrame;
 struct PendingBinding;
 class nsGenericDOMDataNode;
 
 class nsFrameConstructorState;
 
@@ -1938,22 +1939,23 @@ private:
   void ReframeContainingBlock(nsIFrame*    aFrame,
                               RemoveFlags  aFlags,
                               nsIContent** aReframeContent);
 
   //----------------------------------------
 
   // Methods support :first-letter style
 
-  void CreateFloatingLetterFrame(nsFrameConstructorState& aState,
-                                 nsIContent*              aTextContent,
-                                 nsIFrame*                aTextFrame,
-                                 nsContainerFrame*        aParentFrame,
-                                 nsStyleContext*          aStyleContext,
-                                 nsFrameItems&            aResult);
+  nsFirstLetterFrame*
+  CreateFloatingLetterFrame(nsFrameConstructorState& aState,
+                            nsIContent*              aTextContent,
+                            nsIFrame*                aTextFrame,
+                            nsContainerFrame*        aParentFrame,
+                            nsStyleContext*          aStyleContext,
+                            nsFrameItems&            aResult);
 
   void CreateLetterFrame(nsContainerFrame*        aBlockFrame,
                          nsContainerFrame*        aBlockContinuation,
                          nsIContent*              aTextContent,
                          nsContainerFrame*        aParentFrame,
                          nsFrameItems&            aResult);
 
   void WrapFramesInFirstLetterFrame(nsContainerFrame* aBlockFrame,
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -7534,16 +7534,27 @@ nsBlockFrame::ResolveBulletStyle(CSSPseu
     CorrectStyleParentFrame(this,
                             nsCSSPseudoElements::GetPseudoAtom(aType))->
     StyleContext();
 
   return aStyleSet->ResolvePseudoElementStyle(mContent->AsElement(), aType,
                                               parentStyle, nullptr);
 }
 
+nsIFrame*
+nsBlockFrame::GetFirstLetter() const
+{
+  if (!(GetStateBits() & NS_BLOCK_HAS_FIRST_LETTER_STYLE)) {
+    // Certainly no first-letter frame.
+    return nullptr;
+  }
+
+  return GetProperty(FirstLetterProperty());
+}
+
 #ifdef DEBUG
 void
 nsBlockFrame::VerifyLines(bool aFinalCheckOK)
 {
   if (!gVerifyLines) {
     return;
   }
   if (mLines.empty()) {
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -267,16 +267,21 @@ public:
   /**
    * @return the bullet frame or nullptr if we don't have one.
    */
   nsBulletFrame* GetBullet() const {
     nsBulletFrame* outside = GetOutsideBullet();
     return outside ? outside : GetInsideBullet();
   }
 
+  /**
+   * @return the first-letter frame or nullptr if we don't have one.
+   */
+  nsIFrame* GetFirstLetter() const;
+
   void MarkIntrinsicISizesDirty() override;
 private:
   void CheckIntrinsicCacheAgainstShrinkWrapState();
 public:
   nscoord GetMinISize(gfxContext *aRenderingContext) override;
   nscoord GetPrefISize(gfxContext *aRenderingContext) override;
 
   nsRect ComputeTightBounds(DrawTarget* aDrawTarget) const override;
--- a/layout/generic/nsContainerFrame.h
+++ b/layout/generic/nsContainerFrame.h
@@ -516,16 +516,20 @@ public:
 
   typedef PropertyDescriptor<nsFrameList> FrameListPropertyDescriptor;
 
   NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowProperty)
   NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowContainersProperty)
   NS_DECLARE_FRAME_PROPERTY_FRAMELIST(ExcessOverflowContainersProperty)
   NS_DECLARE_FRAME_PROPERTY_FRAMELIST(BackdropProperty)
 
+  // Only really used on nsBlockFrame instances, but the caller thinks it could
+  // have arbitrary nsContainerFrames.
+  NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(FirstLetterProperty, nsIFrame)
+
 #ifdef DEBUG
   // Use this to suppress the CRAZY_SIZE assertions.
   NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(DebugReflowingWithInfiniteISize, bool)
   bool IsCrazySizeAssertSuppressed() const {
     return GetProperty(DebugReflowingWithInfiniteISize());
   }
 #endif