Bug 1248708 - Part2.2: render -webkit-text-stroke property. r=jfkthame
authorJeremy Chen <jeremychen@mozilla.com>
Sat, 23 Apr 2016 01:40:39 +0800
changeset 332455 5f3ba1d0bbf4cbfe14050cf230c289fc71bb5ab3
parent 332454 500cb83c36269920989f23e6ad054a5d0db7ce13
child 332456 83a3c6e64690599b3ee426185c9d95efc51d16e3
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjfkthame
bugs1248708
milestone48.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 1248708 - Part2.2: render -webkit-text-stroke property. r=jfkthame
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
gfx/thebes/gfxTextRun.cpp
gfx/thebes/gfxTextRun.h
layout/base/nsPresContext.h
layout/generic/nsLineLayout.cpp
layout/generic/nsTextFrame.cpp
layout/generic/nsTextFrame.h
layout/style/nsStyleStruct.h
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -1674,27 +1674,43 @@ private:
                                           mFontParams.renderingOptions);
             } else {
                 mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf,
                                           ColorPattern(state.color),
                                           mFontParams.drawOptions,
                                           mFontParams.renderingOptions);
             }
         }
+        if ((mRunParams.drawMode &
+             (DrawMode::GLYPH_STROKE | DrawMode::GLYPH_STROKE_UNDERNEATH)) ==
+            DrawMode::GLYPH_STROKE) {
+            state.color = gfx::Color::FromABGR(mRunParams.textStrokeColor);
+            state.strokeOptions.mLineWidth = mRunParams.textStrokeWidth;
+            FlushStroke(buf, state);
+        }
         if (mRunParams.drawMode & DrawMode::GLYPH_PATH) {
             mRunParams.context->EnsurePathBuilder();
             Matrix mat = mRunParams.dt->GetTransform();
             mFontParams.scaledFont->CopyGlyphsToBuilder(
                 buf, mRunParams.context->mPathBuilder,
                 mRunParams.dt->GetBackendType(), &mat);
         }
 
         mNumGlyphs = 0;
     }
 
+    void FlushStroke(gfx::GlyphBuffer& aBuf, gfxContext::AzureState& aState)
+    {
+        RefPtr<Path> path =
+            mFontParams.scaledFont->GetPathForGlyphs(aBuf, mRunParams.dt);
+        mRunParams.dt->Stroke(path,
+                              ColorPattern(aState.color),
+                              aState.strokeOptions);
+    }
+
     Glyph        mGlyphBuffer[GLYPH_BUFFER_SIZE];
     unsigned int mNumGlyphs;
 
 #undef GLYPH_BUFFER_SIZE
 };
 
 // Bug 674909. When synthetic bolding text by drawing twice, need to
 // render using a pixel offset in device pixels, otherwise text
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -23,16 +23,17 @@
 #include "nsIObserver.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Attributes.h"
 #include <algorithm>
 #include "DrawMode.h"
 #include "nsDataHashtable.h"
 #include "harfbuzz/hb.h"
 #include "mozilla/gfx/2D.h"
+#include "nsColor.h"
 
 typedef struct _cairo cairo_t;
 typedef struct _cairo_scaled_font cairo_scaled_font_t;
 //typedef struct gr_face            gr_face;
 
 #ifdef DEBUG
 #include <stdio.h>
 #endif
