Implement text-shadow for the text-overflow marker text (ellipsis) r=roc
authorMichael Ventnor <mventnor@mozilla.com>
Fri, 01 Jul 2011 16:43:11 +1000
changeset 72117 2fba916e056cdb24bd569a6b8ab9ea69b90c7c25
parent 72116 592403064072fe729609a03d4d4eed7c47e16fff
child 72118 e8023ac96fb770f73baeb3a985b64388f7a2c184
push id20656
push usermventnor@mozilla.com
push dateFri, 01 Jul 2011 06:43:51 +0000
treeherdermozilla-central@2fba916e056c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
milestone7.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
Implement text-shadow for the text-overflow marker text (ellipsis) r=roc
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/generic/TextOverflow.cpp
layout/reftests/text-overflow/marker-shadow-notref.html
layout/reftests/text-overflow/marker-shadow.html
layout/reftests/text-overflow/reftest.list
layout/xul/base/src/nsTextBoxFrame.cpp
layout/xul/base/src/nsTextBoxFrame.h
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -2884,16 +2884,71 @@ nsLayoutUtils::GetStringWidth(const nsIF
                                          direction, presContext, *aContext);
     }
   }
 #endif // IBMBIDI
   aContext->SetTextRunRTL(PR_FALSE);
   return aContext->GetWidth(aString, aLength);
 }
 
+/* static */ void
+nsLayoutUtils::PaintTextShadow(const nsIFrame* aFrame,
+                               nsRenderingContext* aContext,
+                               const nsRect& aTextRect,
+                               const nsRect& aDirtyRect,
+                               const nscolor& aForegroundColor,
+                               TextShadowCallback aCallback,
+                               void* aCallbackData)
+{
+  const nsStyleText* textStyle = aFrame->GetStyleText();
+  if (!textStyle->mTextShadow)
+    return;
+
+  // Text shadow happens with the last value being painted at the back,
+  // ie. it is painted first.
+  gfxContext* aDestCtx = aContext->ThebesContext();
+  for (PRUint32 i = textStyle->mTextShadow->Length(); i > 0; --i) {
+    nsCSSShadowItem* shadowDetails = textStyle->mTextShadow->ShadowAt(i - 1);
+    nsPoint shadowOffset(shadowDetails->mXOffset,
+                         shadowDetails->mYOffset);
+    nscoord blurRadius = NS_MAX(shadowDetails->mRadius, 0);
+
+    nsRect shadowRect(aTextRect);
+    shadowRect.MoveBy(shadowOffset);
+
+    nsPresContext* presCtx = aFrame->PresContext();
+    nsContextBoxBlur contextBoxBlur;
+    gfxContext* shadowContext = contextBoxBlur.Init(shadowRect, 0, blurRadius,
+                                                    presCtx->AppUnitsPerDevPixel(),
+                                                    aDestCtx, aDirtyRect, nsnull);
+    if (!shadowContext)
+      continue;
+
+    nscolor shadowColor;
+    if (shadowDetails->mHasColor)
+      shadowColor = shadowDetails->mColor;
+    else
+      shadowColor = aForegroundColor;
+
+    // Conjure an nsRenderingContext from a gfxContext for drawing the text
+    nsRefPtr<nsRenderingContext> renderingContext = new nsRenderingContext();
+    renderingContext->Init(presCtx->DeviceContext(), shadowContext);
+
+    aDestCtx->Save();
+    aDestCtx->NewPath();
+    aDestCtx->SetColor(gfxRGBA(shadowColor));
+
+    // The callback will draw whatever we want to blur as a shadow.
+    aCallback(renderingContext, shadowOffset, shadowColor, aCallbackData);
+
+    contextBoxBlur.DoPaint();
+    aDestCtx->Restore();
+  }
+}
+
 /* static */ nscoord
 nsLayoutUtils::GetCenteredFontBaseline(nsFontMetrics* aFontMetrics,
                                        nscoord         aLineHeight)
 {
   nscoord fontAscent = aFontMetrics->MaxAscent();
   nscoord fontHeight = aFontMetrics->MaxHeight();
 
   nscoord leading = aLineHeight - fontHeight;
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -942,16 +942,33 @@ public:
                          PRUint8              aDirection = NS_STYLE_DIRECTION_INHERIT);
 
   static nscoord GetStringWidth(const nsIFrame*      aFrame,
                                 nsRenderingContext* aContext,
                                 const PRUnichar*     aString,
                                 PRInt32              aLength);
 
   /**
+   * Helper function for drawing text-shadow. The callback's job
+   * is to draw whatever needs to be blurred onto the given context.
+   */
+  typedef void (* TextShadowCallback)(nsRenderingContext* aCtx,
+                                      nsPoint aShadowOffset,
+                                      const nscolor& aShadowColor,
+                                      void* aData);
+
+  static void PaintTextShadow(const nsIFrame*     aFrame,
+                              nsRenderingContext* aContext,
+                              const nsRect&       aTextRect,
+                              const nsRect&       aDirtyRect,
+                              const nscolor&      aForegroundColor,
+                              TextShadowCallback  aCallback,
+                              void*               aCallbackData);
+
+  /**
    * Gets the baseline to vertically center text from a font within a
    * line of specified height.
    *
    * Returns the baseline position relative to the top of the line.
    */
   static nscoord GetCenteredFontBaseline(nsFontMetrics* aFontMetrics,
                                          nscoord         aLineHeight);
 
