Bug 389321 - Part 3: Use a centralized algorithm for caret positioning; r=roc a=blocking-betaN+
authorEhsan Akhgari <ehsan@mozilla.com>
Tue, 31 Aug 2010 14:54:44 -0400
changeset 56866 ed0befc22bb702cd0f18f8837f88829afda6ab9e
parent 56865 5d3216e3857fd1849046bf156829ca973b775292
child 56867 38e63e07395aa7889797f453cce25d822b2f6741
push idunknown
push userunknown
push dateunknown
reviewersroc, blocking-betaN
bugs389321
milestone2.0b8pre
Bug 389321 - Part 3: Use a centralized algorithm for caret positioning; r=roc a=blocking-betaN+
layout/base/nsCaret.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/generic/nsBRFrame.cpp
layout/generic/nsBlockFrame.cpp
layout/generic/nsBlockFrame.h
layout/generic/nsBulletFrame.cpp
layout/generic/nsBulletFrame.h
layout/generic/nsFirstLetterFrame.cpp
layout/generic/nsFirstLetterFrame.h
layout/generic/nsIFrame.h
layout/generic/nsInlineFrame.cpp
layout/generic/nsInlineFrame.h
layout/generic/nsTextFrame.h
layout/generic/nsTextFrameThebes.cpp
--- a/layout/base/nsCaret.cpp
+++ b/layout/base/nsCaret.cpp
@@ -360,34 +360,27 @@ void nsCaret::SetCaretReadOnly(PRBool in
 void
 nsCaret::GetGeometryForFrame(nsIFrame* aFrame,
                              PRInt32   aFrameOffset,
                              nsRect*   aRect,
                              nscoord*  aBidiIndicatorSize)
 {
   nsPoint framePos(0, 0);
   aFrame->GetPointFromOffset(aFrameOffset, &framePos);
-  nscoord height = aFrame->GetContentRect().height;
-  if (height == 0) {
-    nsCOMPtr<nsIFontMetrics> fm;
-    nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm));
-    if (fm) {
-      nscoord ascent, descent;
-      fm->GetMaxAscent(ascent);
-      fm->GetMaxDescent(descent);
-      height = ascent + descent;
-
-      // Place the caret on the baseline for inline frames, except when there is
-      // a frame on the line with non-zero height.  XXXmats why the exception? --
-      // I don't know but it seems to be necessary, see bug 503531.
-      if (aFrame->GetStyleDisplay()->IsInlineOutside() &&
-          !FramesOnSameLineHaveZeroHeight(aFrame))
-        framePos.y -= ascent;
-    }
+  nscoord baseline = aFrame->GetCaretBaseline();
+  nscoord ascent = 0, descent = 0;
+  nsCOMPtr<nsIFontMetrics> fm;
+  nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm));
+  NS_ASSERTION(fm, "We should be able to get the font metrics");
+  if (fm) {
+    fm->GetMaxAscent(ascent);
+    fm->GetMaxDescent(descent);
   }
+  nscoord height = ascent + descent;
+  framePos.y = baseline - ascent;
   Metrics caretMetrics = ComputeMetrics(aFrame, aFrameOffset, height);
   *aRect = nsRect(framePos, nsSize(caretMetrics.mCaretWidth, height));
 
   // Clamp the x-position to be within our scroll frame. If we don't, then it
   // clips us, and we don't appear at all. See bug 335560.
   nsIFrame *scrollFrame =
     nsLayoutUtils::GetClosestFrameOfType(aFrame, nsGkAtoms::scrollFrame);
   if (scrollFrame) {
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -1645,17 +1645,17 @@ nsLayoutUtils::GetTextShadowRectsUnion(c
       nsContextBoxBlur::GetBlurRadiusMargin(shadow->mRadius, A2D));
 
     resultRect.UnionRect(resultRect, tmpRect);
   }
   return resultRect;
 }
 
 nsresult
