Bug 719286 - Add new gfxTextObjectPaint paint wrapper for use with SVG glyphs r=roc
authorEdwin Flores <eflores@mozilla.com>
Thu, 06 Sep 2012 16:58:46 +1200
changeset 111295 a0c93525cfba43767f05865415ed8c786afa39be
parent 111294 1ea9c7730f9754bf321d1d9365bffee3de06bf35
child 111296 c4b0ce0436fdaf551845a7749b790fc811af7d9b
push idunknown
push userunknown
push dateunknown
reviewersroc
bugs719286
milestone18.0a1
Bug 719286 - Add new gfxTextObjectPaint paint wrapper for use with SVG glyphs r=roc
content/base/public/nsContentUtils.h
content/base/src/nsContentUtils.cpp
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
gfx/thebes/gfxSVGGlyphs.cpp
gfx/thebes/gfxSVGGlyphs.h
layout/svg/base/src/nsSVGGeometryFrame.cpp
layout/svg/base/src/nsSVGGlyphFrame.cpp
layout/svg/base/src/nsSVGGlyphFrame.h
layout/svg/base/src/nsSVGPathGeometryFrame.cpp
layout/svg/base/src/nsSVGUtils.cpp
layout/svg/base/src/nsSVGUtils.h
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -42,16 +42,17 @@ static fp_except_t oldmask = fpsetmask(~
 #include "nsHtml5StringParser.h"
 #include "nsIDocument.h"
 #include "nsContentSink.h"
 #include "nsMathUtils.h"
 #include "nsThreadUtils.h"
 #include "nsIContent.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "gfxContext.h"
+#include "gfxFont.h"
 
 #include "mozilla/AutoRestore.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/TimeStamp.h"
 
 struct nsNativeKeyEvent; // Don't include nsINativeKeyBindings.h here: it will force strange compilation error!
 
 class nsIDOMScriptObjectFactory;
@@ -109,16 +110,17 @@ class nsIMIMEHeaderParam;
 class nsIObserver;
 class nsPresContext;
 class nsIChannel;
 class nsAutoScriptBlockerSuppressNodeRemoved;
 struct nsIntMargin;
 class nsPIDOMWindow;
 class nsIDocumentLoaderFactory;
 class nsIDOMHTMLInputElement;
+class gfxTextObjectPaint;
 
 namespace mozilla {
 
 class Selection;
 
 namespace layers {
   class LayerManager;
 } // namespace layers
@@ -2079,17 +2081,19 @@ public:
    */
   static void GetSelectionInTextControl(mozilla::Selection* aSelection,
                                         Element* aRoot,
                                         int32_t& aOutStartOffset,
                                         int32_t& aOutEndOffset);
 
   static nsIEditor* GetHTMLEditor(nsPresContext* aPresContext);
 
-  static bool PaintSVGGlyph(Element *aElement, gfxContext *aContext);
+  static bool PaintSVGGlyph(Element *aElement, gfxContext *aContext,
+                            gfxFont::DrawMode aDrawMode,
+                            gfxTextObjectPaint *aObjectPaint);
 
 private:
   static bool InitializeEventTable();
 
   static nsresult EnsureStringBundle(PropertiesFile aFile);
 
   static bool CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
                                 nsIPrincipal* aPrincipal);
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -120,16 +120,17 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_
 #include "nsParserConstants.h"
 #include "nsIWebNavigation.h"
 #include "nsILoadContext.h"
 #include "nsTextFragment.h"
 #include "mozilla/Selection.h"
 #include "nsSVGUtils.h"
 #include "nsISVGChildFrame.h"
 #include "nsRenderingContext.h"
+#include "gfxSVGGlyphs.h"
 
 #ifdef IBMBIDI
 #include "nsIBidiKeyboard.h"
 #endif
 #include "nsCycleCollectionParticipant.h"
 
 // for ReportToConsole
 #include "nsIStringBundle.h"
@@ -6945,32 +6946,36 @@ nsContentUtils::JSArrayToAtomArray(JSCon
     }
     aRetVal.AppendObject(a);
   }
   return NS_OK;
 }
 
 /* static */
 bool