--- a/layout/generic/TextOverflow.cpp
+++ b/layout/generic/TextOverflow.cpp
@@ -16,16 +16,17 @@
  * The Original Code is an implementation of CSS3 text-overflow.
  *
  * The Initial Developer of the Original Code is the Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2011
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Mats Palmgren <matspal@gmail.com> (original author)
+ *   Michael Ventnor <m.ventnor@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -199,38 +200,68 @@ public:
     MOZ_COUNT_CTOR(nsDisplayTextOverflowMarker);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayTextOverflowMarker() {
     MOZ_COUNT_DTOR(nsDisplayTextOverflowMarker);
   }
 #endif
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) {
-    return mRect;
+    nsRect shadowRect =
+      nsLayoutUtils::GetTextShadowRectsUnion(mRect, mFrame);
+    return mRect.Union(shadowRect);
   }
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsRenderingContext* aCtx);
+  void PaintTextToContext(nsRenderingContext* aCtx,
+                          nsPoint aOffsetFromRect);
   NS_DISPLAY_DECL_NAME("TextOverflow", TYPE_TEXT_OVERFLOW)
 private:
   nsRect          mRect;   // in reference frame coordinates
   const nsString  mString; // the marker text
   nscoord         mAscent; // baseline for the marker text in mRect
 };
 
