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 104427 a0c93525cfba43767f05865415ed8c786afa39be
parent 104426 1ea9c7730f9754bf321d1d9365bffee3de06bf35
child 104428 c4b0ce0436fdaf551845a7749b790fc811af7d9b
push id23426
push userryanvm@gmail.com
push dateFri, 07 Sep 2012 00:38:20 +0000
treeherdermozilla-central@233441ff53af [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs719286
milestone18.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 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);