-nsContentUtils::PaintSVGGlyph(Element *aElement, gfxContext *aContext) {
+nsContentUtils::PaintSVGGlyph(Element *aElement, gfxContext *aContext,
+                              gfxFont::DrawMode aDrawMode,
+                              gfxTextObjectPaint *aObjectPaint)
+{
   nsIFrame *frame = aElement->GetPrimaryFrame();
   if (!frame) {
     NS_WARNING("No frame for SVG glyph");
     return false;
   }
 
   nsISVGChildFrame *displayFrame = do_QueryFrame(frame);
   if (!displayFrame) {
     NS_WARNING("Non SVG frame for SVG glyph");
     return false;
   }
 
   nsRenderingContext context;
 
   context.Init(frame->PresContext()->DeviceContext(), aContext);
+  context.AddUserData(&gfxTextObjectPaint::sUserDataKey, aObjectPaint, nullptr);
 
   nsresult rv = displayFrame->PaintSVG(&context, nullptr);
   NS_ENSURE_SUCCESS(rv, false);
 
   return true;
 }
 
 // static
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -207,20 +207,21 @@ bool
 gfxFontEntry::HasSVGGlyph(uint32_t aGlyphId)
 {
     NS_ASSERTION(mSVGInitialized, "SVG data has not yet been loaded. TryGetSVGData() first.");
     return mSVGGlyphs->HasSVGGlyph(aGlyphId);
 }
 
 bool
 gfxFontEntry::RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId,
-                             int aDrawMode)
+                             int aDrawMode, gfxTextObjectPaint *aObjectPaint)
 {
     NS_ASSERTION(mSVGInitialized, "SVG data has not yet been loaded. TryGetSVGData() first.");
-    return mSVGGlyphs->RenderGlyph(aContext, aGlyphId, gfxFont::DrawMode(aDrawMode));
+    return mSVGGlyphs->RenderGlyph(aContext, aGlyphId, gfxFont::DrawMode(aDrawMode),
+                                   aObjectPaint);
 }
 
 bool
 gfxFontEntry::TryGetSVGData()
 {
     if (!mSVGInitialized) {
         mSVGInitialized = true;
 
@@ -1498,19 +1499,19 @@ struct GlyphBuffer {
 
     GlyphBuffer()
         : mNumGlyphs(0) { }
 
     cairo_glyph_t *AppendGlyph() {
         return &mGlyphBuffer[mNumGlyphs++];
     }
 
-    void Flush(cairo_t *aCR, cairo_pattern_t *aStrokePattern,
-               gfxFont::DrawMode aDrawMode, bool aReverse,
-               bool aFinish = false) {
+    void Flush(cairo_t *aCR, gfxFont::DrawMode aDrawMode, bool aReverse,
+               gfxTextObjectPaint *aObjectPaint,
+               const gfxMatrix& aGlobalMatrix, bool aFinish = false) {
         // Ensure there's enough room for a glyph to be added to the buffer
         // and we actually have glyphs to draw
         if ((!aFinish && mNumGlyphs < GLYPH_BUFFER_SIZE) || !mNumGlyphs) {
             return;
         }
 
         if (aReverse) {
             for (uint32_t i = 0; i < mNumGlyphs/2; ++i) {
@@ -1520,30 +1521,47 @@ struct GlyphBuffer {
             }
         }
 
         if (aDrawMode == gfxFont::GLYPH_PATH) {
             cairo_glyph_path(aCR, mGlyphBuffer, mNumGlyphs);
         } else {
             if (aDrawMode & gfxFont::GLYPH_FILL) {
                 SAMPLE_LABEL("GlyphBuffer", "cairo_show_glyphs");
+                nsRefPtr<gfxPattern> pattern;
+                if (aObjectPaint && !!(pattern = aObjectPaint->GetFillPattern())) {
+                    gfxMatrix matrix = pattern->GetMatrix().PreMultiply(aGlobalMatrix);
+                    pattern->SetMatrix(matrix);
+
+                    cairo_save(aCR);
+                    cairo_set_source(aCR, pattern->CairoPattern());
+                }
+
                 cairo_show_glyphs(aCR, mGlyphBuffer, mNumGlyphs);
+
+                if (pattern) {
+                    cairo_restore(aCR);
+                }
             }
 
             if (aDrawMode & gfxFont::GLYPH_STROKE) {
-                if (aStrokePattern) {
+                nsRefPtr<gfxPattern> pattern;
+                if (aObjectPaint && !!(pattern = aObjectPaint->GetStrokePattern())) {
+                    gfxMatrix matrix = pattern->GetMatrix().PreMultiply(aGlobalMatrix);
+                    pattern->SetMatrix(matrix);
+
                     cairo_save(aCR);
-                    cairo_set_source(aCR, aStrokePattern);
+                    cairo_set_source(aCR, pattern->CairoPattern());
                 }
 
                 cairo_new_path(aCR);
                 cairo_glyph_path(aCR, mGlyphBuffer, mNumGlyphs);
                 cairo_stroke(aCR);
 
-                if (aStrokePattern) {
+                if (pattern) {
                     cairo_restore(aCR);
                 }
             }
         }
 
         mNumGlyphs = 0;
     }
 #undef GLYPH_BUFFER_SIZE
@@ -1659,30 +1677,38 @@ gfxFont::CalcXScale(gfxContext *aContext
 
     // scale factor so that offsets are 1px in device pixels
     return 1.0 / m;
 }
 
 void
 gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
               gfxContext *aContext, DrawMode aDrawMode, gfxPoint *aPt,
-              Spacing *aSpacing, gfxPattern *aStrokePattern)
+              Spacing *aSpacing, gfxTextObjectPaint *aObjectPaint)
 {
     NS_ASSERTION(aDrawMode <= gfxFont::GLYPH_PATH, "GLYPH_PATH cannot be used with GLYPH_FILL or GLYPH_STROKE");
 
     if (aStart >= aEnd)
         return;
 
     const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
     const uint32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
     const double devUnitsPerAppUnit = 1.0/double(appUnitsPerDevUnit);
     bool isRTL = aTextRun->IsRightToLeft();
     double direction = aTextRun->GetDirection();
 
     bool haveSVGGlyphs = GetFontEntry()->TryGetSVGData();
+    nsAutoPtr<gfxTextObjectPaint> objectPaint;
+    if (haveSVGGlyphs && !aObjectPaint) {
+        // If no pattern is specified for fill, use the current pattern
+        NS_ASSERTION((aDrawMode & GLYPH_STROKE) == 0, "no pattern supplied for stroking text");
+        nsRefPtr<gfxPattern> fillPattern = aContext->GetPattern();
+        objectPaint = new SimpleTextObjectPaint(fillPattern, nullptr);
+        aObjectPaint = objectPaint;
+    }
 
     // synthetic-bold strikes are each offset one device pixel in run direction
     // (these values are only needed if IsSyntheticBold() is true)
     double synBoldOnePixelOffset = 0;
     int32_t strikes = 0;
     if (IsSyntheticBold()) {
         double xscale = CalcXScale(aContext);
         synBoldOnePixelOffset = direction * xscale;
@@ -1691,31 +1717,20 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint
     }
 
     uint32_t i;
     // Current position in appunits
     double x = aPt->x;
     double y = aPt->y;
 
     cairo_t *cr = aContext->GetCairo();
+    gfxMatrix globalMatrix = aContext->CurrentMatrix();
     RefPtr<DrawTarget> dt = aContext->GetDrawTarget();
 
     if (aContext->IsCairo()) {
-      cairo_pattern_t *strokePattern = nullptr;
-
-      // Multiply the stroke matrix by the context matrix since cairo multiplies
-      // the pattern matrix by the inverse of the context matrix whenever the
-      // pattern is set
-      gfxMatrix strokeMatrix;
-      if (aStrokePattern) {
-          strokeMatrix = aStrokePattern->GetMatrix();
-          aStrokePattern->SetMatrix(aContext->CurrentMatrix().Multiply(strokeMatrix));
-          strokePattern = aStrokePattern->CairoPattern();
-      }
-
       bool success = SetupCairoFont(aContext);
       if (NS_UNLIKELY(!success))
           return;
 
       ::GlyphBuffer glyphs;
       cairo_glyph_t *glyph;
 
       if (aSpacing) {
@@ -1733,47 +1748,48 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint
                   glyphX = x;
                   x += advance;
               }
 
               if (haveSVGGlyphs) {
                   gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
                                  ToDeviceUnits(y, devUnitsPerAppUnit));
                   if (RenderSVGGlyph(aContext, point, aDrawMode,
-                                     glyphData->GetSimpleGlyph())) {
+                                     glyphData->GetSimpleGlyph(),
+                                     aObjectPaint)) {
                       continue;
                   }
               }
 
               // Perhaps we should put a scale in the cairo context instead of
               // doing this scaling here...
               // Multiplying by the reciprocal may introduce tiny error here,
               // but we assume cairo is going to round coordinates at some stage
               // and this is faster
               glyph = glyphs.AppendGlyph();
               glyph->index = glyphData->GetSimpleGlyph();
               glyph->x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
               glyph->y = ToDeviceUnits(y, devUnitsPerAppUnit);
-              glyphs.Flush(cr, strokePattern, aDrawMode, isRTL);
+              glyphs.Flush(cr, aDrawMode, isRTL, aObjectPaint, globalMatrix);
             
               // synthetic bolding by multi-striking with 1-pixel offsets
               // at least once, more if there's room (large font sizes)
               if (IsSyntheticBold()) {
                   double strikeOffset = synBoldOnePixelOffset;
                   int32_t strikeCount = strikes;
                   do {
                       cairo_glyph_t *doubleglyph;
                       doubleglyph = glyphs.AppendGlyph();
                       doubleglyph->index = glyph->index;
                       doubleglyph->x =
                           ToDeviceUnits(glyphX + strikeOffset * appUnitsPerDevUnit,
                                         devUnitsPerAppUnit);
                       doubleglyph->y = glyph->y;
                       strikeOffset += synBoldOnePixelOffset;
-                      glyphs.Flush(cr, strokePattern, aDrawMode, isRTL);
+                      glyphs.Flush(cr, aDrawMode, isRTL, aObjectPaint, globalMatrix);
                   } while (--strikeCount > 0);
               }
           } else {
               uint32_t glyphCount = glyphData->GetGlyphCount();
               if (glyphCount > 0) {
                   const gfxTextRun::DetailedGlyph *details =
                       aTextRun->GetDetailedGlyphs(i);
                   NS_ASSERTION(details, "detailedGlyph should not be missing!");
@@ -1802,37 +1818,37 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint
                               glyphX -= advance;
                           }
 
                           gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
                                          ToDeviceUnits(y, devUnitsPerAppUnit));
                                             
                           if (!haveSVGGlyphs ||
                               !RenderSVGGlyph(aContext, point, aDrawMode,
-                                              details->mGlyphID)) {
+                                              details->mGlyphID, aObjectPaint)) {
                               glyph = glyphs.AppendGlyph();
                               glyph->index = details->mGlyphID;
                               glyph->x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
                               glyph->y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit);
-                              glyphs.Flush(cr, strokePattern, aDrawMode, isRTL);
+                              glyphs.Flush(cr, aDrawMode, isRTL, aObjectPaint, globalMatrix);
 
                               if (IsSyntheticBold()) {
                                   double strikeOffset = synBoldOnePixelOffset;
                                   int32_t strikeCount = strikes;
                                   do {
                                       cairo_glyph_t *doubleglyph;
                                       doubleglyph = glyphs.AppendGlyph();
                                       doubleglyph->index = glyph->index;
                                       doubleglyph->x =
                                           ToDeviceUnits(glyphX + strikeOffset *
                                                   appUnitsPerDevUnit,
                                                   devUnitsPerAppUnit);
                                       doubleglyph->y = glyph->y;
                                       strikeOffset += synBoldOnePixelOffset;
-                                      glyphs.Flush(cr, strokePattern, aDrawMode, isRTL);
+                                      glyphs.Flush(cr, aDrawMode, isRTL, aObjectPaint, globalMatrix);
                                   } while (--strikeCount > 0);
                               }
                           }
                       }
                       x += direction*advance;
                   }
               }
           }