+static void
+PaintTextShadowCallback(nsRenderingContext* aCtx,
+                        nsPoint aShadowOffset,
+                        const nscolor& aShadowColor,
+                        void* aData)
+{
+  reinterpret_cast<nsDisplayTextOverflowMarker*>(aData)->
+           PaintTextToContext(aCtx, aShadowOffset);
+}
+
 void
 nsDisplayTextOverflowMarker::Paint(nsDisplayListBuilder* aBuilder,
                                    nsRenderingContext*   aCtx)
 {
+  nscolor foregroundColor = nsLayoutUtils::GetTextColor(mFrame);
+
+  // Paint the text-shadows for the overflow marker
+  nsLayoutUtils::PaintTextShadow(mFrame, aCtx, mRect, mVisibleRect,
+                                 foregroundColor, PaintTextShadowCallback,
+                                 (void*)this);
+
+  aCtx->SetColor(foregroundColor);
+  PaintTextToContext(aCtx, nsPoint(0, 0));
+}
+
+void
+nsDisplayTextOverflowMarker::PaintTextToContext(nsRenderingContext* aCtx,
+                                                nsPoint aOffsetFromRect)
+{
   nsStyleContext* sc = mFrame->GetStyleContext();
   nsLayoutUtils::SetFontFromStyle(aCtx, sc);
-  aCtx->SetColor(nsLayoutUtils::GetTextColor(mFrame));
+
   nsPoint baselinePt = mRect.TopLeft();
   baselinePt.y += mAscent;
-  nsLayoutUtils::DrawString(mFrame, aCtx, mString.get(), mString.Length(),
-                            baselinePt);
+
+  nsLayoutUtils::DrawString(mFrame, aCtx, mString.get(),
+                            mString.Length(), baselinePt + aOffsetFromRect);
 }
 
 /* static */ TextOverflow*
 TextOverflow::WillProcessLines(nsDisplayListBuilder*   aBuilder,
                                const nsDisplayListSet& aLists,
                                nsIFrame*               aBlockFrame)
 {
   if (!CanHaveTextOverflow(aBuilder, aBlockFrame)) {
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text-overflow/marker-shadow-notref.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<!--
+    Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/licenses/publicdomain/
+-->
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<style type="text/css">
+@font-face {
+  font-family: DejaVuSansMono;
+  src: url(../fonts/DejaVuSansMono.woff);
+}
+
+div {
+  font-family: DejaVuSansMono;
+  width: 60px;
+  overflow: hidden;
+  white-space: nowrap;
+  font-size: 14px;
+  text-overflow: "...";
+}
+</style>
+</head>
+<body>
+<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+&nbsp;&nbsp;&nbsp;&nbsp;a</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text-overflow/marker-shadow.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<!--
+    Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/licenses/publicdomain/
+
+    Test: Marker should have text-shadow applied to it
+-->
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<style type="text/css">
+@font-face {
+  font-family: DejaVuSansMono;
+  src: url(../fonts/DejaVuSansMono.woff);
+}
+
+div {
+  font-family: DejaVuSansMono;
+  width: 60px;
+  overflow: hidden;
+  white-space: nowrap;
+  font-size: 14px;
+  text-overflow: "...";
+  text-shadow: 0px 2px 2px red;
+}
+</style>
+</head>
+<body>
+<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+&nbsp;&nbsp;&nbsp;&nbsp;a</div>
+</body>
+</html>
--- a/layout/reftests/text-overflow/reftest.list
+++ b/layout/reftests/text-overflow/reftest.list
@@ -8,8 +8,9 @@ HTTP(..) == anonymous-block.html anonymo
 HTTP(..) == false-marker-overlap.html false-marker-overlap-ref.html
 HTTP(..) == visibility-hidden.html visibility-hidden-ref.html
 HTTP(..) == block-padding.html block-padding-ref.html
 HTTP(..) == quirks-decorations.html quirks-decorations-ref.html
 HTTP(..) == quirks-line-height.html quirks-line-height-ref.html
 HTTP(..) == standards-decorations.html standards-decorations-ref.html
 HTTP(..) == standards-line-height.html standards-line-height-ref.html
 HTTP(..) == selection.html selection-ref.html
+HTTP(..) != marker-shadow.html marker-shadow-notref.html
--- a/layout/xul/base/src/nsTextBoxFrame.cpp
+++ b/layout/xul/base/src/nsTextBoxFrame.cpp
@@ -342,27 +342,56 @@ public:
                      nsRenderingContext* aCtx);
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder);
   NS_DISPLAY_DECL_NAME("XULTextBox", TYPE_XUL_TEXT_BOX)
 
   virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder);
 
   virtual void DisableComponentAlpha() { mDisableSubpixelAA = PR_TRUE; }
 
+  void PaintTextWithOffset(nsRenderingContext* aCtx,
+                           nsPoint aOffset);
+
   PRPackedBool mDisableSubpixelAA;
 };
 