@@ -2177,16 +2178,18 @@ struct TextRunDrawParams {
     RefPtr<mozilla::gfx::DrawTarget> dt;
     gfxContext              *context;
     gfxFont::Spacing        *spacing;
     gfxTextRunDrawCallbacks *callbacks;
     gfxTextContextPaint     *runContextPaint;
     mozilla::gfx::Color      fontSmoothingBGColor;
     gfxFloat                 direction;
     double                   devPerApp;
+    float                    textStrokeWidth;
+    nscolor                  textStrokeColor;
     DrawMode                 drawMode;
     bool                     isVerticalRun;
     bool                     isRTL;
     bool                     paintSVGGlyphs;
 };
 
 struct FontDrawParams {
     RefPtr<mozilla::gfx::ScaledFont>            scaledFont;
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -570,17 +570,17 @@ gfxTextRun::Draw(Range aRange, gfxPoint 
     NS_ASSERTION(aRange.end <= GetLength(), "Substring out of range");
     NS_ASSERTION(aParams.drawMode == DrawMode::GLYPH_PATH ||
                  !(aParams.drawMode & DrawMode::GLYPH_PATH),
                  "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH");
     NS_ASSERTION(aParams.drawMode == DrawMode::GLYPH_PATH || !aParams.callbacks,
                  "callback must not be specified unless using GLYPH_PATH");
 
     bool skipDrawing = mSkipDrawing;
-    if (aParams.drawMode == DrawMode::GLYPH_FILL) {
+    if (aParams.drawMode & DrawMode::GLYPH_FILL) {
         Color currentColor;
         if (aParams.context->GetDeviceColor(currentColor) &&
             currentColor.a == 0) {
             skipDrawing = true;
         }
     }
 
     gfxFloat direction = GetDirection();
@@ -600,17 +600,17 @@ gfxTextRun::Draw(Range aRange, gfxPoint 
     }
 
     // synthetic bolding draws glyphs twice ==> colors with opacity won't draw
     // correctly unless first drawn without alpha
     BufferAlphaColor syntheticBoldBuffer(aParams.context);
     Color currentColor;
     bool needToRestore = false;
 
-    if (aParams.drawMode == DrawMode::GLYPH_FILL &&
+    if (aParams.drawMode & DrawMode::GLYPH_FILL &&
         HasNonOpaqueNonTransparentColor(aParams.context, currentColor) &&
         HasSyntheticBoldOrColor(this, aRange)) {
         needToRestore = true;
         // measure text, use the bounding box
         gfxTextRun::Metrics metrics = MeasureText(
             aRange, gfxFont::LOOSE_INK_EXTENTS,
             aParams.context->GetDrawTarget(), aParams.provider);
         metrics.mBoundingBox.MoveBy(aPt);
@@ -621,16 +621,18 @@ gfxTextRun::Draw(Range aRange, gfxPoint 
     // Set up parameters that will be constant across all glyph runs we need
     // to draw, regardless of the font used.
     TextRunDrawParams params;
     params.context = aParams.context;
     params.devPerApp = 1.0 / double(GetAppUnitsPerDevUnit());
     params.isVerticalRun = IsVertical();
     params.isRTL = IsRightToLeft();
     params.direction = direction;
+    params.textStrokeWidth = aParams.textStrokeWidth;
+    params.textStrokeColor = aParams.textStrokeColor;
     params.drawMode = aParams.drawMode;
     params.callbacks = aParams.callbacks;
     params.runContextPaint = aParams.contextPaint;
     params.paintSVGGlyphs = !aParams.callbacks ||
         aParams.callbacks->mShouldPaintSVGGlyphs;
     params.dt = aParams.context->GetDrawTarget();
     params.fontSmoothingBGColor =
         aParams.context->GetFontSmoothingBackgroundColor();
@@ -640,17 +642,17 @@ gfxTextRun::Draw(Range aRange, gfxPoint 
 
     while (iter.NextRun()) {
         gfxFont *font = iter.GetGlyphRun()->mFont;
         uint32_t start = iter.GetStringStart();
         uint32_t end = iter.GetStringEnd();
         Range ligatureRange(start, end);
         ShrinkToLigatureBoundaries(&ligatureRange);
 
-        bool drawPartial = aParams.drawMode == DrawMode::GLYPH_FILL ||
+        bool drawPartial = (aParams.drawMode & DrawMode::GLYPH_FILL) ||
                            (aParams.drawMode == DrawMode::GLYPH_PATH &&
                             aParams.callbacks);
         gfxPoint origPt = aPt;
 
         if (drawPartial) {
             DrawPartialLigature(font, Range(start, ligatureRange.start),
                                 &aPt, aParams.provider, params,
                                 iter.GetGlyphRun()->mOrientation);
--- a/gfx/thebes/gfxTextRun.h
+++ b/gfx/thebes/gfxTextRun.h
@@ -14,16 +14,17 @@
 #include "gfxFontConstants.h"
 #include "nsTArray.h"
 #include "gfxSkipChars.h"
 #include "gfxPlatform.h"
 #include "mozilla/MemoryReporting.h"
 #include "DrawMode.h"
 #include "harfbuzz/hb.h"
 #include "nsUnicodeScriptCodes.h"
+#include "nsColor.h"
 
 #ifdef DEBUG
 #include <stdio.h>
 #endif
 
 class gfxContext;
 class gfxFontGroup;
 class gfxUserFontEntry;
@@ -231,16 +232,18 @@ public:
         gfxTextRun *mTextRun;
         uint32_t    mCurrentChar;
     };
 
     struct DrawParams
     {
         gfxContext* context;
         DrawMode drawMode = DrawMode::GLYPH_FILL;
+        nscolor textStrokeColor = 0;
+        float textStrokeWidth = 0.0f;
         PropertyProvider* provider = nullptr;
         // If non-null, the advance width of the substring is set.
         gfxFloat* advanceWidth = nullptr;
         gfxTextContextPaint* contextPaint = nullptr;
         gfxTextRunDrawCallbacks* callbacks = nullptr;
         explicit DrawParams(gfxContext* aContext) : context(aContext) {}
     };
 
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -637,16 +637,19 @@ public:
 
   nscoord DevPixelsToAppUnits(int32_t aPixels) const
   { return NSIntPixelsToAppUnits(aPixels, AppUnitsPerDevPixel()); }
 
   int32_t AppUnitsToDevPixels(nscoord aAppUnits) const
   { return NSAppUnitsToIntPixels(aAppUnits,
              float(AppUnitsPerDevPixel())); }
 
+  float AppUnitsToFloatDevPixels(nscoord aAppUnits)
+  { return aAppUnits / float(AppUnitsPerDevPixel()); }
+
   int32_t CSSPixelsToDevPixels(int32_t aPixels)
   { return AppUnitsToDevPixels(CSSPixelsToAppUnits(aPixels)); }
 
   float CSSPixelsToDevPixels(float aPixels)
   {
     return NSAppUnitsToFloatPixels(CSSPixelsToAppUnits(aPixels),
                                    float(AppUnitsPerDevPixel()));
   }
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -3346,25 +3346,27 @@ nsLineLayout::RelativePositionFrames(Per
     nsOverflowAreas r;
     if (pfd->mSpan) {
       // Compute a new combined area for the child span before
       // aggregating it into our combined area.
       RelativePositionFrames(pfd->mSpan, r);
     } else {
       r = pfd->mOverflowAreas;
       if (pfd->mIsTextFrame) {
-        // We need to recompute overflow areas in two cases:
+        // We need to recompute overflow areas in four cases:
         // (1) When PFD_RECOMPUTEOVERFLOW is set due to trimming
         // (2) When there are text decorations, since we can't recompute the
         //     overflow area until Reflow and VerticalAlignLine have finished
         // (3) When there are text emphasis marks, since the marks may be
         //     put further away if the text is inside ruby.
+        // (4) When there are text strokes
         if (pfd->mRecomputeOverflow ||
             frame->StyleContext()->HasTextDecorationLines() ||
-            frame->StyleText()->HasTextEmphasis()) {
+            frame->StyleText()->HasTextEmphasis() ||
+            frame->StyleText()->HasWebkitTextStroke()) {
           nsTextFrame* f = static_cast<nsTextFrame*>(frame);
           r = f->RecomputeOverflow(mBlockReflowState->frame);
         }
         frame->FinishAndStoreOverflow(r, frame->GetSize());
       }
 
       // If we have something that's not an inline but with a complex frame
       // hierarchy inside that contains views, they need to be
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -289,16 +289,33 @@ class nsTextPaintStyle {
 public:
   explicit nsTextPaintStyle(nsTextFrame* aFrame);
 
   void SetResolveColors(bool aResolveColors) {
     mResolveColors = aResolveColors;
   }
 
   nscolor GetTextColor();
+
+  // SVG text has its own painting process, so we should never get its stroke
+  // property from here.
+  nscolor GetWebkitTextStrokeColor() {
+    if (mFrame->IsSVGText()) {
+      return 0;
+    }
+    return mFrame->StyleContext()->GetTextStrokeColor();
+  }
+  float GetWebkitTextStrokeWidth() {
+    if (mFrame->IsSVGText()) {
+      return 0.0f;
+    }
+    nscoord coord = mFrame->StyleText()->mWebkitTextStrokeWidth.GetCoordValue();
+    return mFrame->PresContext()->AppUnitsToFloatDevPixels(coord);
+  }
+
   /**
    * Compute the colors for normally-selected text. Returns false if
    * the normal selection is not being displayed.
    */
   bool GetSelectionColors(nscolor* aForeColor,
                             nscolor* aBackColor);
   void GetHighlightColors(nscolor* aForeColor,
                           nscolor* aBackColor);
@@ -4805,18 +4822,19 @@ nsTextFrame::BuildDisplayList(nsDisplayL
                               const nsRect&           aDirtyRect,
                               const nsDisplayListSet& aLists)
 {
   if (!IsVisibleForPainting(aBuilder))
     return;
   
   DO_GLOBAL_REFLOW_COUNT_DSP("nsTextFrame");
 
-  bool isTextTransparent =
-    NS_GET_A(StyleContext()->GetTextFillColor()) == 0;
+  nsStyleContext* sc = StyleContext();
+  bool isTextTransparent = (NS_GET_A(sc->GetTextFillColor()) == 0) &&
+                           (NS_GET_A(sc->GetTextStrokeColor()) == 0);
   Maybe<bool> isSelected;
   if (((GetStateBits() & TEXT_NO_RENDERED_GLYPHS) ||
        (isTextTransparent && !StyleText()->HasTextShadow())) &&
       aBuilder->IsForPainting() && !IsSVGText()) {
     isSelected.emplace(IsSelected());
     if (!isSelected.value()) {
       TextDecorations textDecs;
       GetTextDecorations(PresContext(), eResolvedColors, textDecs);
@@ -5227,16 +5245,17 @@ void
 nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
                                      nsIFrame* aBlock,
                                      PropertyProvider& aProvider,
                                      nsRect* aVisualOverflowRect,
                                      bool aIncludeTextDecorations)
 {
   const WritingMode wm = GetWritingMode();
   bool verticalRun = mTextRun->IsVertical();
+  const gfxFloat appUnitsPerDevUnit = aPresContext->AppUnitsPerDevPixel();
 
   if (IsFloatingFirstLetterChild()) {
     bool inverted = wm.IsLineInverted();
     // The underline/overline drawable area must be contained in the overflow
     // rect when this is in floating first letter frame at *both* modes.
     // In this case, aBlock is the ::first-letter frame.
     uint8_t decorationStyle = aBlock->StyleContext()->
                                 StyleTextReset()->GetDecorationStyle();
@@ -5248,17 +5267,16 @@ nsTextFrame::UnionAdditionalOverflow(nsP
     }
     nsFontMetrics* fontMetrics = aProvider.GetFontMetrics();
     nscoord underlineOffset, underlineSize;
     fontMetrics->GetUnderline(underlineOffset, underlineSize);
     nscoord maxAscent = inverted ? fontMetrics->MaxDescent()
                                  : fontMetrics->MaxAscent();
 
     nsCSSRendering::DecorationRectParams params;
-    gfxFloat appUnitsPerDevUnit = aPresContext->AppUnitsPerDevPixel();
     Float gfxWidth =
       (verticalRun ? aVisualOverflowRect->height
                    : aVisualOverflowRect->width) /
       appUnitsPerDevUnit;
     params.lineSize = Size(gfxWidth, underlineSize / appUnitsPerDevUnit);
     params.ascent = gfxFloat(mAscent) / appUnitsPerDevUnit;
     params.style = decorationStyle;
     params.vertical = verticalRun;
@@ -5293,19 +5311,19 @@ nsTextFrame::UnionAdditionalOverflow(nsP
     // them
     TextDecorations textDecs;
     GetTextDecorations(aPresContext, eResolvedColors, textDecs);
     if (textDecs.HasDecorationLines()) {
       nscoord inflationMinFontSize =
         nsLayoutUtils::InflationMinFontSizeFor(aBlock);
 
       const nscoord measure = verticalDec ? GetSize().height : GetSize().width;
-      const gfxFloat app = aPresContext->AppUnitsPerDevPixel();
-      gfxFloat gfxWidth = measure / app;
-      gfxFloat ascent = gfxFloat(GetLogicalBaseline(parentWM)) / app;
+      gfxFloat gfxWidth = measure / appUnitsPerDevUnit;
+      gfxFloat ascent = gfxFloat(GetLogicalBaseline(parentWM))
+                          / appUnitsPerDevUnit;
       nscoord frameBStart = 0;
       if (parentWM.IsVerticalRL()) {
         frameBStart = GetSize().width;
         ascent = -ascent;
       }
       // The decoration-line offsets need to be reversed for sideways-lr mode,
       // so we will multiply the values from metrics by this factor.
       gfxFloat decorationOffsetDir = mTextRun->IsSidewaysLeft() ? -1.0 : 1.0;
@@ -5373,16 +5391,27 @@ nsTextFrame::UnionAdditionalOverflow(nsP
         verticalDec ? nsRect(topOrLeft, 0, bottomOrRight - topOrLeft, measure)
                     : nsRect(0, topOrLeft, measure, bottomOrRight - topOrLeft));
     }
 
     aVisualOverflowRect->UnionRect(*aVisualOverflowRect,
                                    UpdateTextEmphasis(parentWM, aProvider));
   }
 
+  // text-stroke overflows
+  nscoord textStrokeWidth = StyleText()->mWebkitTextStrokeWidth.GetCoordValue();
+  if (textStrokeWidth > 0) {
+    nsRect strokeRect = *aVisualOverflowRect;
+    strokeRect.x -= textStrokeWidth;
+    strokeRect.y -= textStrokeWidth;
+    strokeRect.width += textStrokeWidth;
+    strokeRect.height += textStrokeWidth;
+    aVisualOverflowRect->UnionRect(*aVisualOverflowRect, strokeRect);
+  }
+
   // Text-shadow overflows
   nsRect shadowRect =
     nsLayoutUtils::GetTextShadowRectsUnion(*aVisualOverflowRect, this);
   aVisualOverflowRect->UnionRect(*aVisualOverflowRect, shadowRect);
 
   // When this frame is not selected, the text-decoration area must be in
   // frame bounds.
   if (!IsSelected() ||
@@ -6051,16 +6080,18 @@ nsTextFrame::PaintTextWithSelectionColor
       shadowParams.textBaselinePt = textBaselinePt;
       shadowParams.foregroundColor = foreground;
       shadowParams.leftSideOffset = startEdge;
       PaintShadows(shadow, shadowParams);
     }
 
     // Draw text segment
     params.textColor = foreground;
+    params.textStrokeColor = aParams.textPaintStyle->GetWebkitTextStrokeColor();
+    params.textStrokeWidth = aParams.textPaintStyle->GetWebkitTextStrokeWidth();
     params.drawSoftHyphen = hyphenWidth > 0;
     DrawText(range, textBaselinePt, params);
     advance += hyphenWidth;
     iterator.UpdateWithAdvance(advance);
   }
   return true;
 }
 
@@ -6578,16 +6609,23 @@ nsTextFrame::PaintText(const PaintTextPa
 
   nscolor foregroundColor = textPaintStyle.GetTextColor();
   if (aOpacity != 1.0f) {
     gfx::Color gfxColor = gfx::Color::FromABGR(foregroundColor);
     gfxColor.a *= aOpacity;
     foregroundColor = gfxColor.ToABGR();
   }
 
+  nscolor textStrokeColor = textPaintStyle.GetWebkitTextStrokeColor();
+  if (aOpacity != 1.0f) {
+    gfx::Color gfxColor = gfx::Color::FromABGR(textStrokeColor);
+    gfxColor.a *= aOpacity;
+    textStrokeColor = gfxColor.ToABGR();
+  }
+
   range = Range(startOffset, startOffset + maxLength);
   if (!aParams.callbacks) {
     const nsStyleText* textStyle = StyleText();
     PaintShadowParams shadowParams(aParams);
     shadowParams.range = range;
     shadowParams.textBaselinePt = textBaselinePt;
     shadowParams.leftSideOffset = snappedStartEdge;
     shadowParams.provider = &provider;
@@ -6599,42 +6637,56 @@ nsTextFrame::PaintText(const PaintTextPa
   gfxFloat advanceWidth;
   DrawTextParams params(aParams.context);
   params.dirtyRect = aParams.dirtyRect;
   params.framePt = aParams.framePt;
   params.provider = &provider;
   params.advanceWidth = &advanceWidth;
   params.textStyle = &textPaintStyle;
   params.textColor = foregroundColor;
+  params.textStrokeColor = textStrokeColor;
+  params.textStrokeWidth = textPaintStyle.GetWebkitTextStrokeWidth();
   params.clipEdges = &clipEdges;
   params.drawSoftHyphen = (GetStateBits() & TEXT_HYPHEN_BREAK) != 0;
   params.contextPaint = aParams.contextPaint;
   params.callbacks = aParams.callbacks;
   DrawText(range, textBaselinePt, params);
 }
 
 static void
 DrawTextRun(gfxTextRun* aTextRun,
             const gfxPoint& aTextBaselinePt,
             gfxTextRun::Range aRange,
             const nsTextFrame::DrawTextRunParams& aParams)
 {
   gfxTextRun::DrawParams params(aParams.context);
-  params.drawMode = aParams.callbacks ? DrawMode::GLYPH_PATH
-                                      : DrawMode::GLYPH_FILL;
   params.provider = aParams.provider;
   params.advanceWidth = aParams.advanceWidth;
   params.contextPaint = aParams.contextPaint;
   params.callbacks = aParams.callbacks;
   if (aParams.callbacks) {
     aParams.callbacks->NotifyBeforeText(aParams.textColor);
+    params.drawMode = DrawMode::GLYPH_PATH;
     aTextRun->Draw(aRange, aTextBaselinePt, params);
     aParams.callbacks->NotifyAfterText();
   } else {
-    aParams.context->SetColor(Color::FromABGR(aParams.textColor));
+    if (NS_GET_A(aParams.textColor) != 0) {
+      // Default drawMode is DrawMode::GLYPH_FILL
+      aParams.context->SetColor(Color::FromABGR(aParams.textColor));
+    } else {
+      params.drawMode = DrawMode::GLYPH_STROKE;
+    }
+
+    if (NS_GET_A(aParams.textStrokeColor) != 0 &&
+        aParams.textStrokeWidth != 0.0f) {
+      params.drawMode |= DrawMode::GLYPH_STROKE;
+      params.textStrokeWidth = aParams.textStrokeWidth;
+      params.textStrokeColor = aParams.textStrokeColor;
+    }
+
     aTextRun->Draw(aRange, aTextBaselinePt, params);
   }
 }
 
 void
 nsTextFrame::DrawTextRun(Range aRange, const gfxPoint& aTextBaselinePt,
                          const DrawTextRunParams& aParams)
 {
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -408,16 +408,18 @@ public:
   struct DrawTextRunParams
   {
     gfxContext* context;
     PropertyProvider* provider = nullptr;
     gfxFloat* advanceWidth = nullptr;
     gfxTextContextPaint* contextPaint = nullptr;
     DrawPathCallbacks* callbacks = nullptr;
     nscolor textColor = NS_RGBA(0, 0, 0, 0);
+    nscolor textStrokeColor = NS_RGBA(0, 0, 0, 0);
+    float textStrokeWidth = 0.0f;
     bool drawSoftHyphen = false;
     explicit DrawTextRunParams(gfxContext* aContext)
       : context(aContext) {}
   };
 
   struct DrawTextParams : DrawTextRunParams
   {
     gfxPoint framePt;
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -2085,16 +2085,20 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
     return WhiteSpaceCanWrapStyle() &&
            mWordWrap == NS_STYLE_WORDWRAP_BREAK_WORD;
   }
 
   bool HasTextEmphasis() const {
     return !mTextEmphasisStyleString.IsEmpty();
   }
 
+  bool HasWebkitTextStroke() const {
+    return mWebkitTextStrokeWidth.GetCoordValue() > 0;
+  }
+
   // These are defined in nsStyleStructInlines.h.
   inline bool HasTextShadow() const;
   inline nsCSSShadowArray* GetTextShadow() const;
 
   // The aContextFrame argument on each of these is the frame this
   // style struct is for.  If the frame is for SVG text or inside ruby,
   // the return value will be massaged to be something that makes sense
   // for those cases.