@@ -1852,22 +1868,23 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint
            * flush, since that'll blow away the num_glyphs.
            */
           gfxFontTestStore::CurrentStore()->AddItem(GetName(),
                                                     glyphs.mGlyphBuffer,
                                                     glyphs.mNumGlyphs);
       }
 
       // draw any remaining glyphs
-      glyphs.Flush(cr, strokePattern, aDrawMode, isRTL, true);
+      glyphs.Flush(cr, aDrawMode, isRTL, aObjectPaint, globalMatrix, true);
 
     } else {
       RefPtr<ScaledFont> scaledFont =
         gfxPlatform::GetPlatform()->GetScaledFontForFont(dt, this);
-      
+      nsRefPtr<gfxPattern> strokePattern = aObjectPaint->GetStrokePattern();
+
       if (!scaledFont) {
         return;
       }
 
       bool oldSubpixelAA = dt->GetPermitSubpixelAA();
 
       if (!AllowSubpixelAA()) {
           dt->SetPermitSubpixelAA(false);
@@ -1930,17 +1947,17 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint
                   glyphX = x;
               } else {
                   glyphX = x;
                   x += advance;
               }
               glyph->mPosition.x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
               glyph->mPosition.y = ToDeviceUnits(y, devUnitsPerAppUnit);
               glyph->mPosition = matInv * glyph->mPosition;
-              glyphs.Flush(dt, aStrokePattern, scaledFont,
+              glyphs.Flush(dt, strokePattern, scaledFont,
                            aDrawMode, isRTL, renderingOptions,
                            aContext, passedInvMatrix);
             
               // synthetic bolding by multi-striking with 1-pixel offsets
               // at least once, more if there's room (large font sizes)
               if (IsSyntheticBold()) {
                   double strikeOffset = synBoldOnePixelOffset;
                   int32_t strikeCount = strikes;
@@ -1949,17 +1966,17 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint
                       doubleglyph = glyphs.AppendGlyph();
                       doubleglyph->mIndex = glyph->mIndex;
                       doubleglyph->mPosition.x =
                           ToDeviceUnits(glyphX + strikeOffset * appUnitsPerDevUnit,
                                         devUnitsPerAppUnit);
                       doubleglyph->mPosition.y = glyph->mPosition.y;
                       doubleglyph->mPosition = matInv * doubleglyph->mPosition;
                       strikeOffset += synBoldOnePixelOffset;
-                      glyphs.Flush(dt, aStrokePattern, scaledFont,
+                      glyphs.Flush(dt, strokePattern, scaledFont,
                                    aDrawMode, isRTL, renderingOptions,
                                    aContext, passedInvMatrix);
                   } while (--strikeCount > 0);
               }
           } else {
               uint32_t glyphCount = glyphData->GetGlyphCount();
               if (glyphCount > 0) {
                   const gfxTextRun::DetailedGlyph *details =
@@ -1989,34 +2006,34 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint
                           glyph->mIndex = details->mGlyphID;
                           double glyphX = x + details->mXOffset;
                           if (isRTL) {
                               glyphX -= advance;
                           }
                           glyph->mPosition.x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
                           glyph->mPosition.y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit);
                           glyph->mPosition = matInv * glyph->mPosition;
-                          glyphs.Flush(dt, aStrokePattern, scaledFont, aDrawMode,
+                          glyphs.Flush(dt, strokePattern, scaledFont, aDrawMode,
                                        isRTL, renderingOptions, aContext, passedInvMatrix);
 
                           if (IsSyntheticBold()) {
                               double strikeOffset = synBoldOnePixelOffset;
                               int32_t strikeCount = strikes;
                               do {
                                   Glyph *doubleglyph;
                                   doubleglyph = glyphs.AppendGlyph();
                                   doubleglyph->mIndex = glyph->mIndex;
                                   doubleglyph->mPosition.x =
                                       ToDeviceUnits(glyphX + strikeOffset *
                                                         appUnitsPerDevUnit,
                                                     devUnitsPerAppUnit);
                                   doubleglyph->mPosition.y = glyph->mPosition.y;
                                   strikeOffset += synBoldOnePixelOffset;
                                   doubleglyph->mPosition = matInv * doubleglyph->mPosition;
-                                  glyphs.Flush(dt, aStrokePattern, scaledFont,
+                                  glyphs.Flush(dt, strokePattern, scaledFont,
                                                aDrawMode, isRTL, renderingOptions,
                                                aContext, passedInvMatrix);
                               } while (--strikeCount > 0);
                           }
                       }
                       x += direction*advance;
                   }
               }
@@ -2026,49 +2043,45 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint
               double space = aSpacing[i - aStart].mAfter;
               if (i + 1 < aEnd) {
                   space += aSpacing[i + 1 - aStart].mBefore;
               }
               x += direction*space;
           }
       }
 