+static void
+PaintTextShadowCallback(nsRenderingContext* aCtx,
+                        nsPoint aShadowOffset,
+                        const nscolor& aShadowColor,
+                        void* aData)
+{
+  reinterpret_cast<nsDisplayXULTextBox*>(aData)->
+           PaintTextWithOffset(aCtx, aShadowOffset);
+}
+
 void
 nsDisplayXULTextBox::Paint(nsDisplayListBuilder* aBuilder,
                            nsRenderingContext* aCtx)
 {
   gfxContextAutoDisableSubpixelAntialiasing disable(aCtx->ThebesContext(),
                                                     mDisableSubpixelAA);
+
+  // Paint the text shadow before doing any foreground stuff
+  nsRect drawRect = static_cast<nsTextBoxFrame*>(mFrame)->mTextDrawRect;
+  nsLayoutUtils::PaintTextShadow(mFrame, aCtx,
+                                 drawRect, mVisibleRect,
+                                 mFrame->GetStyleColor()->mColor,
+                                 PaintTextShadowCallback,
+                                 (void*)this);
+
+  PaintTextWithOffset(aCtx, nsPoint(0, 0));
+}
+
+void
+nsDisplayXULTextBox::PaintTextWithOffset(nsRenderingContext* aCtx,
+                                         nsPoint aOffset)
+{
   static_cast<nsTextBoxFrame*>(mFrame)->
-    PaintTitle(*aCtx, mVisibleRect, ToReferenceFrame());
+    PaintTitle(*aCtx, mVisibleRect, ToReferenceFrame() + aOffset);
 }
 
 nsRect
 nsDisplayXULTextBox::GetBounds(nsDisplayListBuilder* aBuilder) {
   return mFrame->GetVisualOverflowRect() + ToReferenceFrame();
 }
 
 nsRect