-nsLayoutUtils::GetFontMetricsForFrame(nsIFrame* aFrame,
+nsLayoutUtils::GetFontMetricsForFrame(const nsIFrame* aFrame,
                                       nsIFontMetrics** aFontMetrics)
 {
   return nsLayoutUtils::GetFontMetricsForStyleContext(aFrame->GetStyleContext(),
                                                       aFontMetrics);
 }
 
 nsresult
 nsLayoutUtils::GetFontMetricsForStyleContext(nsStyleContext* aStyleContext,
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -667,17 +667,17 @@ public:
                                         nsIFrame* aFrame);
 
   /**
    * Get the font metrics corresponding to the frame's style data.
    * @param aFrame the frame
    * @param aFontMetrics the font metrics result
    * @return success or failure code
    */
-  static nsresult GetFontMetricsForFrame(nsIFrame* aFrame,
+  static nsresult GetFontMetricsForFrame(const nsIFrame* aFrame,
                                          nsIFontMetrics** aFontMetrics);
 
   /**
    * Get the font metrics corresponding to the given style data.
    * @param aStyleContext the style data
    * @param aFontMetrics the font metrics result
    * @return success or failure code
    */
--- a/layout/generic/nsBRFrame.cpp
+++ b/layout/generic/nsBRFrame.cpp
@@ -53,16 +53,18 @@
 #include "nsIAccessibilityService.h"
 #endif
 
 //FOR SELECTION
 #include "nsIContent.h"
 #include "nsFrameSelection.h"
 //END INCLUDES FOR SELECTION
 
+#define BR_USING_CENTERED_FONT_BASELINE NS_FRAME_STATE_BIT(63)
+
 class BRFrame : public nsFrame {
 public:
   NS_DECL_FRAMEARENA_HELPERS
 
   friend nsIFrame* NS_NewBRFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 
   virtual ContentOffsets CalcContentOffsetsFromFramePoint(nsPoint aPoint);
 
@@ -77,16 +79,17 @@ public:
                     nsReflowStatus& aStatus);
   virtual void AddInlineMinWidth(nsIRenderingContext *aRenderingContext,
                                  InlineMinWidthData *aData);
   virtual void AddInlinePrefWidth(nsIRenderingContext *aRenderingContext,
                                   InlinePrefWidthData *aData);
   virtual nscoord GetMinWidth(nsIRenderingContext *aRenderingContext);
   virtual nscoord GetPrefWidth(nsIRenderingContext *aRenderingContext);
   virtual nsIAtom* GetType() const;
+  virtual nscoord GetBaseline() const;
 
   virtual PRBool IsFrameOfType(PRUint32 aFlags) const
   {
     return nsFrame::IsFrameOfType(aFlags & ~(nsIFrame::eReplaced |
                                              nsIFrame::eLineParticipant));
   }
 
 #ifdef ACCESSIBILITY
@@ -118,16 +121,17 @@ BRFrame::Reflow(nsPresContext* aPresCont
 {
   DO_GLOBAL_REFLOW_COUNT("BRFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
   aMetrics.height = 0; // BR frames with height 0 are ignored in quirks
                        // mode by nsLineLayout::VerticalAlignFrames .
                        // However, it's not always 0.  See below.
   aMetrics.width = 0;
   aMetrics.ascent = 0;
+  RemoveStateBits(BR_USING_CENTERED_FONT_BASELINE);
 
   // Only when the BR is operating in a line-layout situation will it
   // behave like a BR.
   nsLineLayout* ll = aReflowState.mLineLayout;
   if (ll) {
     // Note that the compatibility mode check excludes AlmostStandards
     // mode, since this is the inline box model.  See bug 161691.
     if ( ll->LineIsEmpty() ||
@@ -149,16 +153,17 @@ BRFrame::Reflow(nsPresContext* aPresCont
       nsLayoutUtils::SetFontFromStyle(aReflowState.rendContext, mStyleContext);
       nsCOMPtr<nsIFontMetrics> fm;
       aReflowState.rendContext->GetFontMetrics(*getter_AddRefs(fm));
       if (fm) {
         nscoord logicalHeight = aReflowState.CalcLineHeight();
         aMetrics.height = logicalHeight;
         aMetrics.ascent =
           nsLayoutUtils::GetCenteredFontBaseline(fm, logicalHeight);
+        AddStateBits(BR_USING_CENTERED_FONT_BASELINE);
       }
       else {
         aMetrics.ascent = aMetrics.height = 0;
       }
 
       // XXX temporary until I figure out a better solution; see the
       // code in nsLineLayout::VerticalAlignFrames that zaps minY/maxY
       // if the width is zero.
@@ -219,16 +224,34 @@ BRFrame::GetPrefWidth(nsIRenderingContex
 }
 
 nsIAtom*
 BRFrame::GetType() const
 {
   return nsGkAtoms::brFrame;
 }
 
+nscoord
+BRFrame::GetBaseline() const
+{
+  nscoord ascent = 0;
+  nsCOMPtr<nsIFontMetrics> fm;
+  nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
+  if (fm) {
+    nscoord logicalHeight = GetRect().height;
+    if (GetStateBits() & BR_USING_CENTERED_FONT_BASELINE) {
+      ascent = nsLayoutUtils::GetCenteredFontBaseline(fm, logicalHeight);
+    } else {
+      fm->GetMaxAscent(ascent);
+      ascent += GetUsedBorderAndPadding().top;
+    }
+  }
+  return ascent;
+}
+
 nsIFrame::ContentOffsets BRFrame::CalcContentOffsetsFromFramePoint(nsPoint aPoint)
 {
   ContentOffsets offsets;
   offsets.content = mContent->GetParent();
   if (offsets.content) {
     offsets.offset = offsets.content->IndexOf(mContent);
     offsets.secondaryOffset = offsets.offset;
     offsets.associateWithNext = PR_TRUE;
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -545,23 +545,42 @@ nsBlockFrame::InvalidateInternal(const n
   }
 
   nsBlockFrameSuper::InvalidateInternal(aDamageRect, aX, aY, this, aFlags);
 }
 
 nscoord
 nsBlockFrame::GetBaseline() const
 {
-  NS_ASSERTION(!NS_SUBTREE_DIRTY(this), "frame must not be dirty");
   nscoord result;
   if (nsLayoutUtils::GetLastLineBaseline(this, &result))
     return result;
   return nsFrame::GetBaseline();
 }
 
+nscoord
+nsBlockFrame::GetCaretBaseline() const
+{
+  nsRect contentRect = GetContentRect();
+  nsMargin bp = GetUsedBorderAndPadding();
+
+  if (!mLines.empty()) {
+    const_line_iterator line = begin_lines();
+    const nsLineBox* firstLine = line;
+    if (firstLine->GetChildCount()) {
+      return bp.top + firstLine->mFirstChild->GetCaretBaseline();
+    }
+  }
+  nsCOMPtr<nsIFontMetrics> fm;
+  nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
+  return nsLayoutUtils::GetCenteredFontBaseline(fm, nsHTMLReflowState::
+      CalcLineHeight(GetStyleContext(), contentRect.height)) +
+    bp.top;
+}
+
 /////////////////////////////////////////////////////////////////////////////
 // Child frame enumeration
 
 nsFrameList
 nsBlockFrame::GetChildList(nsIAtom* aListName) const
 {
   if (nsGkAtoms::absoluteList == aListName) {
     return mAbsoluteContainer.GetChildList();
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -179,16 +179,17 @@ public:
                            nsFrameList&    aFrameList);
   NS_IMETHOD  InsertFrames(nsIAtom*        aListName,
                            nsIFrame*       aPrevFrame,
                            nsFrameList&    aFrameList);
   NS_IMETHOD  RemoveFrame(nsIAtom*        aListName,
                           nsIFrame*       aOldFrame);
   virtual nsFrameList GetChildList(nsIAtom* aListName) const;
   virtual nscoord GetBaseline() const;
+  virtual nscoord GetCaretBaseline() const;
   virtual nsIAtom* GetAdditionalChildListName(PRInt32 aIndex) const;
   virtual void DestroyFrom(nsIFrame* aDestructRoot);
   virtual nsSplittableType GetSplittableType() const;
   virtual PRBool IsContainingBlock() const;
   virtual PRBool IsFloatContainingBlock() const;
   NS_IMETHOD BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                               const nsRect&           aDirtyRect,
                               const nsDisplayListSet& aLists);
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -57,16 +57,18 @@
 #include "imgILoader.h"
 #include "imgIContainer.h"
 #include "nsStubImageDecoderObserver.h"
 
 #include "nsIServiceManager.h"
 #include "nsIComponentManager.h"
 #include "nsContentUtils.h"
 
+#define BULLET_FRAME_IMAGE_LOADING NS_FRAME_STATE_BIT(63)
+
 class nsBulletListener : public nsStubImageDecoderObserver
 {
 public:
   nsBulletListener();
   virtual ~nsBulletListener();
 
   NS_DECL_ISUPPORTS
   // imgIDecoderObserver (override nsStubImageDecoderObserver)
@@ -1273,28 +1275,32 @@ nsBulletFrame::GetDesiredSize(nsPresCont
                               nsHTMLReflowMetrics& aMetrics)
 {
   // Reset our padding.  If we need it, we'll set it below.
   mPadding.SizeTo(0, 0, 0, 0);
   
   const nsStyleList* myList = GetStyleList();
   nscoord ascent;
 
+  RemoveStateBits(BULLET_FRAME_IMAGE_LOADING);
+
   if (myList->GetListStyleImage() && mImageRequest) {
     PRUint32 status;
     mImageRequest->GetImageStatus(&status);
     if (status & imgIRequest::STATUS_SIZE_AVAILABLE &&
         !(status & imgIRequest::STATUS_ERROR)) {
       // auto size the image
       mComputedSize.width = mIntrinsicSize.width;
       mComputedSize.height = mIntrinsicSize.height;
 
       aMetrics.width = mComputedSize.width;
       aMetrics.ascent = aMetrics.height = mComputedSize.height;
 
+      AddStateBits(BULLET_FRAME_IMAGE_LOADING);
+
       return;
     }
   }
 
   // If we're getting our desired size and don't have an image, reset
   // mIntrinsicSize to (0,0).  Otherwise, if we used to have an image, it
   // changed, and the new one is coming in, but we're reflowing before it's
   // fully there, we'll end up with mIntrinsicSize not matching our size, but
@@ -1534,16 +1540,48 @@ nsBulletFrame::GetLoadGroup(nsPresContex
 
   nsIDocument *doc = shell->GetDocument();
   if (!doc)
     return;
 
   *aLoadGroup = doc->GetDocumentLoadGroup().get();  // already_AddRefed
 }
 
+nscoord
+nsBulletFrame::GetBaseline() const
+{
+  nscoord ascent = 0, bottomPadding;
+  if (GetStateBits() & BULLET_FRAME_IMAGE_LOADING) {
+    ascent = GetRect().height;
+  } else {
+    nsCOMPtr<nsIFontMetrics> fm;
+    nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
+    const nsStyleList* myList = GetStyleList();
+    switch (myList->mListStyleType) {
+      case NS_STYLE_LIST_STYLE_NONE:
+        break;
+
+      case NS_STYLE_LIST_STYLE_DISC:
+      case NS_STYLE_LIST_STYLE_CIRCLE:
+      case NS_STYLE_LIST_STYLE_SQUARE:
+        fm->GetMaxAscent(ascent);
+        bottomPadding = NSToCoordRound(float(ascent) / 8.0f);
+        ascent = NS_MAX(nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE),
+                        NSToCoordRound(0.8f * (float(ascent) / 2.0f)));
+        ascent += bottomPadding;
+        break;
+
+      default:
+        fm->GetMaxAscent(ascent);
+        break;
+    }
+  }
+  return ascent + GetUsedBorderAndPadding().top;
+}
+
 
 
 
 
 
 
 
 NS_IMPL_ISUPPORTS2(nsBulletListener, imgIDecoderObserver, imgIContainerObserver)
--- a/layout/generic/nsBulletFrame.h
+++ b/layout/generic/nsBulletFrame.h
@@ -99,16 +99,17 @@ public:
   PRBool GetListItemText(const nsStyleList& aStyleList,
                          nsString& aResult);
                          
   void PaintBullet(nsIRenderingContext& aRenderingContext, nsPoint aPt,
                    const nsRect& aDirtyRect);
   
   virtual PRBool IsEmpty();
   virtual PRBool IsSelfEmpty();
+  virtual nscoord GetBaseline() const;
 
 protected:
   void GetDesiredSize(nsPresContext* aPresContext,
                       nsIRenderingContext *aRenderingContext,
                       nsHTMLReflowMetrics& aMetrics);
 
   void GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup);
 
--- a/layout/generic/nsFirstLetterFrame.cpp
+++ b/layout/generic/nsFirstLetterFrame.cpp
@@ -390,8 +390,14 @@ nsFirstLetterFrame::DrainOverflowFrames(
                    "should contain only text nodes");
       sc = aPresContext->StyleSet()->ResolveStyleForNonElement(mStyleContext);
       if (sc) {
         kid->SetStyleContext(sc);
       }
     }
   }
 }
+
+nscoord
+nsFirstLetterFrame::GetBaseline() const
+{
+  return mBaseline;
+}
--- a/layout/generic/nsFirstLetterFrame.h
+++ b/layout/generic/nsFirstLetterFrame.h
@@ -79,16 +79,17 @@ public:
                              nsSize aMargin, nsSize aBorder, nsSize aPadding,
                              PRBool aShrinkWrap);
   NS_IMETHOD Reflow(nsPresContext*          aPresContext,
                     nsHTMLReflowMetrics&     aDesiredSize,
                     const nsHTMLReflowState& aReflowState,
                     nsReflowStatus&          aStatus);
 
   virtual PRBool CanContinueTextRun() const;
+  virtual nscoord GetBaseline() const;
 
 //override of nsFrame method
   NS_IMETHOD GetChildFrameContainingOffset(PRInt32 inContentOffset,
                                            PRBool inHint,
                                            PRInt32* outFrameContentOffset,
                                            nsIFrame **outChildFrame);
 
   nscoord GetFirstLetterBaseline() const { return mBaseline; }
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -967,22 +967,31 @@ public:
   virtual PRBool GetBorderRadii(nscoord aRadii[8]) const;
 
   PRBool GetPaddingBoxBorderRadii(nscoord aRadii[8]) const;
   PRBool GetContentBoxBorderRadii(nscoord aRadii[8]) const;
 
   /**
    * Get the position of the frame's baseline, relative to the top of
    * the frame (its top border edge).  Only valid when Reflow is not
-   * needed and when the frame returned nsHTMLReflowMetrics::
-   * ASK_FOR_BASELINE as ascent in its reflow metrics.
+   * needed.
    */
   virtual nscoord GetBaseline() const = 0;
 
   /**
+   * Get the position of the baseline on which the caret needs to be placed,
+   * relative to the top of the frame.  This is mostly needed for frames
+   * which return a baseline from GetBaseline which is not useful for
+   * caret positioning.
+   */
+  virtual nscoord GetCaretBaseline() const {
+    return GetBaseline();
+  }
+
+  /**
    * Used to iterate the list of additional child list names. Returns the atom
    * name for the additional child list at the specified 0-based index, or a
    * NULL pointer if there are no more named child lists.
    *
    * Note that the list is only the additional named child lists and does not
    * include the unnamed principal child list.
    */
   virtual nsIAtom* GetAdditionalChildListName(PRInt32 aIndex) const = 0;
--- a/layout/generic/nsInlineFrame.cpp
+++ b/layout/generic/nsInlineFrame.cpp
@@ -915,16 +915,27 @@ nsInlineFrame::GetSkipSides() const
         skip |= startBit;
       }
     }
   }
 
   return skip;
 }
 
+nscoord
+nsInlineFrame::GetBaseline() const
+{
+  nscoord ascent = 0;
+  nsCOMPtr<nsIFontMetrics> fm;
+  if (NS_SUCCEEDED(nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)))) {
+    fm->GetMaxAscent(ascent);
+  }
+  return ascent + GetUsedBorderAndPadding().top;
+}
+
 #ifdef ACCESSIBILITY
 already_AddRefed<nsAccessible>
 nsInlineFrame::CreateAccessible()
 {
   // Broken image accessibles are created here, because layout
   // replaces the image or image control frame with an inline frame
   nsIAtom *tagAtom = mContent->Tag();
   if ((tagAtom == nsGkAtoms::img || tagAtom == nsGkAtoms::input || 
--- a/layout/generic/nsInlineFrame.h
+++ b/layout/generic/nsInlineFrame.h
@@ -112,16 +112,17 @@ public:
   NS_IMETHOD Reflow(nsPresContext* aPresContext,
                     nsHTMLReflowMetrics& aDesiredSize,
                     const nsHTMLReflowState& aReflowState,
                     nsReflowStatus& aStatus);
 
   virtual PRBool CanContinueTextRun() const;
 
   virtual void PullOverflowsFromPrevInFlow();
+  virtual nscoord GetBaseline() const;
 
   /**
    * Return true if the frame is leftmost frame or continuation.
    */
   PRBool IsLeftMost() const {
     // If the frame's bidi visual state is set, return is-leftmost state
     // else return true if it's the first continuation.
     return (GetStateBits() & NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET)
@@ -187,17 +188,16 @@ protected:
   virtual nsIFrame* PullOneFrame(nsPresContext* aPresContext,
                                  InlineReflowState& rs,
                                  PRBool* aIsComplete);
 
   virtual void PushFrames(nsPresContext* aPresContext,
                           nsIFrame* aFromChild,
                           nsIFrame* aPrevSibling,
                           InlineReflowState& aState);
-
 };
 
 //----------------------------------------------------------------------
 
 /**
  * Variation on inline-frame used to manage lines for line layout in
  * special situations (:first-line style in particular).
  */
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -189,16 +189,17 @@ public:
                                             PRBool                  inHint,
                                             PRInt32*                outFrameContentOffset,
                                             nsIFrame*               *outChildFrame);
   
   virtual PRBool IsVisibleInSelection(nsISelection* aSelection);
   
   virtual PRBool IsEmpty();
   virtual PRBool IsSelfEmpty() { return IsEmpty(); }
+  virtual nscoord GetBaseline() const;
   
   /**
    * @return PR_TRUE if this text frame ends with a newline character.  It
    * should return PR_FALSE if this is not a text frame.
    */
   virtual PRBool HasTerminalNewline() const;
 
   /**
--- a/layout/generic/nsTextFrameThebes.cpp
+++ b/layout/generic/nsTextFrameThebes.cpp
@@ -6562,16 +6562,28 @@ nsTextFrame::ReflowText(nsLineLayout& aL
     mTextRun->BreakAndMeasureText(transformedOffset, transformedLength,
                                   (GetStateBits() & TEXT_START_OF_LINE) != 0,
                                   availWidth,
                                   &provider, !aLineLayout.LineIsBreakable(),
                                   canTrimTrailingWhitespace ? &trimmedWidth : nsnull,
                                   &textMetrics, boundingBoxType, ctx,
                                   &usedHyphenation, &transformedLastBreak,
                                   textStyle->WordCanWrap(), &breakPriority);
+  if (!length && !textMetrics.mAscent && !textMetrics.mDescent) {
+    // If we're measuring a zero-length piece of text, update
+    // the height manually.
+    nsIFontMetrics* fm = provider.GetFontMetrics();
+    if (fm) {
+      nscoord ascent, descent;
+      fm->GetMaxAscent(ascent);
+      fm->GetMaxDescent(descent);
+      textMetrics.mAscent = gfxFloat(ascent);
+      textMetrics.mDescent = gfxFloat(descent);
+    }
+  }
   // The "end" iterator points to the first character after the string mapped
   // by this frame. Basically, its original-string offset is offset+charsFit
   // after we've computed charsFit.
   gfxSkipCharsIterator end(provider.GetEndHint());
   end.SetSkippedOffset(transformedOffset + transformedCharsFit);
   PRInt32 charsFit = end.GetOriginalOffset() - offset;
   if (offset + charsFit == newLineOffset) {
     // We broke before a trailing preformatted '\n'. The newline should
@@ -6653,17 +6665,17 @@ nsTextFrame::ReflowText(nsLineLayout& aL
     textMetrics.mAscent = NS_MAX(gfxFloat(0.0), -textMetrics.mBoundingBox.Y());
     textMetrics.mDescent = NS_MAX(gfxFloat(0.0), textMetrics.mBoundingBox.YMost());
   }
 
   // Setup metrics for caller
   // Disallow negative widths
   aMetrics.width = NSToCoordCeil(NS_MAX(gfxFloat(0.0), textMetrics.mAdvanceWidth));
 
-  if (transformedCharsFit == 0 && !usedHyphenation) {
+  if (completedFirstLetter && transformedCharsFit == 0 && !usedHyphenation) {
     aMetrics.ascent = 0;
     aMetrics.height = 0;
   } else if (boundingBoxType != gfxFont::LOOSE_INK_EXTENTS) {
     // Use actual text metrics for floating first letter frame.
     aMetrics.ascent = NSToCoordCeil(textMetrics.mAscent);
     aMetrics.height = aMetrics.ascent + NSToCoordCeil(textMetrics.mDescent);
   } else {
     // Otherwise, ascent should contain the overline drawable area.
@@ -7237,16 +7249,22 @@ nsTextFrame::HasTerminalNewline() const
 }
 
 PRBool
 nsTextFrame::IsAtEndOfLine() const
 {
   return (GetStateBits() & TEXT_END_OF_LINE) != 0;
 }
 
+nscoord
+nsTextFrame::GetBaseline() const
+{
+  return mAscent;
+}
+
 PRBool
 nsTextFrame::HasAnyNoncollapsedCharacters()
 {
   gfxSkipCharsIterator iter = EnsureTextRun();
   PRInt32 offset = GetContentOffset(),
           offsetEnd = GetContentEnd();
   PRInt32 skippedOffset = iter.ConvertOriginalToSkipped(offset);
   PRInt32 skippedOffsetEnd = iter.ConvertOriginalToSkipped(offsetEnd);