-      glyphs.Flush(dt, aStrokePattern, scaledFont, aDrawMode, isRTL,
+      glyphs.Flush(dt, strokePattern, scaledFont, aDrawMode, isRTL,
                    renderingOptions, aContext, passedInvMatrix, true);
 
       dt->SetTransform(oldMat);
 
       dt->SetPermitSubpixelAA(oldSubpixelAA);
     }
 
-    // Restore matrix for stroke pattern
-    if (aStrokePattern) {
-        aStrokePattern->SetMatrix(strokeMatrix);
-    }
-
     *aPt = gfxPoint(x, y);
 }
 
 bool
 gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode,
-                        uint32_t aGlyphId)
+                        uint32_t aGlyphId, gfxTextObjectPaint *aObjectPaint)
 {
     static const float SVG_UNITS_PER_EM = 1000.0;
 
     if (!GetFontEntry()->HasSVGGlyph(aGlyphId)) {
         return false;
     }
 
     const float devUnitsPerSVGUnit = GetStyle()->size / SVG_UNITS_PER_EM;
     gfxContextMatrixAutoSaveRestore matrixRestore(aContext);
 
     aContext->Translate(gfxPoint(aPoint.x, aPoint.y));
     aContext->Scale(devUnitsPerSVGUnit, devUnitsPerSVGUnit);
 
-    return GetFontEntry()->RenderSVGGlyph(aContext, aGlyphId, aDrawMode);
+    return GetFontEntry()->RenderSVGGlyph(aContext, aGlyphId, aDrawMode,
+                                          aObjectPaint);
 }
 
 static void
 UnionRange(gfxFloat aX, gfxFloat* aDestMin, gfxFloat* aDestMax)
 {
     *aDestMin = NS_MIN(*aDestMin, aX);
     *aDestMax = NS_MAX(*aDestMax, aX);
 }