@@ -390,33 +419,17 @@ nsTextBoxFrame::BuildDisplayList(nsDispl
 void
 nsTextBoxFrame::PaintTitle(nsRenderingContext& aRenderingContext,
                            const nsRect&        aDirtyRect,
                            nsPoint              aPt)
 {
     if (mTitle.IsEmpty())
         return;
 
-    nsRect textRect = mTextDrawRect + aPt;
-
-    // Paint the text shadow before doing any foreground stuff
-    const nsStyleText* textStyle = GetStyleText();
-    if (textStyle->mTextShadow) {
-      // Text shadow happens with the last value being painted at the back,
-      // ie. it is painted first.
-      for (PRUint32 i = textStyle->mTextShadow->Length(); i > 0; --i) {
-        PaintOneShadow(aRenderingContext.ThebesContext(),
-                       textRect,
-                       textStyle->mTextShadow->ShadowAt(i - 1),
-                       GetStyleColor()->mColor,
-                       aDirtyRect);
-      }
-    }
-
-    DrawText(aRenderingContext, textRect, nsnull);
+    DrawText(aRenderingContext, mTextDrawRect + aPt, nsnull);
 }
 
 void
 nsTextBoxFrame::DrawText(nsRenderingContext& aRenderingContext,
                          const nsRect&        aTextRect,
                          const nscolor*       aOverrideColor)
 {
     nsPresContext* presContext = PresContext();
@@ -604,59 +617,16 @@ nsTextBoxFrame::DrawText(nsRenderingCont
       gfxFloat sizePixel = presContext->AppUnitsToGfxUnits(size);
       nsCSSRendering::PaintDecorationLine(ctx, strikeColor,
                         pt, gfxSize(width, sizePixel), ascentPixel, offsetPixel,
                         NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH,
                         strikeStyle);
     }
 }
 
-void nsTextBoxFrame::PaintOneShadow(gfxContext*      aCtx,
-                                    const nsRect&    aTextRect,
-                                    nsCSSShadowItem* aShadowDetails,
-                                    const nscolor&   aForegroundColor,
-                                    const nsRect&    aDirtyRect) {
-  nsPoint shadowOffset(aShadowDetails->mXOffset,
-                       aShadowDetails->mYOffset);
-  nscoord blurRadius = NS_MAX(aShadowDetails->mRadius, 0);
-
-  nsRect shadowRect(aTextRect);
-  shadowRect.MoveBy(shadowOffset);
-
-  nsContextBoxBlur contextBoxBlur;
-  gfxContext* shadowContext = contextBoxBlur.Init(shadowRect, 0, blurRadius,
-                                                  PresContext()->AppUnitsPerDevPixel(),
-                                                  aCtx, aDirtyRect, nsnull);
-
-  if (!shadowContext)
-    return;
-
-  nscolor shadowColor;
-  if (aShadowDetails->mHasColor)
-    shadowColor = aShadowDetails->mColor;
-  else
-    shadowColor = aForegroundColor;
-
-  // Conjure an nsRenderingContext from a gfxContext for DrawText
-  nsRefPtr<nsRenderingContext> renderingContext = new nsRenderingContext();
-  renderingContext->Init(PresContext()->DeviceContext(), shadowContext);
-
-  aCtx->Save();
-  aCtx->NewPath();
-  aCtx->SetColor(gfxRGBA(shadowColor));
-
-  // Draw the text onto our alpha-only surface to capture the alpha
-  // values.  Remember that the box blur context has a device offset
-  // on it, so we don't need to translate any coordinates to fit on
-  // the surface.
-  DrawText(*renderingContext, shadowRect, &shadowColor);
-  contextBoxBlur.DoPaint();
-  aCtx->Restore();
-}
-
 void
 nsTextBoxFrame::CalculateUnderline(nsRenderingContext& aRenderingContext)
 {
     if (mAccessKeyInfo && mAccessKeyInfo->mAccesskeyIndex != kNotFound) {
          // Calculate all fields of mAccessKeyInfo which
          // are the same for both BiDi and non-BiDi frames.
          const PRUnichar *titleString = mCroppedTitle.get();
          aRenderingContext.SetTextRunRTL(PR_FALSE);
--- a/layout/xul/base/src/nsTextBoxFrame.h
+++ b/layout/xul/base/src/nsTextBoxFrame.h
@@ -89,16 +89,17 @@ public:
                   nsPoint              aPt);
 
   nsRect GetComponentAlphaBounds();
 
   virtual PRBool ComputesOwnOverflowArea();
 
 protected:
   friend class nsAsyncAccesskeyUpdate;
+  friend class nsDisplayXULTextBox;
   // Should be called only by nsAsyncAccesskeyUpdate.
   // Returns PR_TRUE if accesskey was updated.
   PRBool UpdateAccesskey(nsWeakFrame& aWeakThis);
   void UpdateAccessTitle();
   void UpdateAccessIndex();
 
   // REVIEW: SORRY! Couldn't resist devirtualizing these
   void LayoutTitle(nsPresContext*      aPresContext,
@@ -129,22 +130,16 @@ private:
 
   PRBool AlwaysAppendAccessKey();
   PRBool InsertSeparatorBeforeAccessKey();
 
   void DrawText(nsRenderingContext& aRenderingContext,
                          const nsRect&        aTextRect,
                          const nscolor*       aOverrideColor);
 
-  void PaintOneShadow(gfxContext *     aCtx,
-                      const nsRect&    aTextRect,
-                      nsCSSShadowItem* aShadowDetails,
-                      const nscolor&   aForegroundColor,
-                      const nsRect&    aDirtyRect);
-
   nsString mTitle;
   nsString mCroppedTitle;
   nsString mAccessKey;
   nsSize mTextSize;
   nsRect mTextDrawRect;
   nsAccessKeyInfo* mAccessKeyInfo;
 
   CroppingStyle mCropType;