@@ -4657,26 +4670,26 @@ gfxTextRun::ShrinkToLigatureBoundaries(u
             --(*aEnd);
         }
     }
 }
 
 void
 gfxTextRun::DrawGlyphs(gfxFont *aFont, gfxContext *aContext,
                        gfxFont::DrawMode aDrawMode, gfxPoint *aPt,
-                       gfxPattern *aStrokePattern,
+                       gfxTextObjectPaint *aObjectPaint,
                        uint32_t aStart, uint32_t aEnd,
                        PropertyProvider *aProvider,
                        uint32_t aSpacingStart, uint32_t aSpacingEnd)
 {
     nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
     bool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider,
         aSpacingStart, aSpacingEnd, &spacingBuffer);
     aFont->Draw(this, aStart, aEnd, aContext, aDrawMode, aPt,
-                haveSpacing ? spacingBuffer.Elements() : nullptr, aStrokePattern);
+                haveSpacing ? spacingBuffer.Elements() : nullptr, aObjectPaint);
 }
 
 static void
 ClipPartialLigature(gfxTextRun *aTextRun, gfxFloat *aLeft, gfxFloat *aRight,
                     gfxFloat aXOrigin, gfxTextRun::LigatureData *aLigature)
 {
     if (aLigature->mClipBeforePart) {
         if (aTextRun->IsRightToLeft()) {
@@ -4809,17 +4822,17 @@ struct BufferAlphaColor {
     gfxContext *mContext;
     gfxFloat mAlpha;
 };
 
 void
 gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt, gfxFont::DrawMode aDrawMode,
                  uint32_t aStart, uint32_t aLength,
                  PropertyProvider *aProvider, gfxFloat *aAdvanceWidth,
-                 gfxPattern *aStrokePattern,
+                 gfxTextObjectPaint *aObjectPaint,
                  gfxTextRun::DrawCallbacks *aCallbacks)
 {
     NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range");
     NS_ASSERTION(aDrawMode <= gfxFont::GLYPH_PATH, "GLYPH_PATH cannot be used with GLYPH_FILL or GLYPH_STROKE");
     NS_ASSERTION(aDrawMode == gfxFont::GLYPH_PATH || !aCallbacks, "callback must not be specified unless using GLYPH_PATH");
 
     gfxFloat direction = GetDirection();
 
@@ -4866,17 +4879,17 @@ gfxTextRun::Draw(gfxContext *aContext, g
         bool drawPartial = aDrawMode == gfxFont::GLYPH_FILL ||
                            aDrawMode == gfxFont::GLYPH_PATH && aCallbacks;
 
         if (drawPartial) {
             DrawPartialLigature(font, aContext, start, ligatureRunStart, &pt,
                                 aProvider, aCallbacks);
         }
 
-        DrawGlyphs(font, aContext, aDrawMode, &pt, aStrokePattern, ligatureRunStart,
+        DrawGlyphs(font, aContext, aDrawMode, &pt, aObjectPaint, ligatureRunStart,
                    ligatureRunEnd, aProvider, ligatureRunStart, ligatureRunEnd);
 
         if (aCallbacks) {
           aCallbacks->NotifyGlyphPathEmitted();
         }
 
         if (drawPartial) {
             DrawPartialLigature(font, aContext, ligatureRunEnd, end, &pt,
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -39,16 +39,17 @@ class gfxContext;
 class gfxTextRun;
 class gfxFont;
 class gfxFontFamily;
 class gfxFontGroup;
 class gfxUserFontSet;
 class gfxUserFontData;
 class gfxShapedWord;
 class gfxSVGGlyphs;
+class gfxTextObjectPaint;
 
 class nsILanguageAtomService;
 
 typedef struct hb_blob_t hb_blob_t;
 
 #define FONT_MAX_SIZE                  2000.0
 
 #define NO_FONT_LANGUAGE_OVERRIDE      0
@@ -275,17 +276,18 @@ public:
     virtual bool SkipDuringSystemFallback() { return false; }
     virtual bool TestCharacterMap(uint32_t aCh);
     nsresult InitializeUVSMap();
     uint16_t GetUVSGlyph(uint32_t aCh, uint32_t aVS);
     virtual nsresult ReadCMAP();
 
     bool TryGetSVGData();
     bool HasSVGGlyph(uint32_t aGlyphId);
-    bool RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId, int aDrawMode);
+    bool RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId, int aDrawMode,
+                        gfxTextObjectPaint *aObjectPaint);
 
     virtual bool MatchesGenericFamily(const nsACString& aGeneric) const {
         return true;
     }
     virtual bool SupportsLangGroup(nsIAtom *aLangGroup) const {
         return true;
     }
 
@@ -1413,27 +1415,30 @@ public:
      * this should be updated to the other end of the baseline. In application
      * units, really!
      * @param aSpacing spacing to insert before and after characters (for RTL
      * glyphs, before-spacing is inserted to the right of characters). There
      * are aEnd - aStart elements in this array, unless it's null to indicate
      * that there is no spacing.
      * @param aDrawMode specifies whether the fill or stroke of the glyph should be
      * drawn, or if it should be drawn into the current path
+     * @param aObjectPaint information about how to construct the fill and
+     * stroke pattern. Can be NULL if we are not stroking the text, which
+     * indicates that the current source from aContext should be used for filling
      * 
      * Callers guarantee:
      * -- aStart and aEnd are aligned to cluster and ligature boundaries
      * -- all glyphs use this font
      * 
      * The default implementation builds a cairo glyph array and
      * calls cairo_show_glyphs or cairo_glyph_path.
      */
     virtual void Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
                       gfxContext *aContext, DrawMode aDrawMode, gfxPoint *aBaselineOrigin,
-                      Spacing *aSpacing, gfxPattern *aStrokePattern);
+                      Spacing *aSpacing, gfxTextObjectPaint *aObjectPaint);
 
     /**
      * Measure a run of characters. See gfxTextRun::Metrics.
      * @param aTight if false, then return the union of the glyph extents
      * with the font-box for the characters (the rectangle with x=0,width=
      * the advance width for the character run,y=-(font ascent), and height=
      * font ascent + font descent). Otherwise, we must return as tight as possible
      * an approximation to the area actually painted by glyphs.
@@ -1706,17 +1711,17 @@ protected:
     // InitMetricsFromSfntTables or equivalent platform code
     void CalculateDerivedMetrics(Metrics& aMetrics);
 
     // some fonts have bad metrics, this method sanitize them.
     // if this font has bad underline offset, aIsBadUnderlineFont should be true.
     void SanitizeMetrics(gfxFont::Metrics *aMetrics, bool aIsBadUnderlineFont);
 
     bool RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode,
-                        uint32_t aGlyphId);
+                        uint32_t aGlyphId, gfxTextObjectPaint *aObjectPaint);
 
     // Bug 674909. When synthetic bolding text by drawing twice, need to
     // render using a pixel offset in device pixels, otherwise text
     // doesn't appear bolded, it appears as if a bad text shadow exists
     // when a non-identity transform exists.  Use an offset factor so that
     // the second draw occurs at a constant offset in device pixels.
     // This helper calculates the scale factor we need to apply to the
     // synthetic-bold offset.
@@ -2521,17 +2526,17 @@ public:
      * 
      * Glyphs should be drawn in logical content order, which can be significant
      * if they overlap (perhaps due to negative spacing).
      */
     void Draw(gfxContext *aContext, gfxPoint aPt,
               gfxFont::DrawMode aDrawMode,
               uint32_t aStart, uint32_t aLength,
               PropertyProvider *aProvider,
-              gfxFloat *aAdvanceWidth, gfxPattern *aStrokePattern,
+              gfxFloat *aAdvanceWidth, gfxTextObjectPaint *aObjectPaint,
               DrawCallbacks *aCallbacks = nullptr);
 
     /**
      * Computes the ReflowMetrics for a substring.
      * Uses GetSpacing from aBreakProvider.
      * @param aBoundingBoxType which kind of bounding box (loose/tight)
      */
     Metrics MeasureText(uint32_t aStart, uint32_t aLength,
@@ -2969,18 +2974,18 @@ private:
                                  gfxContext *aRefContext,
                                  PropertyProvider *aProvider,
                                  uint32_t aSpacingStart, uint32_t aSpacingEnd,
                                  Metrics *aMetrics);
 
     // **** drawing helper ****
     void DrawGlyphs(gfxFont *aFont, gfxContext *aContext,
                     gfxFont::DrawMode aDrawMode, gfxPoint *aPt,
-                    gfxPattern *aStrokePattern, uint32_t aStart, uint32_t aEnd,
-                    PropertyProvider *aProvider,
+                    gfxTextObjectPaint *aObjectPaint, uint32_t aStart,
+                    uint32_t aEnd, PropertyProvider *aProvider,
                     uint32_t aSpacingStart, uint32_t aSpacingEnd);
 
     nsAutoPtr<DetailedGlyphStore>   mDetailedGlyphs;
 
     // XXX this should be changed to a GlyphRun plus a maybe-null GlyphRun*,
     // for smaller size especially in the super-common one-glyphrun case
     nsAutoTArray<GlyphRun,1>        mGlyphRuns;
 
--- a/gfx/thebes/gfxSVGGlyphs.cpp
+++ b/gfx/thebes/gfxSVGGlyphs.cpp
@@ -61,17 +61,17 @@
 #include "nsStreamUtils.h"
 #include "nsIPrincipal.h"
 #include "Element.h"
 
 #define SVG_CONTENT_TYPE NS_LITERAL_CSTRING("image/svg+xml")
 #define UTF8_CHARSET NS_LITERAL_CSTRING("utf-8")
 
 
-typedef mozilla::dom::Element Element;
+mozilla::gfx::UserDataKey gfxTextObjectPaint::sUserDataKey;
 
 /* static */ gfxSVGGlyphs*
 gfxSVGGlyphs::ParseFromBuffer(uint8_t *aBuffer, uint32_t aBufLen)
 {
     nsCOMPtr<nsIDocument> doc;
     nsresult rv = ParseDocument(aBuffer, aBufLen, getter_AddRefs(doc));
     NS_ENSURE_SUCCESS(rv, nullptr);
 
@@ -169,28 +169,28 @@ gfxSVGGlyphs::FindGlyphElements(Element 
  * If no such glyph exists, or in the case of an error return false
  * @param aContext The thebes aContext to draw to
  * @param aGlyphId The glyph id
  * @param aDrawMode Whether to fill or stroke or both (see |gfxFont::DrawMode|)
  * @return true iff rendering succeeded
  */
 bool
 gfxSVGGlyphs::RenderGlyph(gfxContext *aContext, uint32_t aGlyphId,
-                          DrawMode aDrawMode)
+                          DrawMode aDrawMode, gfxTextObjectPaint *aObjectPaint)
 {
     if (aDrawMode == gfxFont::GLYPH_PATH) {
         return false;
     }
 
     gfxContextAutoSaveRestore aContextRestorer(aContext);
 
     Element *glyph = mGlyphIdMap.Get(aGlyphId);
     NS_ASSERTION(glyph, "No glyph element. Should check with HasSVGGlyph() first!");
 
-    return nsContentUtils::PaintSVGGlyph(glyph, aContext);
+    return nsContentUtils::PaintSVGGlyph(glyph, aContext, aDrawMode, aObjectPaint);
 }
 
 bool
 gfxSVGGlyphs::HasSVGGlyph(uint32_t aGlyphId)
 {
     Element *glyph = mGlyphIdMap.Get(aGlyphId);
     return !!glyph;
 }
--- a/gfx/thebes/gfxSVGGlyphs.h
+++ b/gfx/thebes/gfxSVGGlyphs.h
@@ -43,33 +43,35 @@
 #include "nsAutoPtr.h"
 #include "nsIContentViewer.h"
 #include "nsIPresShell.h"
 #include "nsBaseHashtable.h"
 #include "nsHashKeys.h"
 #include "nsIDocument.h"
 #include "gfxPattern.h"
 #include "gfxFont.h"
+#include "mozilla/gfx/UserData.h"
 
 
 /**
  * Used by gfxFontEntry for OpenType fonts which contains SVG tables to parse
  * the SVG document and store and render SVG glyphs
  */
 class gfxSVGGlyphs {
 private:
     typedef mozilla::dom::Element Element;
     typedef gfxFont::DrawMode DrawMode;
 
 public:
     static gfxSVGGlyphs* ParseFromBuffer(uint8_t *aBuffer, uint32_t aBufLen);
 
     bool HasSVGGlyph(uint32_t aGlyphId);
 
-    bool RenderGlyph(gfxContext *aContext, uint32_t aGlyphId, DrawMode aDrawMode);
+    bool RenderGlyph(gfxContext *aContext, uint32_t aGlyphId, DrawMode aDrawMode,
+                     gfxTextObjectPaint *aObjectPaint);
 
     bool Init(const gfxFontEntry *aFont,
               const FallibleTArray<uint8_t> &aCmapTable);
 
     ~gfxSVGGlyphs() {
         mViewer->Destroy();
     }
 
@@ -89,9 +91,72 @@ private:
     
     nsCOMPtr<nsIDocument> mDocument;
     nsCOMPtr<nsIContentViewer> mViewer;
     nsCOMPtr<nsIPresShell> mPresShell;
 
     nsBaseHashtable<nsUint32HashKey, Element*, Element*> mGlyphIdMap;
 };
 
+/**
+ * Used for trickling down paint information through to SVG glyphs.
+ * Will be extended in later patch.
+ */
+class gfxTextObjectPaint
+{
+protected:
+    gfxTextObjectPaint() { }
+
+public:
+    static mozilla::gfx::UserDataKey sUserDataKey;
+
+    /*
+     * Get outer text object pattern with the specified opacity value.
+     * This lets us inherit paints and paint opacities (i.e. fill/stroke and
+     * fill-opacity/stroke-opacity) separately.
+     *
+     * Deferred opacity to be actually implemented in a later patch
+     */
+    virtual already_AddRefed<gfxPattern> GetFillPattern(float aOpacity = 1.0f) = 0;
+    virtual already_AddRefed<gfxPattern> GetStrokePattern(float aOpacity = 1.0f) = 0;
+
+    virtual ~gfxTextObjectPaint() { }
+};
+
+/**
+ * For passing in patterns where the outer text object has no separate pattern
+ * opacity value.
+ */
+class SimpleTextObjectPaint : public gfxTextObjectPaint
+{
+public:
+    SimpleTextObjectPaint(gfxPattern *aFillPattern, gfxPattern *aStrokePattern) :
+        mFillPattern(aFillPattern), mStrokePattern(aStrokePattern),
+        mFillMatrix(aFillPattern ? aFillPattern->GetMatrix() : gfxMatrix()),
+        mStrokeMatrix(aStrokePattern ? aStrokePattern->GetMatrix() : gfxMatrix())
+    {
+    }
+
+    already_AddRefed<gfxPattern> GetFillPattern(float aOpacity) {
+        if (mFillPattern) {
+            mFillPattern->SetMatrix(mFillMatrix);
+        }
+        nsRefPtr<gfxPattern> fillPattern = mFillPattern;
+        return fillPattern.forget();
+    }
+
+    already_AddRefed<gfxPattern> GetStrokePattern(float aOpacity) {
+        if (mStrokePattern) {
+            mStrokePattern->SetMatrix(mStrokeMatrix);
+        }
+        nsRefPtr<gfxPattern> strokePattern = mStrokePattern;
+        return strokePattern.forget();
+    }
+
+private:
+    nsRefPtr<gfxPattern> mFillPattern;
+    nsRefPtr<gfxPattern> mStrokePattern;
+
+    gfxMatrix mFillMatrix;
+    gfxMatrix mStrokeMatrix;
+};
+
 #endif
--- a/layout/svg/base/src/nsSVGGeometryFrame.cpp
+++ b/layout/svg/base/src/nsSVGGeometryFrame.cpp
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Main header first:
 #include "nsSVGGeometryFrame.h"
 
 // Keep others in (case-insensitive) order:
 #include "gfxContext.h"
+#include "gfxSVGGlyphs.h"
 #include "nsPresContext.h"
 #include "nsSVGEffects.h"
 #include "nsSVGPaintServerFrame.h"
 #include "nsSVGPathElement.h"
 #include "nsSVGUtils.h"
 
 NS_IMPL_FRAMEARENA_HELPERS(nsSVGGeometryFrame)
 
--- a/layout/svg/base/src/nsSVGGlyphFrame.cpp
+++ b/layout/svg/base/src/nsSVGGlyphFrame.cpp
@@ -428,17 +428,17 @@ nsSVGGlyphFrame::PaintSVG(nsRenderingCon
     gfx->Restore();
     return NS_OK;
   }
 
   nsRefPtr<gfxPattern> strokePattern;
   DrawMode drawMode = SetupCairoState(gfx, getter_AddRefs(strokePattern));
 
   if (drawMode) {
-    DrawCharacters(&iter, gfx, drawMode, strokePattern);
+    DrawCharacters(&iter, gfx, drawMode, nullptr);
   }
   
   gfx->Restore();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP_(nsIFrame*)
@@ -600,33 +600,33 @@ nsSVGGlyphFrame::AddBoundingBoxesToPath(
     aContext->Rectangle(metrics.mBoundingBox);
   }
 }
 
 void
 nsSVGGlyphFrame::DrawCharacters(CharacterIterator *aIter,
                                 gfxContext *aContext,
                                 DrawMode aDrawMode,
-                                gfxPattern *aStrokePattern)
+                                gfxTextObjectPaint *aObjectPaint)
 {
   if (aDrawMode & gfxFont::GLYPH_STROKE) {
     aIter->SetLineWidthAndDashesForDrawing(aContext);
   }
 
   if (aIter->SetupForDirectTextRunDrawing(aContext)) {
     mTextRun->Draw(aContext, gfxPoint(0, 0), aDrawMode, 0,
-                   mTextRun->GetLength(), nullptr, nullptr, aStrokePattern);
+                   mTextRun->GetLength(), nullptr, nullptr, aObjectPaint);
     return;
   }
 
   uint32_t i;
   while ((i = aIter->NextCluster()) != aIter->InvalidCluster()) {
     aIter->SetupForDrawing(aContext);
     mTextRun->Draw(aContext, gfxPoint(0, 0), aDrawMode, i,
-                   aIter->ClusterLength(), nullptr, nullptr, aStrokePattern);
+                   aIter->ClusterLength(), nullptr, nullptr, aObjectPaint);
   }
 }
 
 SVGBBox
 nsSVGGlyphFrame::GetBBoxContribution(const gfxMatrix &aToBBoxUserspace,
                                      uint32_t aFlags)
 {
   SVGBBox bbox;
--- a/layout/svg/base/src/nsSVGGlyphFrame.h
+++ b/layout/svg/base/src/nsSVGGlyphFrame.h
@@ -16,16 +16,17 @@
 class CharacterIterator;
 class gfxContext;
 class nsDisplaySVGGlyphs;
 class nsIDOMSVGRect;
 class nsRenderingContext;
 class nsSVGGlyphFrame;
 class nsSVGTextFrame;
 class nsSVGTextPathFrame;
+class gfxTextObjectPaint;
 
 struct CharacterPosition;
 
 typedef gfxFont::DrawMode DrawMode;
 
 typedef nsSVGGeometryFrame nsSVGGlyphFrameBase;
 
 class nsSVGGlyphFrame : public nsSVGGlyphFrameBase,
@@ -231,17 +232,17 @@ private:
 
   void AddCharactersToPath(CharacterIterator *aIter,
                            gfxContext *aContext);
   void AddBoundingBoxesToPath(CharacterIterator *aIter,
                               gfxContext *aContext);
   void DrawCharacters(CharacterIterator *aIter,
                       gfxContext *aContext,
                       DrawMode aDrawMode,
-                      gfxPattern *aStrokePattern = nullptr);
+                      gfxTextObjectPaint *aObjectPaint = nullptr);
 
   void NotifyGlyphMetricsChange();
   void SetupGlobalTransform(gfxContext *aContext, uint32_t aFor);
   nsresult GetHighlight(uint32_t *charnum, uint32_t *nchars,
                         nscolor *foreground, nscolor *background);
   float GetSubStringAdvance(uint32_t charnum, uint32_t fragmentChars,
                             float aMetricsScale);
   gfxFloat GetBaselineOffset(float aMetricsScale);
--- a/layout/svg/base/src/nsSVGPathGeometryFrame.cpp
+++ b/layout/svg/base/src/nsSVGPathGeometryFrame.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Main header first:
 #include "nsSVGPathGeometryFrame.h"
 
 // Keep others in (case-insensitive) order:
 #include "gfxContext.h"
 #include "gfxPlatform.h"
+#include "gfxSVGGlyphs.h"
 #include "nsDisplayList.h"
 #include "nsGkAtoms.h"
 #include "nsRenderingContext.h"
 #include "nsSVGEffects.h"
 #include "nsSVGGraphicElement.h"
 #include "nsSVGIntegrationUtils.h"
 #include "nsSVGMarkerFrame.h"
 #include "nsSVGPathGeometryElement.h"
@@ -563,22 +564,28 @@ nsSVGPathGeometryFrame::Render(nsRenderi
       gfx->SetColor(gfxRGBA(1.0f, 1.0f, 1.0f, 1.0f));
       gfx->Fill();
       gfx->NewPath();
     }
 
     return;
   }
 
-  if (nsSVGUtils::SetupCairoFillPaint(this, gfx)) {
+  gfxTextObjectPaint *objectPaint =
+    (gfxTextObjectPaint*)aContext->GetUserData(&gfxTextObjectPaint::sUserDataKey);
+
+  if (nsSVGUtils::SetupCairoFillPaint(this, gfx, objectPaint)) {
     gfx->Fill();
   }
 
-  if (nsSVGUtils::SetupCairoStroke(this, gfx)) {
-    gfx->Stroke();
+  if (nsSVGUtils::HasStroke(this, objectPaint)) {
+    nsSVGUtils::SetupCairoStrokeHitGeometry(this, gfx, objectPaint);
+    if (nsSVGUtils::SetupCairoStrokePaint(this, gfx, objectPaint)) {
+      gfx->Stroke();
+    }
   }
 
   gfx->NewPath();
 
   gfx->Restore();
 }
 
 void
--- a/layout/svg/base/src/nsSVGUtils.cpp
+++ b/layout/svg/base/src/nsSVGUtils.cpp
@@ -38,16 +38,17 @@
 #include "nsSVGAnimationElement.h"
 #include "nsSVGClipPathFrame.h"
 #include "nsSVGContainerFrame.h"
 #include "nsSVGEffects.h"
 #include "nsSVGFilterFrame.h"
 #include "nsSVGFilterPaintCallback.h"
 #include "nsSVGForeignObjectFrame.h"
 #include "nsSVGGeometryFrame.h"
+#include "nsSVGGlyphFrame.h"
 #include "nsSVGInnerSVGFrame.h"
 #include "nsSVGIntegrationUtils.h"
 #include "nsSVGLength2.h"
 #include "nsSVGMaskFrame.h"
 #include "nsSVGOuterSVGFrame.h"
 #include "nsSVGPathElement.h"
 #include "nsSVGPathGeometryElement.h"
 #include "nsSVGPathGeometryFrame.h"
@@ -1885,57 +1886,102 @@ MaybeOptimizeOpacity(nsIFrame *aFrame, f
 {
   float opacity = aFrame->GetStyleDisplay()->mOpacity;
   if (opacity < 1 && nsSVGUtils::CanOptimizeOpacity(aFrame)) {
     return aFillOrStrokeOpacity * opacity;
   }
   return aFillOrStrokeOpacity;
 }
 
+/* static */ bool
+nsSVGUtils::SetupObjectPaint(gfxContext *aContext,
+                             gfxTextObjectPaint *aObjectPaint,
+                             const nsStyleSVGPaint &aPaint,
+                             float aOpacity)
+{
+  nsRefPtr<gfxPattern> pattern;
+
+  if (!aObjectPaint) {
+    return false;
+  }
+
+  switch (aPaint.mType) {
+    case eStyleSVGPaintType_ObjectFill:
+      pattern = aObjectPaint->GetFillPattern();
+      break;
+    case eStyleSVGPaintType_ObjectStroke:
+      pattern = aObjectPaint->GetStrokePattern();
+      break;
+    default:
+      return false;
+  }
+
+  if (!pattern) {
+    return false;
+  }
+
+  // When the pattern is set, cairo sets the context source matrix to the
+  // inverse of the context matrix so we have to account for that here
+  pattern->SetMatrix(aContext->CurrentMatrix().Multiply(pattern->GetMatrix()));
+  aContext->SetPattern(pattern);
+
+  return true;
+}
+
 bool
-nsSVGUtils::SetupCairoFillPaint(nsIFrame *aFrame, gfxContext* aContext)
+nsSVGUtils::SetupCairoFillPaint(nsIFrame *aFrame, gfxContext* aContext,
+                                gfxTextObjectPaint *aObjectPaint)
 {
   const nsStyleSVG* style = aFrame->GetStyleSVG();
   if (style->mFill.mType == eStyleSVGPaintType_None)
     return false;
 
   if (style->mFillRule == NS_STYLE_FILL_RULE_EVENODD)
     aContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
   else
     aContext->SetFillRule(gfxContext::FILL_RULE_WINDING);
 
   float opacity = MaybeOptimizeOpacity(aFrame, style->mFillOpacity);
   nsSVGPaintServerFrame *ps =
     nsSVGEffects::GetPaintServer(aFrame, &style->mFill, nsSVGEffects::FillProperty());
   if (ps && ps->SetupPaintServer(aContext, aFrame, &nsStyleSVG::mFill, opacity))
     return true;
 
+  if (SetupObjectPaint(aContext, aObjectPaint, style->mFill, opacity)) {
+    return true;
+  }
+
   // On failure, use the fallback colour in case we have an
   // objectBoundingBox where the width or height of the object is zero.
   // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
   SetupFallbackOrPaintColor(aContext, aFrame->GetStyleContext(),
                             &nsStyleSVG::mFill, opacity);
 
   return true;
 }
 
 bool
-nsSVGUtils::SetupCairoStrokePaint(nsIFrame *aFrame, gfxContext* aContext)
+nsSVGUtils::SetupCairoStrokePaint(nsIFrame *aFrame, gfxContext* aContext,
+                                  gfxTextObjectPaint *aObjectPaint)
 {
   const nsStyleSVG* style = aFrame->GetStyleSVG();
   if (style->mStroke.mType == eStyleSVGPaintType_None)
     return false;
 
   float opacity = MaybeOptimizeOpacity(aFrame, style->mStrokeOpacity);
 
   nsSVGPaintServerFrame *ps =
     nsSVGEffects::GetPaintServer(aFrame, &style->mStroke, nsSVGEffects::StrokeProperty());
   if (ps && ps->SetupPaintServer(aContext, aFrame, &nsStyleSVG::mStroke, opacity))
     return true;
 
+  if (SetupObjectPaint(aContext, aObjectPaint, style->mStroke, opacity)) {
+    return true;
+  }
+
   // On failure, use the fallback colour in case we have an
   // objectBoundingBox where the width or height of the object is zero.
   // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
   SetupFallbackOrPaintColor(aContext, aFrame->GetStyleContext(),
                             &nsStyleSVG::mStroke, opacity);
 
   return true;
 }
--- a/layout/svg/base/src/nsSVGUtils.h
+++ b/layout/svg/base/src/nsSVGUtils.h
@@ -41,16 +41,17 @@ class nsSVGDisplayContainerFrame;
 class nsSVGElement;
 class nsSVGEnum;
 class nsSVGGeometryFrame;
 class nsSVGLength2;
 class nsSVGOuterSVGFrame;
 class nsSVGPathGeometryFrame;
 class nsSVGSVGElement;
 class nsTextFrame;
+class gfxTextObjectPaint;
 
 struct nsStyleSVG;
 struct nsStyleSVGPaint;
 
 namespace mozilla {
 class SVGAnimatedPreserveAspectRatio;
 class SVGPreserveAspectRatio;
 namespace dom {
@@ -674,26 +675,36 @@ public:
                             NS_MIN(double(PR_INT32_MAX), aVal)));
   }
 
   static nscolor GetFallbackOrPaintColor(gfxContext *aContext,
                                          nsStyleContext *aStyleContext,
                                          nsStyleSVGPaint nsStyleSVG::*aFillOrStroke);
 
   /**
+   * Set up cairo context with an object pattern
+   */
+  static bool SetupObjectPaint(gfxContext *aContext,
+                               gfxTextObjectPaint *aObjectPaint,
+                               const nsStyleSVGPaint& aPaint,
+                               float aOpacity);
+
+  /**
    * Sets the current paint on the specified gfxContent to be the SVG 'fill'
    * for the given frame.
    */
-  static bool SetupCairoFillPaint(nsIFrame* aFrame, gfxContext* aContext);
+  static bool SetupCairoFillPaint(nsIFrame* aFrame, gfxContext* aContext,
+                                  gfxTextObjectPaint *aObjectPaint = nullptr);
 
   /**
    * Sets the current paint on the specified gfxContent to be the SVG 'stroke'
    * for the given frame.
    */
-  static bool SetupCairoStrokePaint(nsIFrame* aFrame, gfxContext* aContext);
+  static bool SetupCairoStrokePaint(nsIFrame* aFrame, gfxContext* aContext,
+                                    gfxTextObjectPaint *aObjectPaint = nullptr);
 
   /*
    * @return false if there is no stroke
    */
   static bool HasStroke(nsIFrame* aFrame);
 
   static float GetStrokeWidth(nsIFrame* aFrame);