Bug 1417699 - Replace hash map with tagged union r=mstange
authorDoug Thayer <dothayer@mozilla.com>
Wed, 19 Sep 2018 17:18:16 +0000
changeset 495719 4f3360c4f1049712a9ab641078838bc0e197e0c4
parent 495718 6f43cff9efd7348aa54fa980d19dd98cc480e2e8
child 495720 87694c630c01f0ce1bd244d6cb53cde3b6785100
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs1417699
milestone64.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 1417699 - Replace hash map with tagged union r=mstange This replaces the hash map of attributes with a tagged union. In this case, all filter attributes will be stored in line, with the exception of some more complex attributes which have an internal nsTArray of floats. This should help avoid all the hashing and extra heap allocations. Depends on D4899 Differential Revision: https://phabricator.services.mozilla.com/D4900
dom/svg/SVGComponentTransferFunctionElement.h
dom/svg/SVGFEBlendElement.cpp
dom/svg/SVGFEColorMatrixElement.cpp
dom/svg/SVGFEComponentTransferElement.cpp
dom/svg/SVGFECompositeElement.cpp
dom/svg/SVGFEConvolveMatrixElement.cpp
dom/svg/SVGFEDiffuseLightingElement.cpp
dom/svg/SVGFEDisplacementMapElement.cpp
dom/svg/SVGFEDistantLightElement.cpp
dom/svg/SVGFEDistantLightElement.h
dom/svg/SVGFEDropShadowElement.cpp
dom/svg/SVGFEFloodElement.cpp
dom/svg/SVGFEGaussianBlurElement.cpp
dom/svg/SVGFEImageElement.cpp
dom/svg/SVGFEMorphologyElement.cpp
dom/svg/SVGFEOffsetElement.cpp
dom/svg/SVGFEPointLightElement.cpp
dom/svg/SVGFEPointLightElement.h
dom/svg/SVGFESpecularLightingElement.cpp
dom/svg/SVGFESpotLightElement.cpp
dom/svg/SVGFESpotLightElement.h
dom/svg/SVGFETurbulenceElement.cpp
dom/svg/nsSVGFilters.cpp
dom/svg/nsSVGFilters.h
gfx/ipc/GfxMessageUtils.h
gfx/src/FilterSupport.cpp
gfx/src/FilterSupport.h
layout/svg/nsCSSFilterInstance.cpp
layout/svg/nsFilterInstance.cpp
--- a/dom/svg/SVGComponentTransferFunctionElement.h
+++ b/dom/svg/SVGComponentTransferFunctionElement.h
@@ -31,29 +31,29 @@ protected:
   explicit SVGComponentTransferFunctionElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
     : SVGComponentTransferFunctionElementBase(aNodeInfo)
   {
   }
 
   virtual ~SVGComponentTransferFunctionElement() {}
 
 public:
-  typedef gfx::AttributeMap AttributeMap;
+  typedef gfx::ComponentTransferAttributes ComponentTransferAttributes;
 
   // interfaces:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_SVG_FE_COMPONENT_TRANSFER_FUNCTION_ELEMENT_CID)
 
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual bool AttributeAffectsRendering(
           int32_t aNameSpaceID, nsAtom* aAttribute) const override;
 
   virtual int32_t GetChannel() = 0;
 
-  AttributeMap ComputeAttributes();
+  void ComputeAttributes(int32_t aChannel, ComponentTransferAttributes& aAttributes);
 
   // WebIDL
   virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override = 0;
   already_AddRefed<SVGAnimatedEnumeration> Type();
   already_AddRefed<DOMSVGAnimatedNumberList> TableValues();
   already_AddRefed<SVGAnimatedNumber> Slope();
   already_AddRefed<SVGAnimatedNumber> Intercept();
   already_AddRefed<SVGAnimatedNumber> Amplitude();
--- a/dom/svg/SVGFEBlendElement.cpp
+++ b/dom/svg/SVGFEBlendElement.cpp
@@ -85,17 +85,19 @@ SVGFEBlendElement::Mode()
 FilterPrimitiveDescription
 SVGFEBlendElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance,
                                            const IntRect& aFilterSubregion,
                                            const nsTArray<bool>& aInputsAreTainted,
                                            nsTArray<RefPtr<SourceSurface>>& aInputImages)
 {
   uint32_t mode = mEnumAttributes[MODE].GetAnimValue();
   FilterPrimitiveDescription descr(PrimitiveType::Blend);
-  descr.Attributes().Set(eBlendBlendmode, mode);
+  BlendAttributes attributes;
+  attributes.mBlendMode = mode;
+  descr.Attributes() = AsVariant(attributes);
   return descr;
 }
 
 bool
 SVGFEBlendElement::AttributeAffectsRendering(int32_t aNameSpaceID,
                                              nsAtom* aAttribute) const
 {
   return SVGFEBlendElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
--- a/dom/svg/SVGFEColorMatrixElement.cpp
+++ b/dom/svg/SVGFEColorMatrixElement.cpp
@@ -90,35 +90,36 @@ SVGFEColorMatrixElement::GetPrimitiveDes
                                                  const IntRect& aFilterSubregion,
                                                  const nsTArray<bool>& aInputsAreTainted,
                                                  nsTArray<RefPtr<SourceSurface>>& aInputImages)
 {
   uint32_t type = mEnumAttributes[TYPE].GetAnimValue();
   const SVGNumberList &values = mNumberListAttributes[VALUES].GetAnimValue();
 
   FilterPrimitiveDescription descr(PrimitiveType::ColorMatrix);
+  ColorMatrixAttributes atts;
   if (!mNumberListAttributes[VALUES].IsExplicitlySet() &&
       (type == SVG_FECOLORMATRIX_TYPE_MATRIX ||
        type == SVG_FECOLORMATRIX_TYPE_SATURATE ||
        type == SVG_FECOLORMATRIX_TYPE_HUE_ROTATE)) {
-    descr.Attributes().Set(eColorMatrixType, (uint32_t)SVG_FECOLORMATRIX_TYPE_MATRIX);
+    atts.mType = (uint32_t)SVG_FECOLORMATRIX_TYPE_MATRIX;
     static const float identityMatrix[] =
       { 1, 0, 0, 0, 0,
         0, 1, 0, 0, 0,
         0, 0, 1, 0, 0,
         0, 0, 0, 1, 0 };
-    descr.Attributes().Set(eColorMatrixValues, identityMatrix, 20);
+    atts.mValues.AppendElements(identityMatrix, 20);
   } else {
-    descr.Attributes().Set(eColorMatrixType, type);
+    atts.mType = type;
     if (values.Length()) {
-      descr.Attributes().Set(eColorMatrixValues, &values[0], values.Length());
-    } else {
-      descr.Attributes().Set(eColorMatrixValues, nullptr, 0);
+      atts.mValues.AppendElements(&values[0], values.Length());
     }
   }
+
+  descr.Attributes() = AsVariant(std::move(atts));
   return descr;
 }
 
 bool
 SVGFEColorMatrixElement::AttributeAffectsRendering(int32_t aNameSpaceID,
                                                    nsAtom* aAttribute) const
 {
   return SVGFEColorMatrixElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
--- a/dom/svg/SVGFEComponentTransferElement.cpp
+++ b/dom/svg/SVGFEComponentTransferElement.cpp
@@ -67,34 +67,27 @@ SVGFEComponentTransferElement::GetPrimit
     RefPtr<SVGComponentTransferFunctionElement> child;
     CallQueryInterface(childContent,
             (SVGComponentTransferFunctionElement**)getter_AddRefs(child));
     if (child) {
       childForChannel[child->GetChannel()] = child;
     }
   }
 
-  static const AttributeName attributeNames[4] = {
-    eComponentTransferFunctionR,
-    eComponentTransferFunctionG,
-    eComponentTransferFunctionB,
-    eComponentTransferFunctionA
-  };
-
   FilterPrimitiveDescription descr(PrimitiveType::ComponentTransfer);
+  ComponentTransferAttributes atts;
   for (int32_t i = 0; i < 4; i++) {
     if (childForChannel[i]) {
-      descr.Attributes().Set(attributeNames[i], childForChannel[i]->ComputeAttributes());
+      childForChannel[i]->ComputeAttributes(i, atts);
     } else {
-      AttributeMap functionAttributes;
-      functionAttributes.Set(eComponentTransferFunctionType,
-                             (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY);
-      descr.Attributes().Set(attributeNames[i], std::move(functionAttributes));
+      atts.mTypes[i] =
+        (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY;
     }
   }
+  descr.Attributes() = AsVariant(std::move(atts));
   return descr;
 }
 
 bool
 SVGFEComponentTransferElement::AttributeAffectsRendering(int32_t aNameSpaceID,
                                                          nsAtom* aAttribute) const
 {
   return SVGFEComponentTransferElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
--- a/dom/svg/SVGFECompositeElement.cpp
+++ b/dom/svg/SVGFECompositeElement.cpp
@@ -111,25 +111,27 @@ SVGFECompositeElement::SetK(float k1, fl
 
 FilterPrimitiveDescription
 SVGFECompositeElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance,
                                                const IntRect& aFilterSubregion,
                                                const nsTArray<bool>& aInputsAreTainted,
                                                nsTArray<RefPtr<SourceSurface>>& aInputImages)
 {
   FilterPrimitiveDescription descr(PrimitiveType::Composite);
+  CompositeAttributes atts;
   uint32_t op = mEnumAttributes[OPERATOR].GetAnimValue();
-  descr.Attributes().Set(eCompositeOperator, op);
+  atts.mOperator = op;
 
   if (op == SVG_FECOMPOSITE_OPERATOR_ARITHMETIC) {
     float k[4];
     GetAnimatedNumberValues(k, k+1, k+2, k+3, nullptr);
-    descr.Attributes().Set(eCompositeCoefficients, k, 4);
+    atts.mCoefficients.AppendElements(k, 4);
   }
 
+  descr.Attributes() = AsVariant(std::move(atts));
   return descr;
 }
 
 bool
 SVGFECompositeElement::AttributeAffectsRendering(int32_t aNameSpaceID,
                                                  nsAtom* aAttribute) const
 {
   return SVGFECompositeElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
--- a/dom/svg/SVGFEConvolveMatrixElement.cpp
+++ b/dom/svg/SVGFEConvolveMatrixElement.cpp
@@ -234,26 +234,27 @@ SVGFEConvolveMatrixElement::GetPrimitive
 
   if (kernelUnitLength.width <= 0 || kernelUnitLength.height <= 0) {
     // According to spec, A negative or zero value is an error. See link below for details.
     // https://www.w3.org/TR/SVG/filters.html#feConvolveMatrixElementKernelUnitLengthAttribute
     return failureDescription;
   }
 
   FilterPrimitiveDescription descr(PrimitiveType::ConvolveMatrix);
-  AttributeMap& atts = descr.Attributes();
-  atts.Set(eConvolveMatrixKernelSize, IntSize(orderX, orderY));
-  atts.Set(eConvolveMatrixKernelMatrix, &kernelMatrix[0], kmLength);
-  atts.Set(eConvolveMatrixDivisor, divisor);
-  atts.Set(eConvolveMatrixBias, bias);
-  atts.Set(eConvolveMatrixTarget, IntPoint(targetX, targetY));
-  atts.Set(eConvolveMatrixEdgeMode, edgeMode);
-  atts.Set(eConvolveMatrixKernelUnitLength, kernelUnitLength);
-  atts.Set(eConvolveMatrixPreserveAlpha, preserveAlpha);
+  ConvolveMatrixAttributes atts;
+  atts.mKernelSize = IntSize(orderX, orderY);
+  atts.mKernelMatrix.AppendElements(&kernelMatrix[0], kmLength);
+  atts.mDivisor = divisor;
+  atts.mBias = bias;
+  atts.mTarget = IntPoint(targetX, targetY);
+  atts.mEdgeMode = edgeMode;
+  atts.mKernelUnitLength = kernelUnitLength;
+  atts.mPreserveAlpha = preserveAlpha;
 
+  descr.Attributes() = AsVariant(std::move(atts));
   return descr;
 }
 
 bool
 SVGFEConvolveMatrixElement::AttributeAffectsRendering(int32_t aNameSpaceID,
                                                       nsAtom* aAttribute) const
 {
   return SVGFEConvolveMatrixElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
--- a/dom/svg/SVGFEDiffuseLightingElement.cpp
+++ b/dom/svg/SVGFEDiffuseLightingElement.cpp
@@ -65,18 +65,24 @@ FilterPrimitiveDescription
 SVGFEDiffuseLightingElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance,
                                                      const IntRect& aFilterSubregion,
                                                      const nsTArray<bool>& aInputsAreTainted,
                                                      nsTArray<RefPtr<SourceSurface>>& aInputImages)
 {
   float diffuseConstant = mNumberAttributes[DIFFUSE_CONSTANT].GetAnimValue();
 
   FilterPrimitiveDescription descr(PrimitiveType::DiffuseLighting);
-  descr.Attributes().Set(eDiffuseLightingDiffuseConstant, diffuseConstant);
-  return AddLightingAttributes(descr, aInstance);
+  DiffuseLightingAttributes atts;
+  atts.mLightingConstant = diffuseConstant;
+  if (!AddLightingAttributes(&atts, aInstance)) {
+    return FilterPrimitiveDescription(PrimitiveType::Empty);
+  }
+
+  descr.Attributes() = AsVariant(std::move(atts));
+  return descr;
 }
 
 bool
 SVGFEDiffuseLightingElement::AttributeAffectsRendering(int32_t aNameSpaceID,
                                                        nsAtom* aAttribute) const
 {
   return SVGFEDiffuseLightingElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
          (aNameSpaceID == kNameSpaceID_None &&
--- a/dom/svg/SVGFEDisplacementMapElement.cpp
+++ b/dom/svg/SVGFEDisplacementMapElement.cpp
@@ -96,28 +96,32 @@ SVGFEDisplacementMapElement::GetPrimitiv
                                                      const IntRect& aFilterSubregion,
                                                      const nsTArray<bool>& aInputsAreTainted,
                                                      nsTArray<RefPtr<SourceSurface>>& aInputImages)
 {
   if (aInputsAreTainted[1]) {
     // If the map is tainted, refuse to apply the effect and act as a
     // pass-through filter instead, as required by the spec.
     FilterPrimitiveDescription descr(PrimitiveType::Offset);
-    descr.Attributes().Set(eOffsetOffset, IntPoint(0, 0));
+    OffsetAttributes atts;
+    atts.mValue = IntPoint(0, 0);
+    descr.Attributes() = AsVariant(std::move(atts));
     return descr;
   }
 
   float scale = aInstance->GetPrimitiveNumber(SVGContentUtils::XY,
                                               &mNumberAttributes[SCALE]);
   uint32_t xChannel = mEnumAttributes[CHANNEL_X].GetAnimValue();
   uint32_t yChannel = mEnumAttributes[CHANNEL_Y].GetAnimValue();
   FilterPrimitiveDescription descr(PrimitiveType::DisplacementMap);
-  descr.Attributes().Set(eDisplacementMapScale, scale);
-  descr.Attributes().Set(eDisplacementMapXChannel, xChannel);
-  descr.Attributes().Set(eDisplacementMapYChannel, yChannel);
+  DisplacementMapAttributes atts;
+  atts.mScale = scale;
+  atts.mXChannel = xChannel;
+  atts.mYChannel = yChannel;
+  descr.Attributes() = AsVariant(std::move(atts));
   return descr;
 }
 
 bool
 SVGFEDisplacementMapElement::AttributeAffectsRendering(int32_t aNameSpaceID,
                                                        nsAtom* aAttribute) const
 {
   return SVGFEDisplacementMapElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
--- a/dom/svg/SVGFEDistantLightElement.cpp
+++ b/dom/svg/SVGFEDistantLightElement.cpp
@@ -39,27 +39,27 @@ bool
 SVGFEDistantLightElement::AttributeAffectsRendering(int32_t aNameSpaceID,
                                                     nsAtom* aAttribute) const
 {
   return aNameSpaceID == kNameSpaceID_None &&
          (aAttribute == nsGkAtoms::azimuth ||
           aAttribute == nsGkAtoms::elevation);
 }
 
-AttributeMap
-SVGFEDistantLightElement::ComputeLightAttributes(nsSVGFilterInstance* aInstance)
+LightType
+SVGFEDistantLightElement::ComputeLightAttributes(nsSVGFilterInstance* aInstance,
+                                                 nsTArray<float>& aFloatAttributes)
 {
   float azimuth, elevation;
   GetAnimatedNumberValues(&azimuth, &elevation, nullptr);
 
-  AttributeMap map;
-  map.Set(eLightType, (uint32_t)eLightTypeDistant);
-  map.Set(eDistantLightAzimuth, azimuth);
-  map.Set(eDistantLightElevation, elevation);
-  return map;
+  aFloatAttributes.SetLength(kDistantLightNumAttributes);
+  aFloatAttributes[kDistantLightAzimuthIndex] = azimuth;
+  aFloatAttributes[kDistantLightElevationIndex] = elevation;
+  return LightType::Distant;
 }
 
 already_AddRefed<SVGAnimatedNumber>
 SVGFEDistantLightElement::Azimuth()
 {
   return mNumberAttributes[AZIMUTH].ToDOMAnimatedNumber(this);
 }
 
--- a/dom/svg/SVGFEDistantLightElement.h
+++ b/dom/svg/SVGFEDistantLightElement.h
@@ -25,17 +25,19 @@ class SVGFEDistantLightElement : public 
 protected:
   explicit SVGFEDistantLightElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
     : SVGFEDistantLightElementBase(aNodeInfo)
   {
   }
   virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
 public:
-  virtual AttributeMap ComputeLightAttributes(nsSVGFilterInstance* aInstance) override;
+
+  virtual mozilla::gfx::LightType ComputeLightAttributes(nsSVGFilterInstance* aInstance,
+                                                         nsTArray<float>& aFloatAttributes) override;
   virtual bool AttributeAffectsRendering(
           int32_t aNameSpaceID, nsAtom* aAttribute) const override;
 
   virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
 
   // WebIDL
   already_AddRefed<SVGAnimatedNumber> Azimuth();
   already_AddRefed<SVGAnimatedNumber> Elevation();
--- a/dom/svg/SVGFEDropShadowElement.cpp
+++ b/dom/svg/SVGFEDropShadowElement.cpp
@@ -99,28 +99,30 @@ SVGFEDropShadowElement::GetPrimitiveDesc
   }
 
   IntPoint offset(int32_t(aInstance->GetPrimitiveNumber(
                             SVGContentUtils::X, &mNumberAttributes[DX])),
                   int32_t(aInstance->GetPrimitiveNumber(
                             SVGContentUtils::Y, &mNumberAttributes[DY])));
 
   FilterPrimitiveDescription descr(PrimitiveType::DropShadow);
-  descr.Attributes().Set(eDropShadowStdDeviation, Size(stdX, stdY));
-  descr.Attributes().Set(eDropShadowOffset, offset);
+  DropShadowAttributes atts;
+  atts.mStdDeviation = Size(stdX, stdY);
+  atts.mOffset = offset;
 
   nsIFrame* frame = GetPrimaryFrame();
   if (frame) {
     const nsStyleSVGReset* styleSVGReset = frame->Style()->StyleSVGReset();
     Color color(Color::FromABGR(styleSVGReset->mFloodColor.CalcColor(frame)));
     color.a *= styleSVGReset->mFloodOpacity;
-    descr.Attributes().Set(eDropShadowColor, color);
+    atts.mColor = color;
   } else {
-    descr.Attributes().Set(eDropShadowColor, Color());
+    atts.mColor = Color();
   }
+  descr.Attributes() = AsVariant(std::move(atts));
   return descr;
 }
 
 bool
 SVGFEDropShadowElement::AttributeAffectsRendering(int32_t aNameSpaceID,
                                                   nsAtom* aAttribute) const
 {
   return SVGFEDropShadowElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
--- a/dom/svg/SVGFEFloodElement.cpp
+++ b/dom/svg/SVGFEFloodElement.cpp
@@ -36,25 +36,27 @@ NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEFlo
 
 FilterPrimitiveDescription
 SVGFEFloodElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance,
                                            const IntRect& aFilterSubregion,
                                            const nsTArray<bool>& aInputsAreTainted,
                                            nsTArray<RefPtr<SourceSurface>>& aInputImages)
 {
   FilterPrimitiveDescription descr(PrimitiveType::Flood);
+  FloodAttributes atts;
   nsIFrame* frame = GetPrimaryFrame();
   if (frame) {
     const nsStyleSVGReset* styleSVGReset = frame->Style()->StyleSVGReset();
     Color color(Color::FromABGR(styleSVGReset->mFloodColor.CalcColor(frame)));
     color.a *= styleSVGReset->mFloodOpacity;
-    descr.Attributes().Set(eFloodColor, color);
+    atts.mColor = color;
   } else {
-    descr.Attributes().Set(eFloodColor, Color());
+    atts.mColor = Color();
   }
+  descr.Attributes() = AsVariant(std::move(atts));
   return descr;
 }
 
 //----------------------------------------------------------------------
 // nsIContent methods
 
 NS_IMETHODIMP_(bool)
 SVGFEFloodElement::IsAttributeMapped(const nsAtom* name) const
--- a/dom/svg/SVGFEGaussianBlurElement.cpp
+++ b/dom/svg/SVGFEGaussianBlurElement.cpp
@@ -76,17 +76,19 @@ SVGFEGaussianBlurElement::GetPrimitiveDe
   float stdY = aInstance->GetPrimitiveNumber(SVGContentUtils::Y,
                                              &mNumberPairAttributes[STD_DEV],
                                              nsSVGNumberPair::eSecond);
   if (stdX < 0 || stdY < 0) {
     return FilterPrimitiveDescription(PrimitiveType::Empty);
   }
 
   FilterPrimitiveDescription descr(PrimitiveType::GaussianBlur);
-  descr.Attributes().Set(eGaussianBlurStdDeviation, Size(stdX, stdY));
+  GaussianBlurAttributes atts;
+  atts.mStdDeviation = Size(stdX, stdY);
+  descr.Attributes() = AsVariant(std::move(atts));
   return descr;
 }
 
 bool
 SVGFEGaussianBlurElement::AttributeAffectsRendering(int32_t aNameSpaceID,
                                                     nsAtom* aAttribute) const
 {
   return SVGFEGaussianBlurElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
--- a/dom/svg/SVGFEImageElement.cpp
+++ b/dom/svg/SVGFEImageElement.cpp
@@ -248,23 +248,25 @@ SVGFEImageElement::GetPrimitiveDescripti
                                          0, 0, nativeSize.width, nativeSize.height,
                                          mPreserveAspectRatio);
   Matrix TM = viewBoxTM;
   TM.PostTranslate(aFilterSubregion.x, aFilterSubregion.y);
 
   SamplingFilter samplingFilter = nsLayoutUtils::GetSamplingFilterForFrame(frame);
 
   FilterPrimitiveDescription descr(PrimitiveType::Image);
-  descr.Attributes().Set(eImageFilter, (uint32_t)samplingFilter);
-  descr.Attributes().Set(eImageTransform, TM);
+  ImageAttributes atts;
+  atts.mFilter = (uint32_t)samplingFilter;
+  atts.mTransform = TM;
 
   // Append the image to aInputImages and store its index in the description.
   size_t imageIndex = aInputImages.Length();
   aInputImages.AppendElement(image);
-  descr.Attributes().Set(eImageInputIndex, (uint32_t)imageIndex);
+  atts.mInputIndex = (uint32_t)imageIndex;
+  descr.Attributes() = AsVariant(std::move(atts));
 
   return descr;
 }
 
 bool
 SVGFEImageElement::AttributeAffectsRendering(int32_t aNameSpaceID,
                                              nsAtom* aAttribute) const
 {
--- a/dom/svg/SVGFEMorphologyElement.cpp
+++ b/dom/svg/SVGFEMorphologyElement.cpp
@@ -116,19 +116,20 @@ FilterPrimitiveDescription
 SVGFEMorphologyElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance,
                                                 const IntRect& aFilterSubregion,
                                                 const nsTArray<bool>& aInputsAreTainted,
                                                 nsTArray<RefPtr<SourceSurface>>& aInputImages)
 {
   int32_t rx, ry;
   GetRXY(&rx, &ry, *aInstance);
   FilterPrimitiveDescription descr(PrimitiveType::Morphology);
-  descr.Attributes().Set(eMorphologyRadii, Size(rx, ry));
-  descr.Attributes().Set(eMorphologyOperator,
-                         (uint32_t)mEnumAttributes[OPERATOR].GetAnimValue());
+  MorphologyAttributes atts;
+  atts.mRadii = Size(rx, ry);
+  atts.mOperator = (uint32_t)mEnumAttributes[OPERATOR].GetAnimValue();
+  descr.Attributes() = AsVariant(std::move(atts));
   return descr;
 }
 
 bool
 SVGFEMorphologyElement::AttributeAffectsRendering(int32_t aNameSpaceID,
                                                   nsAtom* aAttribute) const
 {
   return SVGFEMorphologyElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
--- a/dom/svg/SVGFEOffsetElement.cpp
+++ b/dom/svg/SVGFEOffsetElement.cpp
@@ -62,21 +62,23 @@ SVGFEOffsetElement::Dy()
 
 FilterPrimitiveDescription
 SVGFEOffsetElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance,
                                             const IntRect& aFilterSubregion,
                                             const nsTArray<bool>& aInputsAreTainted,
                                             nsTArray<RefPtr<SourceSurface>>& aInputImages)
 {
   FilterPrimitiveDescription descr(PrimitiveType::Offset);
+  OffsetAttributes atts;
   IntPoint offset(int32_t(aInstance->GetPrimitiveNumber(
                             SVGContentUtils::X, &mNumberAttributes[DX])),
                   int32_t(aInstance->GetPrimitiveNumber(
                             SVGContentUtils::Y, &mNumberAttributes[DY])));
-  descr.Attributes().Set(eOffsetOffset, offset);
+  atts.mValue = offset;
+  descr.Attributes() = AsVariant(std::move(atts));
   return descr;
 }
 
 bool
 SVGFEOffsetElement::AttributeAffectsRendering(int32_t aNameSpaceID,
                                               nsAtom* aAttribute) const
 {
   return SVGFEOffsetElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
--- a/dom/svg/SVGFEPointLightElement.cpp
+++ b/dom/svg/SVGFEPointLightElement.cpp
@@ -43,26 +43,28 @@ SVGFEPointLightElement::AttributeAffects
   return aNameSpaceID == kNameSpaceID_None &&
          (aAttribute == nsGkAtoms::x ||
           aAttribute == nsGkAtoms::y ||
           aAttribute == nsGkAtoms::z);
 }
 
 //----------------------------------------------------------------------
 
-AttributeMap
-SVGFEPointLightElement::ComputeLightAttributes(nsSVGFilterInstance* aInstance)
+LightType
+SVGFEPointLightElement::ComputeLightAttributes(nsSVGFilterInstance* aInstance,
+                                               nsTArray<float>& aFloatAttributes)
 {
   Point3D lightPos;
   GetAnimatedNumberValues(&lightPos.x, &lightPos.y, &lightPos.z, nullptr);
-
-  AttributeMap map;
-  map.Set(eLightType, (uint32_t)eLightTypePoint);
-  map.Set(ePointLightPosition, aInstance->ConvertLocation(lightPos));
-  return map;
+  lightPos = aInstance->ConvertLocation(lightPos);
+  aFloatAttributes.SetLength(kPointLightNumAttributes);
+  aFloatAttributes[kPointLightPositionXIndex] = lightPos.x;
+  aFloatAttributes[kPointLightPositionYIndex] = lightPos.y;
+  aFloatAttributes[kPointLightPositionZIndex] = lightPos.z;
+  return LightType::Point;
 }
 
 already_AddRefed<SVGAnimatedNumber>
 SVGFEPointLightElement::X()
 {
   return mNumberAttributes[ATTR_X].ToDOMAnimatedNumber(this);
 }
 
--- a/dom/svg/SVGFEPointLightElement.h
+++ b/dom/svg/SVGFEPointLightElement.h
@@ -25,17 +25,18 @@ class SVGFEPointLightElement : public SV
 protected:
   explicit SVGFEPointLightElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
     : SVGFEPointLightElementBase(aNodeInfo)
   {
   }
   virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override;
 
 public:
-  virtual AttributeMap ComputeLightAttributes(nsSVGFilterInstance* aInstance) override;
+  virtual mozilla::gfx::LightType ComputeLightAttributes(nsSVGFilterInstance* aInstance,
+                                                         nsTArray<float>& aFloatAttributes) override;
   virtual bool AttributeAffectsRendering(
           int32_t aNameSpaceID, nsAtom* aAttribute) const override;
 
   virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
 
   // WebIDL
   already_AddRefed<SVGAnimatedNumber> X();
   already_AddRefed<SVGAnimatedNumber> Y();
--- a/dom/svg/SVGFESpecularLightingElement.cpp
+++ b/dom/svg/SVGFESpecularLightingElement.cpp
@@ -78,19 +78,25 @@ SVGFESpecularLightingElement::GetPrimiti
   float specularConstant = mNumberAttributes[SPECULAR_CONSTANT].GetAnimValue();
 
   // specification defined range (15.22)
   if (specularExponent < 1 || specularExponent > 128) {
     return FilterPrimitiveDescription(PrimitiveType::Empty);
   }
 
   FilterPrimitiveDescription descr(PrimitiveType::SpecularLighting);
-  descr.Attributes().Set(eSpecularLightingSpecularConstant, specularConstant);
-  descr.Attributes().Set(eSpecularLightingSpecularExponent, specularExponent);
-  return AddLightingAttributes(descr, aInstance);
+  SpecularLightingAttributes atts;
+  atts.mLightingConstant = specularConstant;
+  atts.mSpecularExponent = specularExponent;
+  if (!AddLightingAttributes(static_cast<DiffuseLightingAttributes*>(&atts), aInstance)) {
+    return FilterPrimitiveDescription(PrimitiveType::Empty);
+  }
+
+  descr.Attributes() = AsVariant(std::move(atts));
+  return descr;
 }
 
 bool
 SVGFESpecularLightingElement::AttributeAffectsRendering(int32_t aNameSpaceID,
                                                         nsAtom* aAttribute) const
 {
   return SVGFESpecularLightingElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
          (aNameSpaceID == kNameSpaceID_None &&
--- a/dom/svg/SVGFESpotLightElement.cpp
+++ b/dom/svg/SVGFESpotLightElement.cpp
@@ -53,36 +53,35 @@ SVGFESpotLightElement::AttributeAffectsR
           aAttribute == nsGkAtoms::pointsAtY ||
           aAttribute == nsGkAtoms::pointsAtZ ||
           aAttribute == nsGkAtoms::specularExponent ||
           aAttribute == nsGkAtoms::limitingConeAngle);
 }
 
 //----------------------------------------------------------------------
 
-AttributeMap
-SVGFESpotLightElement::ComputeLightAttributes(nsSVGFilterInstance* aInstance)
+LightType
+SVGFESpotLightElement::ComputeLightAttributes(nsSVGFilterInstance* aInstance,
+                                              nsTArray<float>& aFloatAttributes)
 {
-  Point3D lightPos, pointsAt;
-  float specularExponent, limitingConeAngle;
-  GetAnimatedNumberValues(&lightPos.x, &lightPos.y, &lightPos.z,
-                          &pointsAt.x, &pointsAt.y, &pointsAt.z,
-                          &specularExponent, &limitingConeAngle,
+  aFloatAttributes.SetLength(kSpotLightNumAttributes);
+  GetAnimatedNumberValues(&aFloatAttributes[kSpotLightPositionXIndex],
+                          &aFloatAttributes[kSpotLightPositionYIndex],
+                          &aFloatAttributes[kSpotLightPositionZIndex],
+                          &aFloatAttributes[kSpotLightPointsAtXIndex],
+                          &aFloatAttributes[kSpotLightPointsAtYIndex],
+                          &aFloatAttributes[kSpotLightPointsAtZIndex],
+                          &aFloatAttributes[kSpotLightFocusIndex],
+                          &aFloatAttributes[kSpotLightLimitingConeAngleIndex],
                           nullptr);
   if (!mNumberAttributes[SVGFESpotLightElement::LIMITING_CONE_ANGLE].IsExplicitlySet()) {
-    limitingConeAngle = 90;
+    aFloatAttributes[kSpotLightLimitingConeAngleIndex] = 90;
   }
 
-  AttributeMap map;
-  map.Set(eLightType, (uint32_t)eLightTypeSpot);
-  map.Set(eSpotLightPosition, aInstance->ConvertLocation(lightPos));
-  map.Set(eSpotLightPointsAt, aInstance->ConvertLocation(pointsAt));
-  map.Set(eSpotLightFocus, specularExponent);
-  map.Set(eSpotLightLimitingConeAngle, limitingConeAngle);
-  return map;
+  return LightType::Spot;
 }
 
 already_AddRefed<SVGAnimatedNumber>
 SVGFESpotLightElement::X()
 {
   return mNumberAttributes[ATTR_X].ToDOMAnimatedNumber(this);
 }
 
--- a/dom/svg/SVGFESpotLightElement.h
+++ b/dom/svg/SVGFESpotLightElement.h
@@ -26,17 +26,18 @@ class SVGFESpotLightElement : public SVG
 protected:
   explicit SVGFESpotLightElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
     : SVGFESpotLightElementBase(aNodeInfo)
   {
   }
   virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
 public:
-  virtual AttributeMap ComputeLightAttributes(nsSVGFilterInstance* aInstance) override;
+  virtual mozilla::gfx::LightType ComputeLightAttributes(nsSVGFilterInstance* aInstance,
+                                                         nsTArray<float>& aFloatAttributes) override;
   virtual bool AttributeAffectsRendering(
           int32_t aNameSpaceID, nsAtom* aAttribute) const override;
 
   virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
 
   // WebIDL
   already_AddRefed<SVGAnimatedNumber> X();
   already_AddRefed<SVGAnimatedNumber> Y();
--- a/dom/svg/SVGFETurbulenceElement.cpp
+++ b/dom/svg/SVGFETurbulenceElement.cpp
@@ -134,17 +134,19 @@ SVGFETurbulenceElement::GetPrimitiveDesc
 
   if (fX == 0 && fY == 0) {
     // A base frequency of zero results in transparent black for
     // type="turbulence" and in 50% alpha 50% gray for type="fractalNoise".
     if (type == SVG_TURBULENCE_TYPE_TURBULENCE) {
       return FilterPrimitiveDescription(PrimitiveType::Empty);
     }
     FilterPrimitiveDescription descr(PrimitiveType::Flood);
-    descr.Attributes().Set(eFloodColor, Color(0.5, 0.5, 0.5, 0.5));
+    FloodAttributes atts;
+    atts.mColor = Color(0.5, 0.5, 0.5, 0.5);
+    descr.Attributes() = AsVariant(std::move(atts));
     return descr;
   }
 
   // We interpret the base frequency as relative to user space units. In other
   // words, we consider one turbulence base period to be 1 / fX user space
   // units wide and 1 / fY user space units high. We do not scale the frequency
   // depending on the filter primitive region.
   // We now convert the frequency from user space to filter space.
@@ -156,22 +158,24 @@ SVGFETurbulenceElement::GetPrimitiveDesc
                                  fX == 0 ? 1 : (1 / fX),
                                  fY == 0 ? 1 : (1 / fY));
   gfxRect firstPeriodInFilterSpace = aInstance->UserSpaceToFilterSpace(firstPeriodInUserSpace);
   Size frequencyInFilterSpace(fX == 0 ? 0 : (1 / firstPeriodInFilterSpace.width),
                               fY == 0 ? 0 : (1 / firstPeriodInFilterSpace.height));
   gfxPoint offset = firstPeriodInFilterSpace.TopLeft();
 
   FilterPrimitiveDescription descr(PrimitiveType::Turbulence);
-  descr.Attributes().Set(eTurbulenceOffset, IntPoint::Truncate(offset.x, offset.y));
-  descr.Attributes().Set(eTurbulenceBaseFrequency, frequencyInFilterSpace);
-  descr.Attributes().Set(eTurbulenceSeed, seed);
-  descr.Attributes().Set(eTurbulenceNumOctaves, octaves);
-  descr.Attributes().Set(eTurbulenceStitchable, stitch == SVG_STITCHTYPE_STITCH);
-  descr.Attributes().Set(eTurbulenceType, type);
+  TurbulenceAttributes atts;
+  atts.mOffset = IntPoint::Truncate(offset.x, offset.y);
+  atts.mBaseFrequency = frequencyInFilterSpace;
+  atts.mSeed = seed;
+  atts.mOctaves = octaves;
+  atts.mStitchable = stitch == SVG_STITCHTYPE_STITCH;
+  atts.mType = type;
+  descr.Attributes() = AsVariant(std::move(atts));
   return descr;
 }
 
 bool
 SVGFETurbulenceElement::AttributeAffectsRendering(int32_t aNameSpaceID,
                                                     nsAtom* aAttribute) const
 {
   return SVGFETurbulenceElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
--- a/dom/svg/nsSVGFilters.cpp
+++ b/dom/svg/nsSVGFilters.cpp
@@ -302,41 +302,51 @@ SVGComponentTransferFunctionElement::Exp
 }
 
 already_AddRefed<SVGAnimatedNumber>
 SVGComponentTransferFunctionElement::Offset()
 {
   return mNumberAttributes[OFFSET].ToDOMAnimatedNumber(this);
 }
 
-AttributeMap
-SVGComponentTransferFunctionElement::ComputeAttributes()
+void
+SVGComponentTransferFunctionElement::ComputeAttributes(int32_t aChannel,
+                                                       ComponentTransferAttributes& aAttributes)
 {
   uint32_t type = mEnumAttributes[TYPE].GetAnimValue();
 
   float slope, intercept, amplitude, exponent, offset;
   GetAnimatedNumberValues(&slope, &intercept, &amplitude,
                           &exponent, &offset, nullptr);
 
   const SVGNumberList &tableValues =
     mNumberListAttributes[TABLEVALUES].GetAnimValue();
 
-  AttributeMap map;
-  map.Set(eComponentTransferFunctionType, type);
-  map.Set(eComponentTransferFunctionSlope, slope);
-  map.Set(eComponentTransferFunctionIntercept, intercept);
-  map.Set(eComponentTransferFunctionAmplitude, amplitude);
-  map.Set(eComponentTransferFunctionExponent, exponent);
-  map.Set(eComponentTransferFunctionOffset, offset);
-  if (tableValues.Length()) {
-    map.Set(eComponentTransferFunctionTableValues, &tableValues[0], tableValues.Length());
-  } else {
-    map.Set(eComponentTransferFunctionTableValues, nullptr, 0);
+  aAttributes.mTypes[aChannel] = (uint8_t)type;
+  switch (type) {
+    case SVG_FECOMPONENTTRANSFER_TYPE_LINEAR: {
+      aAttributes.mValues[aChannel].SetLength(2);
+      aAttributes.mValues[aChannel][kComponentTransferSlopeIndex] = slope;
+      aAttributes.mValues[aChannel][kComponentTransferInterceptIndex] = intercept;
+      break;
+    }
+    case SVG_FECOMPONENTTRANSFER_TYPE_GAMMA: {
+      aAttributes.mValues[aChannel].SetLength(3);
+      aAttributes.mValues[aChannel][kComponentTransferAmplitudeIndex] = amplitude;
+      aAttributes.mValues[aChannel][kComponentTransferExponentIndex] = exponent;
+      aAttributes.mValues[aChannel][kComponentTransferOffsetIndex] = offset;
+      break;
+    }
+    case SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE:
+    case SVG_FECOMPONENTTRANSFER_TYPE_TABLE: {
+      aAttributes.mValues[aChannel].AppendElements(&tableValues[0],
+                                                                      tableValues.Length());
+      break;
+    }
   }
-  return map;
 }
 
 //----------------------------------------------------------------------
 // nsSVGElement methods
 
 nsSVGElement::NumberListAttributesInfo
 SVGComponentTransferFunctionElement::GetNumberListInfo()
 {
@@ -462,63 +472,64 @@ nsSVGFELightingElement::IsAttributeMappe
 }
 
 void
 nsSVGFELightingElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources)
 {
   aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
 }
 
-AttributeMap
-nsSVGFELightingElement::ComputeLightAttributes(nsSVGFilterInstance* aInstance)
+LightType
+nsSVGFELightingElement::ComputeLightAttributes(nsSVGFilterInstance* aInstance,
+                                               nsTArray<float>& aFloatAttributes)
 {
   // find specified light
   for (nsCOMPtr<nsIContent> child = nsINode::GetFirstChild();
        child;
        child = child->GetNextSibling()) {
     if (child->IsAnyOfSVGElements(nsGkAtoms::feDistantLight,
                                   nsGkAtoms::fePointLight,
                                   nsGkAtoms::feSpotLight)) {
-      return static_cast<SVGFELightElement*>(child.get())->ComputeLightAttributes(aInstance);
+      return static_cast<SVGFELightElement*>(child.get())->ComputeLightAttributes(aInstance,
+                                                                                  aFloatAttributes);
     }
   }
 
-  AttributeMap map;
-  map.Set(eLightType, (uint32_t)eLightTypeNone);
-  return map;
+  return LightType::None;
 }
 
-FilterPrimitiveDescription
-nsSVGFELightingElement::AddLightingAttributes(const FilterPrimitiveDescription& aDescription,
+bool
+nsSVGFELightingElement::AddLightingAttributes(mozilla::gfx::DiffuseLightingAttributes* aAttributes,
                                               nsSVGFilterInstance* aInstance)
 {
   nsIFrame* frame = GetPrimaryFrame();
   if (!frame) {
-    return FilterPrimitiveDescription(PrimitiveType::Empty);
+    return false;
   }
 
   const nsStyleSVGReset* styleSVGReset = frame->Style()->StyleSVGReset();
   Color color(Color::FromABGR(styleSVGReset->mLightingColor.CalcColor(frame)));
   color.a = 1.f;
   float surfaceScale = mNumberAttributes[SURFACE_SCALE].GetAnimValue();
   Size kernelUnitLength =
     GetKernelUnitLength(aInstance, &mNumberPairAttributes[KERNEL_UNIT_LENGTH]);
 
   if (kernelUnitLength.width <= 0 || kernelUnitLength.height <= 0) {
     // According to spec, A negative or zero value is an error. See link below for details.
     // https://www.w3.org/TR/SVG/filters.html#feSpecularLightingKernelUnitLengthAttribute
-    return FilterPrimitiveDescription(PrimitiveType::Empty);
+    return false;
   }
 
-  FilterPrimitiveDescription descr = aDescription;
-  descr.Attributes().Set(eLightingLight, ComputeLightAttributes(aInstance));
-  descr.Attributes().Set(eLightingSurfaceScale, surfaceScale);
-  descr.Attributes().Set(eLightingKernelUnitLength, kernelUnitLength);
-  descr.Attributes().Set(eLightingColor, color);
-  return descr;
+  aAttributes->mLightType =
+    ComputeLightAttributes(aInstance, aAttributes->mLightValues);
+  aAttributes->mSurfaceScale = surfaceScale;
+  aAttributes->mKernelUnitLength = kernelUnitLength;
+  aAttributes->mColor = color;
+
+  return true;
 }
 
 bool
 nsSVGFELightingElement::AttributeAffectsRendering(int32_t aNameSpaceID,
                                                   nsAtom* aAttribute) const
 {
   return nsSVGFELightingElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
          (aNameSpaceID == kNameSpaceID_None &&
--- a/dom/svg/nsSVGFilters.h
+++ b/dom/svg/nsSVGFilters.h
@@ -50,17 +50,17 @@ protected:
   typedef mozilla::gfx::ColorSpace ColorSpace;
   typedef mozilla::gfx::FilterPrimitiveDescription FilterPrimitiveDescription;
 
   explicit nsSVGFE(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
     : nsSVGFEBase(aNodeInfo) {}
   virtual ~nsSVGFE() {}
 
 public:
-  typedef mozilla::gfx::AttributeMap AttributeMap;
+  typedef mozilla::gfx::PrimitiveAttributes PrimitiveAttributes;
 
   ColorSpace
   GetInputColorSpace(int32_t aInputIndex, ColorSpace aUnchangedInputColorSpace) {
     return OperatesOnSRGB(aInputIndex, aUnchangedInputColorSpace == ColorSpace::SRGB) ?
              ColorSpace::SRGB : ColorSpace::LinearRGB;
   }
 
   // This is only called for filter primitives without inputs. For primitives
@@ -193,21 +193,21 @@ public:
 protected:
   virtual bool OperatesOnSRGB(int32_t aInputIndex,
                               bool aInputIsAlreadySRGB) override { return true; }
 
   virtual NumberAttributesInfo GetNumberInfo() override;
   virtual NumberPairAttributesInfo GetNumberPairInfo() override;
   virtual StringAttributesInfo GetStringInfo() override;
 
-  AttributeMap ComputeLightAttributes(nsSVGFilterInstance* aInstance);
+  mozilla::gfx::LightType ComputeLightAttributes(nsSVGFilterInstance* aInstance,
+                                                 nsTArray<float>& aFloatAttributes);
 
-  FilterPrimitiveDescription
-    AddLightingAttributes(const FilterPrimitiveDescription& aDescription,
-                          nsSVGFilterInstance* aInstance);
+  bool AddLightingAttributes(mozilla::gfx::DiffuseLightingAttributes* aAttributes,
+                             nsSVGFilterInstance* aInstance);
 
   enum { SURFACE_SCALE, DIFFUSE_CONSTANT, SPECULAR_CONSTANT, SPECULAR_EXPONENT };
   nsSVGNumber2 mNumberAttributes[4];
   static NumberInfo sNumberInfo[4];
 
   enum { KERNEL_UNIT_LENGTH };
   nsSVGNumberPair mNumberPairAttributes[1];
   static NumberPairInfo sNumberPairInfo[1];
@@ -224,18 +224,18 @@ typedef SVGFEUnstyledElement SVGFELightE
 
 class SVGFELightElement : public SVGFELightElementBase
 {
 protected:
   explicit SVGFELightElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
     : SVGFELightElementBase(aNodeInfo) {}
 
 public:
-  typedef gfx::AttributeMap AttributeMap;
+  typedef gfx::PrimitiveAttributes PrimitiveAttributes;
 
-  virtual AttributeMap
-    ComputeLightAttributes(nsSVGFilterInstance* aInstance) = 0;
+  virtual mozilla::gfx::LightType ComputeLightAttributes(nsSVGFilterInstance* aInstance,
+                                                         nsTArray<float>& aFloatAttributes) = 0;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -31,17 +31,17 @@
 #ifdef _MSC_VER
 #pragma warning( disable : 4800 )
 #endif
 
 namespace mozilla {
 
 typedef gfxImageFormat PixelFormat;
 
-} // namespace mozilla
+} // namespace
 
 namespace IPC {
 
 template<>
 struct ParamTraits<mozilla::gfx::Matrix>
 {
   typedef mozilla::gfx::Matrix paramType;
 
@@ -249,40 +249,32 @@ template <>
 struct ParamTraits<mozilla::gfx::FeatureStatus>
   : public ContiguousEnumSerializer<
              mozilla::gfx::FeatureStatus,
              mozilla::gfx::FeatureStatus::Unused,
              mozilla::gfx::FeatureStatus::LAST>
 {};
 
 template <>
-struct ParamTraits<mozilla::gfx::AttributeName>
-  : public ContiguousEnumSerializer<
-             mozilla::gfx::AttributeName,
-             mozilla::gfx::eBlendBlendmode,
-             mozilla::gfx::eLastAttributeName>
-{};
-
-template <>
-struct ParamTraits<mozilla::gfx::AttributeType>
-  : public ContiguousEnumSerializer<
-             mozilla::gfx::AttributeType,
-             mozilla::gfx::AttributeType::eBool,
-             mozilla::gfx::AttributeType::Max>
-{};
-
-template <>
 struct ParamTraits<mozilla::gfx::PrimitiveType>
   : public ContiguousEnumSerializer<
              mozilla::gfx::PrimitiveType,
              mozilla::gfx::PrimitiveType::Empty,
              mozilla::gfx::PrimitiveType::Max>
 {};
 
 template <>
+struct ParamTraits<mozilla::gfx::LightType>
+  : public ContiguousEnumSerializer<
+             mozilla::gfx::LightType,
+             mozilla::gfx::LightType::None,
+             mozilla::gfx::LightType::Max>
+{};
+
+template <>
 struct ParamTraits<mozilla::gfx::ColorSpace>
   : public ContiguousEnumSerializer<
              mozilla::gfx::ColorSpace,
              mozilla::gfx::ColorSpace::SRGB,
              mozilla::gfx::ColorSpace::Max>
 {};
 
 /*
@@ -732,123 +724,456 @@ template <>
 struct ParamTraits<mozilla::YUVColorSpace>
   : public ContiguousEnumSerializer<
              mozilla::YUVColorSpace,
              mozilla::YUVColorSpace::BT601,
              mozilla::YUVColorSpace::UNKNOWN>
 {};
 
 template <>
-struct ParamTraits<mozilla::gfx::AttributeMap>
+struct ParamTraits<mozilla::gfx::ImplicitlyCopyableFloatArray>
+  : public ParamTraits<nsTArray<float>>
+{
+  typedef mozilla::gfx::ImplicitlyCopyableFloatArray paramType;
+};
+
+template <>
+struct ParamTraits<mozilla::gfx::EmptyAttributes>
+{
+  typedef mozilla::gfx::EmptyAttributes paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    return true;
+  }
+};
+
+template <>
+struct ParamTraits<mozilla::gfx::MergeAttributes>
+{
+  typedef mozilla::gfx::MergeAttributes paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    return true;
+  }
+};
+
+template <>
+struct ParamTraits<mozilla::gfx::ToAlphaAttributes>
+{
+  typedef mozilla::gfx::ToAlphaAttributes paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    return true;
+  }
+};
+
+template <>
+struct ParamTraits<mozilla::gfx::TileAttributes>
+{
+  typedef mozilla::gfx::TileAttributes paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    return true;
+  }
+};
+
+template <>
+struct ParamTraits<mozilla::gfx::BlendAttributes>
 {
-  typedef mozilla::gfx::AttributeMap paramType;
+  typedef mozilla::gfx::BlendAttributes paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mBlendMode);
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    return ReadParam(aMsg, aIter, &aResult->mBlendMode);
+  }
+};
+
+template<>
+struct ParamTraits<mozilla::gfx::MorphologyAttributes>
+{
+  typedef mozilla::gfx::MorphologyAttributes paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mOperator);
+    WriteParam(aMsg, aParam.mRadii);
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &aResult->mOperator) ||
+        !ReadParam(aMsg, aIter, &aResult->mRadii)) {
+      return false;
+    }
+    return true;
+  }
+};
+
+template<>
+struct ParamTraits<mozilla::gfx::FloodAttributes>
+{
+  typedef mozilla::gfx::FloodAttributes paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mColor);
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &aResult->mColor)) {
+      return false;
+    }
+    return true;
+  }
+};
+
+template<>
+struct ParamTraits<mozilla::gfx::OpacityAttributes>
+{
+  typedef mozilla::gfx::OpacityAttributes paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mOpacity);
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &aResult->mOpacity)) {
+      return false;
+    }
+    return true;
+  }
+};
+
+template<>
+struct ParamTraits<mozilla::gfx::OffsetAttributes>
+{
+  typedef mozilla::gfx::OffsetAttributes paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
-    WriteParam(aMsg, aParam.Count());
-    for (auto iter = aParam.ConstIter(); !iter.Done(); iter.Next()) {
-      mozilla::gfx::AttributeName name =
-        mozilla::gfx::AttributeName(iter.Key());
-      mozilla::gfx::AttributeType type =
-        mozilla::gfx::AttributeMap::GetType(iter.UserData());
+    WriteParam(aMsg, aParam.mValue);
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &aResult->mValue)) {
+      return false;
+    }
+    return true;
+  }
+};
+
+template<>
+struct ParamTraits<mozilla::gfx::DisplacementMapAttributes>
+{
+  typedef mozilla::gfx::DisplacementMapAttributes paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mScale);
+    WriteParam(aMsg, aParam.mXChannel);
+    WriteParam(aMsg, aParam.mYChannel);
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &aResult->mScale) ||
+        !ReadParam(aMsg, aIter, &aResult->mXChannel) ||
+        !ReadParam(aMsg, aIter, &aResult->mYChannel)) {
+      return false;
+  }
+  return true;
+  }
+};
 
-      WriteParam(aMsg, type);
-      WriteParam(aMsg, name);
+template<>
+struct ParamTraits<mozilla::gfx::TurbulenceAttributes>
+{
+  typedef mozilla::gfx::TurbulenceAttributes paramType;
 
-      switch (type) {
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mOffset);
+    WriteParam(aMsg, aParam.mBaseFrequency);
+    WriteParam(aMsg, aParam.mSeed);
+    WriteParam(aMsg, aParam.mOctaves);
+    WriteParam(aMsg, aParam.mStitchable);
+    WriteParam(aMsg, aParam.mType);
+  }
 
-#define CASE_TYPE(typeName)                                          \
-    case mozilla::gfx::AttributeType::e##typeName:                     \
-      WriteParam(aMsg, aParam.Get##typeName(name)); \
-      break;
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &aResult->mOffset) ||
+        !ReadParam(aMsg, aIter, &aResult->mBaseFrequency) ||
+        !ReadParam(aMsg, aIter, &aResult->mSeed) ||
+        !ReadParam(aMsg, aIter, &aResult->mOctaves) ||
+        !ReadParam(aMsg, aIter, &aResult->mStitchable) ||
+        !ReadParam(aMsg, aIter, &aResult->mType)) {
+      return false;
+    }
+    return true;
+  }
+};
+
+template<>
+struct ParamTraits<mozilla::gfx::ImageAttributes>
+{
+  typedef mozilla::gfx::ImageAttributes paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mFilter);
+    WriteParam(aMsg, aParam.mInputIndex);
+    WriteParam(aMsg, aParam.mTransform);
+  }
 
-    CASE_TYPE(Bool)
-    CASE_TYPE(Uint)
-    CASE_TYPE(Float)
-    CASE_TYPE(Size)
-    CASE_TYPE(IntSize)
-    CASE_TYPE(IntPoint)
-    CASE_TYPE(Matrix)
-    CASE_TYPE(Matrix5x4)
-    CASE_TYPE(Point3D)
-    CASE_TYPE(Color)
-    CASE_TYPE(AttributeMap)
-    CASE_TYPE(Floats)
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &aResult->mFilter) ||
+        !ReadParam(aMsg, aIter, &aResult->mInputIndex) ||
+        !ReadParam(aMsg, aIter, &aResult->mTransform)) {
+      return false;
+    }
+    return true;
+  }
+};
+
+template<>
+struct ParamTraits<mozilla::gfx::GaussianBlurAttributes>
+{
+  typedef mozilla::gfx::GaussianBlurAttributes paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mStdDeviation);
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &aResult->mStdDeviation)) {
+      return false;
+    }
+    return true;
+  }
+};
+
+template<>
+struct ParamTraits<mozilla::gfx::DropShadowAttributes>
+{
+  typedef mozilla::gfx::DropShadowAttributes paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mStdDeviation);
+    WriteParam(aMsg, aParam.mOffset);
+    WriteParam(aMsg, aParam.mColor);
+  }
 
-#undef CASE_TYPE
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &aResult->mStdDeviation) ||
+        !ReadParam(aMsg, aIter, &aResult->mOffset) ||
+        !ReadParam(aMsg, aIter, &aResult->mColor)) {
+      return false;
+    }
+    return true;
+  }
+};
+
+template<>
+struct ParamTraits<mozilla::gfx::ColorMatrixAttributes>
+{
+  typedef mozilla::gfx::ColorMatrixAttributes paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mType);
+    WriteParam(aMsg, aParam.mValues);
+  }
 
-        default:
-          MOZ_CRASH("GFX: unhandled attribute type");
-      }
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &aResult->mType) ||
+        !ReadParam(aMsg, aIter, &aResult->mValues)) {
+      return false;
+    }
+    return true;
+  }
+};
+
+template<>
+struct ParamTraits<mozilla::gfx::ComponentTransferAttributes>
+{
+  typedef mozilla::gfx::ComponentTransferAttributes paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    for (int i = 0; i < 4; ++i) {
+      WriteParam(aMsg, aParam.mTypes[i]);
+    }
+    for (int i = 0; i < 4; ++i) {
+      WriteParam(aMsg, aParam.mValues[i]);
     }
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
-    uint32_t count;
-    if (!ReadParam(aMsg, aIter, &count)) {
-      return false;
-    }
-    for (uint32_t i = 0; i < count; i++) {
-      mozilla::gfx::AttributeType type;
-      if (!ReadParam(aMsg, aIter, &type)) {
+    for (int i = 0; i < 4; ++i) {
+      if (!ReadParam(aMsg, aIter, &aResult->mTypes[i])) {
         return false;
       }
-      mozilla::gfx::AttributeName name;
-      if (!ReadParam(aMsg, aIter, &name)) {
+    }
+    for (int i = 0; i < 4; ++i) {
+      if (!ReadParam(aMsg, aIter, &aResult->mValues[i])){
         return false;
       }
-      switch (type) {
+    }
+    return true;
+  }
+};
+
+template<>
+struct ParamTraits<mozilla::gfx::ConvolveMatrixAttributes>
+{
+  typedef mozilla::gfx::ConvolveMatrixAttributes paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mKernelSize);
+    WriteParam(aMsg, aParam.mKernelMatrix);
+    WriteParam(aMsg, aParam.mDivisor);
+    WriteParam(aMsg, aParam.mBias);
+    WriteParam(aMsg, aParam.mTarget);
+    WriteParam(aMsg, aParam.mEdgeMode);
+    WriteParam(aMsg, aParam.mKernelUnitLength);
+    WriteParam(aMsg, aParam.mPreserveAlpha);
+  }
 
-#define HANDLE_TYPE(type, typeName)                                    \
-        case mozilla::gfx::AttributeType::e##typeName:                 \
-        {                                                              \
-          type value;                                                  \
-          if (!ReadParam(aMsg, aIter, &value)) {                       \
-            return false;                                              \
-          }                                                            \
-          aResult->Set(name, value);                                   \
-          break;                                                       \
-        }
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &aResult->mKernelSize) ||
+        !ReadParam(aMsg, aIter, &aResult->mKernelMatrix) ||
+        !ReadParam(aMsg, aIter, &aResult->mDivisor) ||
+        !ReadParam(aMsg, aIter, &aResult->mBias) ||
+        !ReadParam(aMsg, aIter, &aResult->mTarget) ||
+        !ReadParam(aMsg, aIter, &aResult->mEdgeMode) ||
+        !ReadParam(aMsg, aIter, &aResult->mKernelUnitLength) ||
+        !ReadParam(aMsg, aIter, &aResult->mPreserveAlpha)) {
+      return false;
+    }
+    return true;
+  }
+};
 
-        HANDLE_TYPE(bool, Bool)
-        HANDLE_TYPE(uint32_t, Uint)
-        HANDLE_TYPE(float, Float)
-        HANDLE_TYPE(mozilla::gfx::Size, Size)
-        HANDLE_TYPE(mozilla::gfx::IntSize, IntSize)
-        HANDLE_TYPE(mozilla::gfx::IntPoint, IntPoint)
-        HANDLE_TYPE(mozilla::gfx::Matrix, Matrix)
-        HANDLE_TYPE(mozilla::gfx::Matrix5x4, Matrix5x4)
-        HANDLE_TYPE(mozilla::gfx::Point3D, Point3D)
-        HANDLE_TYPE(mozilla::gfx::Color, Color)
+template<>
+struct ParamTraits<mozilla::gfx::DiffuseLightingAttributes>
+{
+  typedef mozilla::gfx::DiffuseLightingAttributes paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mLightType);
+    WriteParam(aMsg, aParam.mLightValues);
+    WriteParam(aMsg, aParam.mSurfaceScale);
+    WriteParam(aMsg, aParam.mKernelUnitLength);
+    WriteParam(aMsg, aParam.mColor);
+    WriteParam(aMsg, aParam.mLightingConstant);
+    WriteParam(aMsg, aParam.mSpecularExponent);
+  }
 
-#undef HANDLE_TYPE
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &aResult->mLightType) ||
+        !ReadParam(aMsg, aIter, &aResult->mLightValues) ||
+        !ReadParam(aMsg, aIter, &aResult->mSurfaceScale) ||
+        !ReadParam(aMsg, aIter, &aResult->mKernelUnitLength) ||
+        !ReadParam(aMsg, aIter, &aResult->mColor) ||
+        !ReadParam(aMsg, aIter, &aResult->mLightingConstant) ||
+        !ReadParam(aMsg, aIter, &aResult->mSpecularExponent)) {
+      return false;
+    }
+    return true;
+  }
+};
 
-        case mozilla::gfx::AttributeType::eAttributeMap:
-        {
-          mozilla::gfx::AttributeMap value;
-          if (!ReadParam(aMsg, aIter, &value)) {
-            return false;
-          }
-          aResult->Set(name, std::move(value));
-          break;
-        }
+template<>
+struct ParamTraits<mozilla::gfx::SpecularLightingAttributes>
+{
+  typedef mozilla::gfx::SpecularLightingAttributes paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mLightType);
+    WriteParam(aMsg, aParam.mLightValues);
+    WriteParam(aMsg, aParam.mSurfaceScale);
+    WriteParam(aMsg, aParam.mKernelUnitLength);
+    WriteParam(aMsg, aParam.mColor);
+    WriteParam(aMsg, aParam.mLightingConstant);
+    WriteParam(aMsg, aParam.mSpecularExponent);
+  }
 
-        case mozilla::gfx::AttributeType::eFloats:
-        {
-          nsTArray<float> value;
-          if (!ReadParam(aMsg, aIter, &value)) {
-            return false;
-          }
-          aResult->Set(name, &value[0], value.Length());
-          break;
-        }
-        default:
-          MOZ_CRASH("GFX: unhandled attribute type");
-      }
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &aResult->mLightType) ||
+        !ReadParam(aMsg, aIter, &aResult->mLightValues) ||
+        !ReadParam(aMsg, aIter, &aResult->mSurfaceScale) ||
+        !ReadParam(aMsg, aIter, &aResult->mKernelUnitLength) ||
+        !ReadParam(aMsg, aIter, &aResult->mColor) ||
+        !ReadParam(aMsg, aIter, &aResult->mLightingConstant) ||
+        !ReadParam(aMsg, aIter, &aResult->mSpecularExponent)) {
+      return false;
+    }
+    return true;
+  }
+};
+
+template<>
+struct ParamTraits<mozilla::gfx::CompositeAttributes>
+{
+  typedef mozilla::gfx::CompositeAttributes paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mOperator);
+    WriteParam(aMsg, aParam.mCoefficients);
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &aResult->mOperator) ||
+        !ReadParam(aMsg, aIter, &aResult->mCoefficients)) {
+      return false;
     }
     return true;
   }
 };
 
 template <>
 struct ParamTraits<mozilla::gfx::FilterPrimitiveDescription>
 {
--- a/gfx/src/FilterSupport.cpp
+++ b/gfx/src/FilterSupport.cpp
@@ -535,42 +535,43 @@ DisableAllTransfers(FilterNode* aTransfe
 //  @param aFunctionAttributes The attributes of the transfer function for this
 //                             channel.
 //  @param aChannel The color channel that this function applies to, where
 //                  0 = red, 1 = green, 2 = blue, 3 = alpha
 //  @param aDT The DrawTarget that the FilterNodes should be created for.
 //  @param aTableTransfer Existing FilterNode holders (which may still be
 //                        null) that the resulting FilterNodes from this
 //                        function will be stored in.
-//           
+//
 static void
-ConvertComponentTransferFunctionToFilter(const AttributeMap& aFunctionAttributes,
-                                         int32_t aChannel,
+ConvertComponentTransferFunctionToFilter(const ComponentTransferAttributes& aFunctionAttributes,
+                                         int32_t aInChannel,
+                                         int32_t aOutChannel,
                                          DrawTarget* aDT,
                                          RefPtr<FilterNode>& aTableTransfer,
                                          RefPtr<FilterNode>& aDiscreteTransfer,
                                          RefPtr<FilterNode>& aLinearTransfer,
                                          RefPtr<FilterNode>& aGammaTransfer)
 {
   static const TransferAtts disableAtt[4] = {
     ATT_TRANSFER_DISABLE_R,
     ATT_TRANSFER_DISABLE_G,
     ATT_TRANSFER_DISABLE_B,
     ATT_TRANSFER_DISABLE_A
   };
 
   RefPtr<FilterNode> filter;
 
-  uint32_t type = aFunctionAttributes.GetUint(eComponentTransferFunctionType);
+  uint32_t type = aFunctionAttributes.mTypes[aInChannel];
 
   switch (type) {
   case SVG_FECOMPONENTTRANSFER_TYPE_TABLE:
   {
     const nsTArray<float>& tableValues =
-      aFunctionAttributes.GetFloats(eComponentTransferFunctionTableValues);
+      aFunctionAttributes.mValues[aInChannel];
     if (tableValues.Length() < 2)
       return;
 
     if (!aTableTransfer) {
       aTableTransfer = aDT->CreateFilter(FilterType::TABLE_TRANSFER);
       if (!aTableTransfer) {
         return;
       }
@@ -578,25 +579,25 @@ ConvertComponentTransferFunctionToFilter
     }
     filter = aTableTransfer;
     static const TableTransferAtts tableAtt[4] = {
       ATT_TABLE_TRANSFER_TABLE_R,
       ATT_TABLE_TRANSFER_TABLE_G,
       ATT_TABLE_TRANSFER_TABLE_B,
       ATT_TABLE_TRANSFER_TABLE_A
     };
-    filter->SetAttribute(disableAtt[aChannel], false);
-    filter->SetAttribute(tableAtt[aChannel], &tableValues[0], tableValues.Length());
+    filter->SetAttribute(disableAtt[aOutChannel], false);
+    filter->SetAttribute(tableAtt[aOutChannel], &tableValues[0], tableValues.Length());
     break;
   }
 
   case SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE:
   {
     const nsTArray<float>& tableValues =
-      aFunctionAttributes.GetFloats(eComponentTransferFunctionTableValues);
+      aFunctionAttributes.mValues[aInChannel];
     if (tableValues.Length() < 1)
       return;
 
     if (!aDiscreteTransfer) {
       aDiscreteTransfer = aDT->CreateFilter(FilterType::DISCRETE_TRANSFER);
       if (!aDiscreteTransfer) {
         return;
       }
@@ -604,18 +605,18 @@ ConvertComponentTransferFunctionToFilter
     }
     filter = aDiscreteTransfer;
     static const DiscreteTransferAtts tableAtt[4] = {
       ATT_DISCRETE_TRANSFER_TABLE_R,
       ATT_DISCRETE_TRANSFER_TABLE_G,
       ATT_DISCRETE_TRANSFER_TABLE_B,
       ATT_DISCRETE_TRANSFER_TABLE_A
     };
-    filter->SetAttribute(disableAtt[aChannel], false);
-    filter->SetAttribute(tableAtt[aChannel], &tableValues[0], tableValues.Length());
+    filter->SetAttribute(disableAtt[aOutChannel], false);
+    filter->SetAttribute(tableAtt[aOutChannel], &tableValues[0], tableValues.Length());
 
     break;
   }
 
   case SVG_FECOMPONENTTRANSFER_TYPE_LINEAR:
   {
     static const LinearTransferAtts slopeAtt[4] = {
       ATT_LINEAR_TRANSFER_SLOPE_R,
@@ -632,21 +633,22 @@ ConvertComponentTransferFunctionToFilter
     if (!aLinearTransfer) {
       aLinearTransfer = aDT->CreateFilter(FilterType::LINEAR_TRANSFER);
       if (!aLinearTransfer) {
         return;
       }
       DisableAllTransfers(aLinearTransfer);
     }
     filter = aLinearTransfer;
-    filter->SetAttribute(disableAtt[aChannel], false);
-    float slope = aFunctionAttributes.GetFloat(eComponentTransferFunctionSlope);
-    float intercept = aFunctionAttributes.GetFloat(eComponentTransferFunctionIntercept);
-    filter->SetAttribute(slopeAtt[aChannel], slope);
-    filter->SetAttribute(interceptAtt[aChannel], intercept);
+    filter->SetAttribute(disableAtt[aOutChannel], false);
+    const nsTArray<float>& slopeIntercept = aFunctionAttributes.mValues[aInChannel];
+    float slope = slopeIntercept[kComponentTransferSlopeIndex];
+    float intercept = slopeIntercept[kComponentTransferInterceptIndex];
+    filter->SetAttribute(slopeAtt[aOutChannel], slope);
+    filter->SetAttribute(interceptAtt[aOutChannel], intercept);
     break;
   }
 
   case SVG_FECOMPONENTTRANSFER_TYPE_GAMMA:
   {
     static const GammaTransferAtts amplitudeAtt[4] = {
       ATT_GAMMA_TRANSFER_AMPLITUDE_R,
       ATT_GAMMA_TRANSFER_AMPLITUDE_G,
@@ -668,23 +670,24 @@ ConvertComponentTransferFunctionToFilter
     if (!aGammaTransfer) {
       aGammaTransfer = aDT->CreateFilter(FilterType::GAMMA_TRANSFER);
       if (!aGammaTransfer) {
         return;
       }
       DisableAllTransfers(aGammaTransfer);
     }
     filter = aGammaTransfer;
-    filter->SetAttribute(disableAtt[aChannel], false);
-    float amplitude = aFunctionAttributes.GetFloat(eComponentTransferFunctionAmplitude);
-    float exponent = aFunctionAttributes.GetFloat(eComponentTransferFunctionExponent);
-    float offset = aFunctionAttributes.GetFloat(eComponentTransferFunctionOffset);
-    filter->SetAttribute(amplitudeAtt[aChannel], amplitude);
-    filter->SetAttribute(exponentAtt[aChannel], exponent);
-    filter->SetAttribute(offsetAtt[aChannel], offset);
+    filter->SetAttribute(disableAtt[aOutChannel], false);
+    const nsTArray<float>& gammaValues = aFunctionAttributes.mValues[aInChannel];
+    float amplitude = gammaValues[kComponentTransferAmplitudeIndex];
+    float exponent = gammaValues[kComponentTransferExponentIndex];
+    float offset = gammaValues[kComponentTransferOffsetIndex];
+    filter->SetAttribute(amplitudeAtt[aOutChannel], amplitude);
+    filter->SetAttribute(exponentAtt[aOutChannel], exponent);
+    filter->SetAttribute(offsetAtt[aOutChannel], offset);
     break;
   }
 
   case SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY:
   default:
     break;
   }
 }
@@ -700,38 +703,56 @@ const int32_t kMorphologyMaxRadius = 100
 // aInputImages carries additional surfaces that are used by eImage primitives.
 static already_AddRefed<FilterNode>
 FilterNodeFromPrimitiveDescription(const FilterPrimitiveDescription& aDescription,
                                    DrawTarget* aDT,
                                    nsTArray<RefPtr<FilterNode> >& aSources,
                                    nsTArray<IntRect>& aSourceRegions,
                                    nsTArray<RefPtr<SourceSurface>>& aInputImages)
 {
-  const AttributeMap& atts = aDescription.Attributes();
-  switch (aDescription.Type()) {
+  struct PrimitiveAttributesMatcher {
+    PrimitiveAttributesMatcher(const FilterPrimitiveDescription& aDescription,
+                               DrawTarget* aDT,
+                               nsTArray<RefPtr<FilterNode> >& aSources,
+                               nsTArray<IntRect>& aSourceRegions,
+                               nsTArray<RefPtr<SourceSurface>>& aInputImages)
+    : mDescription(aDescription)
+    , mDT(aDT)
+    , mSources(aSources)
+    , mSourceRegions(aSourceRegions)
+    , mInputImages(aInputImages)
+    {}
 
-    case PrimitiveType::Empty:
+    const FilterPrimitiveDescription& mDescription;
+    DrawTarget* mDT;
+    nsTArray<RefPtr<FilterNode> >& mSources;
+    nsTArray<IntRect>& mSourceRegions;
+    nsTArray<RefPtr<SourceSurface>>& mInputImages;
+
+    already_AddRefed<FilterNode> match(const EmptyAttributes& aEmptyAttributes)
+    {
       return nullptr;
+    }
 
-    case PrimitiveType::Blend:
+    already_AddRefed<FilterNode> match(const BlendAttributes& aBlend)
     {
-      uint32_t mode = atts.GetUint(eBlendBlendmode);
+      uint32_t mode = aBlend.mBlendMode;
       RefPtr<FilterNode> filter;
       if (mode == SVG_FEBLEND_MODE_UNKNOWN) {
         return nullptr;
       }
       if (mode == SVG_FEBLEND_MODE_NORMAL) {
-        filter = aDT->CreateFilter(FilterType::COMPOSITE);
+        filter = mDT->CreateFilter(FilterType::COMPOSITE);
         if (!filter) {
           return nullptr;
         }
-        filter->SetInput(IN_COMPOSITE_IN_START, aSources[1]);
-        filter->SetInput(IN_COMPOSITE_IN_START + 1, aSources[0]);
+        filter->SetInput(IN_COMPOSITE_IN_START, mSources[1]);
+        filter->SetInput(IN_COMPOSITE_IN_START + 1, mSources[0]);
       } else {
-        filter = aDT->CreateFilter(FilterType::BLEND);
+        filter = mDT->CreateFilter(FilterType::BLEND);
         if (!filter) {
           return nullptr;
         }
         static const uint8_t blendModes[SVG_FEBLEND_MODE_LUMINOSITY + 1] = {
           0,
           0,
           BLEND_MODE_MULTIPLY,
           BLEND_MODE_SCREEN,
@@ -747,437 +768,444 @@ FilterNodeFromPrimitiveDescription(const
           BLEND_MODE_HUE,
           BLEND_MODE_SATURATION,
           BLEND_MODE_COLOR,
           BLEND_MODE_LUMINOSITY
         };
         filter->SetAttribute(ATT_BLEND_BLENDMODE, (uint32_t)blendModes[mode]);
         // The correct input order for both software and D2D filters is flipped
         // from our source order, so flip here.
-        filter->SetInput(IN_BLEND_IN, aSources[1]);
-        filter->SetInput(IN_BLEND_IN2, aSources[0]);
+        filter->SetInput(IN_BLEND_IN, mSources[1]);
+        filter->SetInput(IN_BLEND_IN2, mSources[0]);
       }
       return filter.forget();
     }
 
-    case PrimitiveType::ColorMatrix:
+    already_AddRefed<FilterNode> match(const ColorMatrixAttributes& aColorMatrix)
     {
+      uint32_t type = aColorMatrix.mType;
       float colorMatrix[20];
-      uint32_t type = atts.GetUint(eColorMatrixType);
-      const nsTArray<float>& values = atts.GetFloats(eColorMatrixValues);
-      if (NS_FAILED(ComputeColorMatrix(type, values, colorMatrix)) ||
+      if (NS_FAILED(ComputeColorMatrix(type, aColorMatrix.mValues, colorMatrix)) ||
           ArrayEqual(colorMatrix, identityMatrix)) {
-        RefPtr<FilterNode> filter(aSources[0]);
+        RefPtr<FilterNode> filter(mSources[0]);
         return filter.forget();
       }
       Matrix5x4 matrix(colorMatrix[0], colorMatrix[5], colorMatrix[10],  colorMatrix[15],
                        colorMatrix[1], colorMatrix[6], colorMatrix[11],  colorMatrix[16],
                        colorMatrix[2], colorMatrix[7], colorMatrix[12],  colorMatrix[17],
                        colorMatrix[3], colorMatrix[8], colorMatrix[13],  colorMatrix[18],
                        colorMatrix[4], colorMatrix[9], colorMatrix[14],  colorMatrix[19]);
-      RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::COLOR_MATRIX);
+      RefPtr<FilterNode> filter = mDT->CreateFilter(FilterType::COLOR_MATRIX);
       if (!filter) {
         return nullptr;
       }
       filter->SetAttribute(ATT_COLOR_MATRIX_MATRIX, matrix);
       filter->SetAttribute(ATT_COLOR_MATRIX_ALPHA_MODE, (uint32_t)ALPHA_MODE_STRAIGHT);
-      filter->SetInput(IN_COLOR_MATRIX_IN, aSources[0]);
+      filter->SetInput(IN_COLOR_MATRIX_IN, mSources[0]);
       return filter.forget();
     }
 
-    case PrimitiveType::Morphology:
+    already_AddRefed<FilterNode> match(const MorphologyAttributes& aMorphology)
     {
-      Size radii = atts.GetSize(eMorphologyRadii);
+      Size radii = aMorphology.mRadii;
       int32_t rx = radii.width;
       int32_t ry = radii.height;
       if (rx < 0 || ry < 0) {
         // XXX SVGContentUtils::ReportToConsole()
         return nullptr;
       }
       if (rx == 0 && ry == 0) {
         return nullptr;
       }
 
       // Clamp radii to prevent completely insane values:
       rx = std::min(rx, kMorphologyMaxRadius);
       ry = std::min(ry, kMorphologyMaxRadius);
 
-      MorphologyOperator op = atts.GetUint(eMorphologyOperator) == SVG_OPERATOR_ERODE ?
+      MorphologyOperator op = aMorphology.mOperator == SVG_OPERATOR_ERODE ?
         MORPHOLOGY_OPERATOR_ERODE : MORPHOLOGY_OPERATOR_DILATE;
 
-      RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::MORPHOLOGY);
+      RefPtr<FilterNode> filter = mDT->CreateFilter(FilterType::MORPHOLOGY);
       if (!filter) {
         return nullptr;
       }
       filter->SetAttribute(ATT_MORPHOLOGY_RADII, IntSize(rx, ry));
       filter->SetAttribute(ATT_MORPHOLOGY_OPERATOR, (uint32_t)op);
-      filter->SetInput(IN_MORPHOLOGY_IN, aSources[0]);
+      filter->SetInput(IN_MORPHOLOGY_IN, mSources[0]);
       return filter.forget();
     }
 
-    case PrimitiveType::Flood:
+    already_AddRefed<FilterNode> match(const FloodAttributes& aFlood)
     {
-      Color color = atts.GetColor(eFloodColor);
-      RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::FLOOD);
+      Color color = aFlood.mColor;
+      RefPtr<FilterNode> filter = mDT->CreateFilter(FilterType::FLOOD);
       if (!filter) {
         return nullptr;
       }
       filter->SetAttribute(ATT_FLOOD_COLOR, color);
       return filter.forget();
     }
 
-    case PrimitiveType::Tile:
+    already_AddRefed<FilterNode> match(const TileAttributes& aTile)
     {
-      RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::TILE);
+      RefPtr<FilterNode> filter = mDT->CreateFilter(FilterType::TILE);
       if (!filter) {
         return nullptr;
       }
-      filter->SetAttribute(ATT_TILE_SOURCE_RECT, aSourceRegions[0]);
-      filter->SetInput(IN_TILE_IN, aSources[0]);
+      filter->SetAttribute(ATT_TILE_SOURCE_RECT, mSourceRegions[0]);
+      filter->SetInput(IN_TILE_IN, mSources[0]);
       return filter.forget();
     }
 
-    case PrimitiveType::ComponentTransfer:
+    already_AddRefed<FilterNode> match(const ComponentTransferAttributes& aComponentTransfer)
     {
       RefPtr<FilterNode> filters[4]; // one for each FILTER_*_TRANSFER type
-      static const AttributeName componentFunctionNames[4] = {
-        eComponentTransferFunctionR,
-        eComponentTransferFunctionG,
-        eComponentTransferFunctionB,
-        eComponentTransferFunctionA
-      };
-      const AttributeMap* rgbFunctionAttributes =
-        atts.MaybeGetAttributeMap(eComponentTransferFunctionRGB);
+      bool useRgb =
+        aComponentTransfer.mTypes[kChannelG] == SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN &&
+        aComponentTransfer.mTypes[kChannelB] == SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN;
+
       for (int32_t i = 0; i < 4; i++) {
-        const AttributeMap& functionAttributes = i < 3 && rgbFunctionAttributes ?
-          *rgbFunctionAttributes : atts.GetAttributeMap(componentFunctionNames[i]);
-        ConvertComponentTransferFunctionToFilter(functionAttributes, i, aDT,
+        int32_t inputIndex = useRgb && i < 3 ? 0 : i;
+        ConvertComponentTransferFunctionToFilter(aComponentTransfer, inputIndex, i, mDT,
           filters[0], filters[1], filters[2], filters[3]);
       }
 
       // Connect all used filters nodes.
-      RefPtr<FilterNode> lastFilter = aSources[0];
+      RefPtr<FilterNode> lastFilter = mSources[0];
       for (int32_t i = 0; i < 4; i++) {
         if (filters[i]) {
           filters[i]->SetInput(0, lastFilter);
           lastFilter = filters[i];
         }
       }
 
       return lastFilter.forget();
     }
 
-    case PrimitiveType::Opacity:
+    already_AddRefed<FilterNode> match(const OpacityAttributes& aOpacity)
     {
-      RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::OPACITY);
+      RefPtr<FilterNode> filter = mDT->CreateFilter(FilterType::OPACITY);
       if (!filter) {
         return nullptr;
       }
-      filter->SetAttribute(ATT_OPACITY_VALUE, atts.GetFloat(eOpacityOpacity));
-      filter->SetInput(IN_OPACITY_IN, aSources[0]);
+      filter->SetAttribute(ATT_OPACITY_VALUE, aOpacity.mOpacity);
+      filter->SetInput(IN_OPACITY_IN, mSources[0]);
       return filter.forget();
     }
 
-    case PrimitiveType::ConvolveMatrix:
+    already_AddRefed<FilterNode> match(const ConvolveMatrixAttributes& aConvolveMatrix)
     {
-      RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::CONVOLVE_MATRIX);
+      RefPtr<FilterNode> filter = mDT->CreateFilter(FilterType::CONVOLVE_MATRIX);
       if (!filter) {
         return nullptr;
       }
-      filter->SetAttribute(ATT_CONVOLVE_MATRIX_KERNEL_SIZE, atts.GetIntSize(eConvolveMatrixKernelSize));
-      const nsTArray<float>& matrix = atts.GetFloats(eConvolveMatrixKernelMatrix);
+      filter->SetAttribute(ATT_CONVOLVE_MATRIX_KERNEL_SIZE, aConvolveMatrix.mKernelSize);
+      const nsTArray<float>& matrix = aConvolveMatrix.mKernelMatrix;
       filter->SetAttribute(ATT_CONVOLVE_MATRIX_KERNEL_MATRIX,
                            matrix.Elements(), matrix.Length());
       filter->SetAttribute(ATT_CONVOLVE_MATRIX_DIVISOR,
-                           atts.GetFloat(eConvolveMatrixDivisor));
+                           aConvolveMatrix.mDivisor);
       filter->SetAttribute(ATT_CONVOLVE_MATRIX_BIAS,
-                           atts.GetFloat(eConvolveMatrixBias));
+                           aConvolveMatrix.mBias);
       filter->SetAttribute(ATT_CONVOLVE_MATRIX_TARGET,
-                           atts.GetIntPoint(eConvolveMatrixTarget));
+                           aConvolveMatrix.mTarget);
       filter->SetAttribute(ATT_CONVOLVE_MATRIX_SOURCE_RECT,
-                           aSourceRegions[0]);
-      uint32_t edgeMode = atts.GetUint(eConvolveMatrixEdgeMode);
+                           mSourceRegions[0]);
+      uint32_t edgeMode = aConvolveMatrix.mEdgeMode;
       static const uint8_t edgeModes[SVG_EDGEMODE_NONE+1] = {
         EDGE_MODE_NONE,      // SVG_EDGEMODE_UNKNOWN
         EDGE_MODE_DUPLICATE, // SVG_EDGEMODE_DUPLICATE
         EDGE_MODE_WRAP,      // SVG_EDGEMODE_WRAP
         EDGE_MODE_NONE       // SVG_EDGEMODE_NONE
       };
       filter->SetAttribute(ATT_CONVOLVE_MATRIX_EDGE_MODE, (uint32_t)edgeModes[edgeMode]);
       filter->SetAttribute(ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH,
-                           atts.GetSize(eConvolveMatrixKernelUnitLength));
+                           aConvolveMatrix.mKernelUnitLength);
       filter->SetAttribute(ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA,
-                           atts.GetBool(eConvolveMatrixPreserveAlpha));
-      filter->SetInput(IN_CONVOLVE_MATRIX_IN, aSources[0]);
+                           aConvolveMatrix.mPreserveAlpha);
+      filter->SetInput(IN_CONVOLVE_MATRIX_IN, mSources[0]);
       return filter.forget();
     }
 
-    case PrimitiveType::Offset:
+    already_AddRefed<FilterNode> match(const OffsetAttributes& aOffset)
     {
-      return FilterWrappers::Offset(aDT, aSources[0],
-                                    atts.GetIntPoint(eOffsetOffset));
+      return FilterWrappers::Offset(mDT, mSources[0],
+                                    aOffset.mValue);
     }
 
-    case PrimitiveType::DisplacementMap:
+    already_AddRefed<FilterNode> match(const DisplacementMapAttributes& aDisplacementMap)
     {
-      RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::DISPLACEMENT_MAP);
+      RefPtr<FilterNode> filter = mDT->CreateFilter(FilterType::DISPLACEMENT_MAP);
       if (!filter) {
         return nullptr;
       }
       filter->SetAttribute(ATT_DISPLACEMENT_MAP_SCALE,
-                           atts.GetFloat(eDisplacementMapScale));
+                           aDisplacementMap.mScale);
       static const uint8_t channel[SVG_CHANNEL_A+1] = {
         COLOR_CHANNEL_R, // SVG_CHANNEL_UNKNOWN
         COLOR_CHANNEL_R, // SVG_CHANNEL_R
         COLOR_CHANNEL_G, // SVG_CHANNEL_G
         COLOR_CHANNEL_B, // SVG_CHANNEL_B
         COLOR_CHANNEL_A  // SVG_CHANNEL_A
       };
       filter->SetAttribute(ATT_DISPLACEMENT_MAP_X_CHANNEL,
-                           (uint32_t)channel[atts.GetUint(eDisplacementMapXChannel)]);
+                           (uint32_t)channel[aDisplacementMap.mXChannel]);
       filter->SetAttribute(ATT_DISPLACEMENT_MAP_Y_CHANNEL,
-                           (uint32_t)channel[atts.GetUint(eDisplacementMapYChannel)]);
-      filter->SetInput(IN_DISPLACEMENT_MAP_IN, aSources[0]);
-      filter->SetInput(IN_DISPLACEMENT_MAP_IN2, aSources[1]);
+                           (uint32_t)channel[aDisplacementMap.mYChannel]);
+      filter->SetInput(IN_DISPLACEMENT_MAP_IN, mSources[0]);
+      filter->SetInput(IN_DISPLACEMENT_MAP_IN2, mSources[1]);
       return filter.forget();
     }
 
-    case PrimitiveType::Turbulence:
+    already_AddRefed<FilterNode> match(const TurbulenceAttributes& aTurbulence)
     {
-      RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::TURBULENCE);
+      RefPtr<FilterNode> filter = mDT->CreateFilter(FilterType::TURBULENCE);
       if (!filter) {
         return nullptr;
       }
       filter->SetAttribute(ATT_TURBULENCE_BASE_FREQUENCY,
-                           atts.GetSize(eTurbulenceBaseFrequency));
+                           aTurbulence.mBaseFrequency);
       filter->SetAttribute(ATT_TURBULENCE_NUM_OCTAVES,
-                           atts.GetUint(eTurbulenceNumOctaves));
+                           aTurbulence.mOctaves);
       filter->SetAttribute(ATT_TURBULENCE_STITCHABLE,
-                           atts.GetBool(eTurbulenceStitchable));
+                           aTurbulence.mStitchable);
       filter->SetAttribute(ATT_TURBULENCE_SEED,
-                           (uint32_t)atts.GetFloat(eTurbulenceSeed));
+                           (uint32_t)aTurbulence.mSeed);
       static const uint8_t type[SVG_TURBULENCE_TYPE_TURBULENCE+1] = {
         TURBULENCE_TYPE_FRACTAL_NOISE, // SVG_TURBULENCE_TYPE_UNKNOWN
         TURBULENCE_TYPE_FRACTAL_NOISE, // SVG_TURBULENCE_TYPE_FRACTALNOISE
         TURBULENCE_TYPE_TURBULENCE     // SVG_TURBULENCE_TYPE_TURBULENCE
       };
       filter->SetAttribute(ATT_TURBULENCE_TYPE,
-                           (uint32_t)type[atts.GetUint(eTurbulenceType)]);
+                           (uint32_t)type[aTurbulence.mType]);
       filter->SetAttribute(ATT_TURBULENCE_RECT,
-                           aDescription.PrimitiveSubregion() - atts.GetIntPoint(eTurbulenceOffset));
-      return FilterWrappers::Offset(aDT, filter, atts.GetIntPoint(eTurbulenceOffset));
+                           mDescription.PrimitiveSubregion() - aTurbulence.mOffset);
+      return FilterWrappers::Offset(mDT, filter, aTurbulence.mOffset);
     }
 
-    case PrimitiveType::Composite:
+    already_AddRefed<FilterNode> match(const CompositeAttributes& aComposite)
     {
       RefPtr<FilterNode> filter;
-      uint32_t op = atts.GetUint(eCompositeOperator);
+      uint32_t op = aComposite.mOperator;
       if (op == SVG_FECOMPOSITE_OPERATOR_ARITHMETIC) {
-        const nsTArray<float>& coefficients = atts.GetFloats(eCompositeCoefficients);
+        const nsTArray<float>& coefficients = aComposite.mCoefficients;
         static const float allZero[4] = { 0, 0, 0, 0 };
-        filter = aDT->CreateFilter(FilterType::ARITHMETIC_COMBINE);
+        filter = mDT->CreateFilter(FilterType::ARITHMETIC_COMBINE);
         // All-zero coefficients sometimes occur in junk filters.
         if (!filter ||
             (coefficients.Length() == ArrayLength(allZero) &&
              ArrayEqual(coefficients.Elements(), allZero, ArrayLength(allZero)))) {
           return nullptr;
         }
         filter->SetAttribute(ATT_ARITHMETIC_COMBINE_COEFFICIENTS,
                              coefficients.Elements(), coefficients.Length());
-        filter->SetInput(IN_ARITHMETIC_COMBINE_IN, aSources[0]);
-        filter->SetInput(IN_ARITHMETIC_COMBINE_IN2, aSources[1]);
+        filter->SetInput(IN_ARITHMETIC_COMBINE_IN, mSources[0]);
+        filter->SetInput(IN_ARITHMETIC_COMBINE_IN2, mSources[1]);
       } else {
-        filter = aDT->CreateFilter(FilterType::COMPOSITE);
+        filter = mDT->CreateFilter(FilterType::COMPOSITE);
         if (!filter) {
           return nullptr;
         }
         static const uint8_t operators[SVG_FECOMPOSITE_OPERATOR_ARITHMETIC] = {
           COMPOSITE_OPERATOR_OVER, // SVG_FECOMPOSITE_OPERATOR_UNKNOWN
           COMPOSITE_OPERATOR_OVER, // SVG_FECOMPOSITE_OPERATOR_OVER
           COMPOSITE_OPERATOR_IN,   // SVG_FECOMPOSITE_OPERATOR_IN
           COMPOSITE_OPERATOR_OUT,  // SVG_FECOMPOSITE_OPERATOR_OUT
           COMPOSITE_OPERATOR_ATOP, // SVG_FECOMPOSITE_OPERATOR_ATOP
           COMPOSITE_OPERATOR_XOR   // SVG_FECOMPOSITE_OPERATOR_XOR
         };
         filter->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)operators[op]);
-        filter->SetInput(IN_COMPOSITE_IN_START, aSources[1]);
-        filter->SetInput(IN_COMPOSITE_IN_START + 1, aSources[0]);
+        filter->SetInput(IN_COMPOSITE_IN_START, mSources[1]);
+        filter->SetInput(IN_COMPOSITE_IN_START + 1, mSources[0]);
       }
       return filter.forget();
     }
 
-    case PrimitiveType::Merge:
+    already_AddRefed<FilterNode> match(const MergeAttributes& aMerge)
     {
-      if (aSources.Length() == 0) {
+      if (mSources.Length() == 0) {
         return nullptr;
       }
-      if (aSources.Length() == 1) {
-        RefPtr<FilterNode> filter(aSources[0]);
+      if (mSources.Length() == 1) {
+        RefPtr<FilterNode> filter(mSources[0]);
         return filter.forget();
       }
-      RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::COMPOSITE);
+      RefPtr<FilterNode> filter = mDT->CreateFilter(FilterType::COMPOSITE);
       if (!filter) {
         return nullptr;
       }
       filter->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)COMPOSITE_OPERATOR_OVER);
-      for (size_t i = 0; i < aSources.Length(); i++) {
-        filter->SetInput(IN_COMPOSITE_IN_START + i, aSources[i]);
+      for (size_t i = 0; i < mSources.Length(); i++) {
+        filter->SetInput(IN_COMPOSITE_IN_START + i, mSources[i]);
       }
       return filter.forget();
     }
 
-    case PrimitiveType::GaussianBlur:
+    already_AddRefed<FilterNode> match(const GaussianBlurAttributes& aGaussianBlur)
     {
-      return FilterWrappers::GaussianBlur(aDT, aSources[0],
-                                          atts.GetSize(eGaussianBlurStdDeviation));
+      return FilterWrappers::GaussianBlur(mDT, mSources[0],
+                                          aGaussianBlur.mStdDeviation);
     }
 
-    case PrimitiveType::DropShadow:
+    already_AddRefed<FilterNode> match(const DropShadowAttributes& aDropShadow)
     {
-      RefPtr<FilterNode> alpha = FilterWrappers::ToAlpha(aDT, aSources[0]);
-      RefPtr<FilterNode> blur = FilterWrappers::GaussianBlur(aDT, alpha,
-                                  atts.GetSize(eDropShadowStdDeviation));
-      RefPtr<FilterNode> offsetBlur = FilterWrappers::Offset(aDT, blur,
-                                        atts.GetIntPoint(eDropShadowOffset));
-      RefPtr<FilterNode> flood = aDT->CreateFilter(FilterType::FLOOD);
+      RefPtr<FilterNode> alpha = FilterWrappers::ToAlpha(mDT, mSources[0]);
+      RefPtr<FilterNode> blur = FilterWrappers::GaussianBlur(mDT, alpha,
+                                  aDropShadow.mStdDeviation);
+      RefPtr<FilterNode> offsetBlur = FilterWrappers::Offset(mDT, blur,
+                                        aDropShadow.mOffset);
+      RefPtr<FilterNode> flood = mDT->CreateFilter(FilterType::FLOOD);
       if (!flood) {
         return nullptr;
       }
-      Color color = atts.GetColor(eDropShadowColor);
-      if (aDescription.InputColorSpace(0) == ColorSpace::LinearRGB) {
+      Color color = aDropShadow.mColor;
+      if (mDescription.InputColorSpace(0) == ColorSpace::LinearRGB) {
         color = Color(gsRGBToLinearRGBMap[uint8_t(color.r * 255)],
                       gsRGBToLinearRGBMap[uint8_t(color.g * 255)],
                       gsRGBToLinearRGBMap[uint8_t(color.b * 255)],
                       color.a);
       }
       flood->SetAttribute(ATT_FLOOD_COLOR, color);
 
-      RefPtr<FilterNode> composite = aDT->CreateFilter(FilterType::COMPOSITE);
+      RefPtr<FilterNode> composite = mDT->CreateFilter(FilterType::COMPOSITE);
       if (!composite) {
         return nullptr;
       }
       composite->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)COMPOSITE_OPERATOR_IN);
       composite->SetInput(IN_COMPOSITE_IN_START, offsetBlur);
       composite->SetInput(IN_COMPOSITE_IN_START + 1, flood);
 
-      RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::COMPOSITE);
+      RefPtr<FilterNode> filter = mDT->CreateFilter(FilterType::COMPOSITE);
       if (!filter) {
         return nullptr;
       }
       filter->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)COMPOSITE_OPERATOR_OVER);
       filter->SetInput(IN_COMPOSITE_IN_START, composite);
-      filter->SetInput(IN_COMPOSITE_IN_START + 1, aSources[0]);
+      filter->SetInput(IN_COMPOSITE_IN_START + 1, mSources[0]);
       return filter.forget();
     }
 
-    case PrimitiveType::DiffuseLighting:
-    case PrimitiveType::SpecularLighting:
+    already_AddRefed<FilterNode> match(const SpecularLightingAttributes& aLighting)
+    {
+      return match(*(static_cast<const DiffuseLightingAttributes*>(&aLighting)));
+    }
+
+    already_AddRefed<FilterNode> match(const DiffuseLightingAttributes& aLighting)
     {
       bool isSpecular =
-        aDescription.Type() == PrimitiveType::SpecularLighting;
+        mDescription.Type() == PrimitiveType::SpecularLighting;
 
-      const AttributeMap& lightAttributes = atts.GetAttributeMap(eLightingLight);
-
-      if (lightAttributes.GetUint(eLightType) == eLightTypeNone) {
+      if (aLighting.mLightType == LightType::None) {
         return nullptr;
       }
 
       enum { POINT = 0, SPOT, DISTANT } lightType = POINT;
 
-      switch (lightAttributes.GetUint(eLightType)) {
-        case eLightTypePoint:   lightType = POINT;   break;
-        case eLightTypeSpot:    lightType = SPOT;    break;
-        case eLightTypeDistant: lightType = DISTANT; break;
+      switch (aLighting.mLightType) {
+        case LightType::Point:   lightType = POINT;   break;
+        case LightType::Spot:    lightType = SPOT;    break;
+        case LightType::Distant: lightType = DISTANT; break;
+        default: break;
       }
 
       static const FilterType filterType[2][DISTANT+1] = {
         { FilterType::POINT_DIFFUSE, FilterType::SPOT_DIFFUSE, FilterType::DISTANT_DIFFUSE },
         { FilterType::POINT_SPECULAR, FilterType::SPOT_SPECULAR, FilterType::DISTANT_SPECULAR }
       };
       RefPtr<FilterNode> filter =
-        aDT->CreateFilter(filterType[isSpecular][lightType]);
+        mDT->CreateFilter(filterType[isSpecular][lightType]);
       if (!filter) {
         return nullptr;
       }
 
-      filter->SetAttribute(ATT_LIGHTING_COLOR,
-                           atts.GetColor(eLightingColor));
+      filter->SetAttribute(ATT_LIGHTING_COLOR, aLighting.mColor);
       filter->SetAttribute(ATT_LIGHTING_SURFACE_SCALE,
-                           atts.GetFloat(eLightingSurfaceScale));
+                           aLighting.mSurfaceScale);
       filter->SetAttribute(ATT_LIGHTING_KERNEL_UNIT_LENGTH,
-                           atts.GetSize(eLightingKernelUnitLength));
+                           aLighting.mKernelUnitLength);
 
       if (isSpecular) {
         filter->SetAttribute(ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT,
-                             atts.GetFloat(eSpecularLightingSpecularConstant));
+                             aLighting.mLightingConstant);
         filter->SetAttribute(ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT,
-                             atts.GetFloat(eSpecularLightingSpecularExponent));
+                             aLighting.mSpecularExponent);
       } else {
         filter->SetAttribute(ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT,
-                             atts.GetFloat(eDiffuseLightingDiffuseConstant));
+                             aLighting.mLightingConstant);
       }
 
       switch (lightType) {
-        case POINT:
-          filter->SetAttribute(ATT_POINT_LIGHT_POSITION,
-                               lightAttributes.GetPoint3D(ePointLightPosition));
+        case POINT: {
+          Point3D position(aLighting.mLightValues[kPointLightPositionXIndex],
+                           aLighting.mLightValues[kPointLightPositionYIndex],
+                           aLighting.mLightValues[kPointLightPositionZIndex]);
+          filter->SetAttribute(ATT_POINT_LIGHT_POSITION, position);
           break;
-        case SPOT:
-          filter->SetAttribute(ATT_SPOT_LIGHT_POSITION,
-                               lightAttributes.GetPoint3D(eSpotLightPosition));
-          filter->SetAttribute(ATT_SPOT_LIGHT_POINTS_AT,
-                               lightAttributes.GetPoint3D(eSpotLightPointsAt));
+        }
+        case SPOT: {
+          Point3D position(aLighting.mLightValues[kSpotLightPositionXIndex],
+                           aLighting.mLightValues[kSpotLightPositionYIndex],
+                           aLighting.mLightValues[kSpotLightPositionZIndex]);
+          filter->SetAttribute(ATT_SPOT_LIGHT_POSITION, position);
+          Point3D pointsAt(aLighting.mLightValues[kSpotLightPointsAtXIndex],
+                           aLighting.mLightValues[kSpotLightPointsAtYIndex],
+                           aLighting.mLightValues[kSpotLightPointsAtZIndex]);
+          filter->SetAttribute(ATT_SPOT_LIGHT_POINTS_AT, pointsAt);
           filter->SetAttribute(ATT_SPOT_LIGHT_FOCUS,
-                               lightAttributes.GetFloat(eSpotLightFocus));
+                               aLighting.mLightValues[kSpotLightFocusIndex]);
           filter->SetAttribute(ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE,
-                               lightAttributes.GetFloat(eSpotLightLimitingConeAngle));
+                               aLighting.mLightValues[kSpotLightLimitingConeAngleIndex]);
           break;
-        case DISTANT:
+        }
+        case DISTANT: {
           filter->SetAttribute(ATT_DISTANT_LIGHT_AZIMUTH,
-                               lightAttributes.GetFloat(eDistantLightAzimuth));
+                               aLighting.mLightValues[kDistantLightAzimuthIndex]);
           filter->SetAttribute(ATT_DISTANT_LIGHT_ELEVATION,
-                               lightAttributes.GetFloat(eDistantLightElevation));
+                               aLighting.mLightValues[kDistantLightElevationIndex]);
           break;
+        }
       }
 
-      filter->SetInput(IN_LIGHTING_IN, aSources[0]);
+      filter->SetInput(IN_LIGHTING_IN, mSources[0]);
 
       return filter.forget();
     }
 
-    case PrimitiveType::Image:
+    already_AddRefed<FilterNode> match(const ImageAttributes& aImage)
     {
-      Matrix TM = atts.GetMatrix(eImageTransform);
+      const Matrix& TM = aImage.mTransform;
       if (!TM.Determinant()) {
         return nullptr;
       }
 
       // Pull the image from the additional image list using the index that's
       // stored in the primitive description.
       RefPtr<SourceSurface> inputImage =
-        aInputImages[atts.GetUint(eImageInputIndex)];
+        mInputImages[aImage.mInputIndex];
 
-      RefPtr<FilterNode> transform = aDT->CreateFilter(FilterType::TRANSFORM);
+      RefPtr<FilterNode> transform = mDT->CreateFilter(FilterType::TRANSFORM);
       if (!transform) {
         return nullptr;
       }
       transform->SetInput(IN_TRANSFORM_IN, inputImage);
       transform->SetAttribute(ATT_TRANSFORM_MATRIX, TM);
-      transform->SetAttribute(ATT_TRANSFORM_FILTER, atts.GetUint(eImageFilter));
+      transform->SetAttribute(ATT_TRANSFORM_FILTER, aImage.mFilter);
       return transform.forget();
     }
 
-    case PrimitiveType::ToAlpha:
+    already_AddRefed<FilterNode> match(const ToAlphaAttributes& aToAlpha)
     {
-      return FilterWrappers::ToAlpha(aDT, aSources[0]);
+      return FilterWrappers::ToAlpha(mDT, mSources[0]);
     }
+  };
 
-    default:
-      return nullptr;
-  }
+  return aDescription.Attributes().match(PrimitiveAttributesMatcher(aDescription,
+                                                                    aDT, aSources,
+                                                                    aSourceRegions,
+                                                                    aInputImages));
 }
 
 template<typename T>
 static const T&
 ElementForIndex(int32_t aIndex,
                 const nsTArray<T>& aPrimitiveElements,
                 const T& aSourceGraphicElement,
                 const T& aFillPaintElement,
@@ -1212,17 +1240,18 @@ InputAlphaModelForPrimitive(const Filter
     case PrimitiveType::ComponentTransfer:
       return AlphaModel::Unpremultiplied;
 
     case PrimitiveType::DisplacementMap:
       return aInputIndex == 0 ?
         AlphaModel::Premultiplied : AlphaModel::Unpremultiplied;
 
     case PrimitiveType::ConvolveMatrix:
-      return aDescr.Attributes().GetBool(eConvolveMatrixPreserveAlpha) ?
+      MOZ_ASSERT(aDescr.Attributes().is<ConvolveMatrixAttributes>());
+      return aDescr.Attributes().as<ConvolveMatrixAttributes>().mPreserveAlpha ?
         AlphaModel::Unpremultiplied : AlphaModel::Premultiplied;
 
     default:
       return AlphaModel::Premultiplied;
   }
 }
 
 static AlphaModel
@@ -1391,106 +1420,156 @@ InflateSizeForBlurStdDev(float aStdDev)
   double size = std::min(aStdDev, kMaxStdDeviation) * (3 * sqrt(2 * M_PI) / 4) * 1.5;
   return uint32_t(floor(size + 0.5));
 }
 
 static nsIntRegion
 ResultChangeRegionForPrimitive(const FilterPrimitiveDescription& aDescription,
                                const nsTArray<nsIntRegion>& aInputChangeRegions)
 {
-  const AttributeMap& atts = aDescription.Attributes();
-  switch (aDescription.Type()) {
+  struct PrimitiveAttributesMatcher {
+    PrimitiveAttributesMatcher(const FilterPrimitiveDescription& aDescription,
+                               const nsTArray<nsIntRegion>& aInputChangeRegions)
+    : mDescription(aDescription)
+    , mInputChangeRegions(aInputChangeRegions)
+    {}
 
-    case PrimitiveType::Empty:
-    case PrimitiveType::Flood:
-    case PrimitiveType::Turbulence:
-    case PrimitiveType::Image:
-      return nsIntRegion();
+    const FilterPrimitiveDescription& mDescription;
+    const nsTArray<nsIntRegion>& mInputChangeRegions;
 
-    case PrimitiveType::Blend:
-    case PrimitiveType::Composite:
-    case PrimitiveType::Merge:
-    case PrimitiveType::Opacity:
-      return UnionOfRegions(aInputChangeRegions);
+    nsIntRegion match(const EmptyAttributes& aEmptyAttributes)
+    {
+      return nsIntRegion();
+    }
 
-    case PrimitiveType::ColorMatrix:
-    case PrimitiveType::ComponentTransfer:
-    case PrimitiveType::ToAlpha:
-      return aInputChangeRegions[0];
+    nsIntRegion match(const BlendAttributes& aBlend)
+    {
+      return UnionOfRegions(mInputChangeRegions);
+    }
 
-    case PrimitiveType::Morphology:
+    nsIntRegion match(const ColorMatrixAttributes& aColorMatrix)
     {
-      Size radii = atts.GetSize(eMorphologyRadii);
+      return mInputChangeRegions[0];
+    }
+
+    nsIntRegion match(const MorphologyAttributes& aMorphology)
+    {
+      Size radii = aMorphology.mRadii;
       int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius);
       int32_t ry = clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius);
-      return aInputChangeRegions[0].Inflated(nsIntMargin(ry, rx, ry, rx));
+      return mInputChangeRegions[0].Inflated(nsIntMargin(ry, rx, ry, rx));
+    }
+
+    nsIntRegion match(const FloodAttributes& aFlood)
+    {
+      return nsIntRegion();
+    }
+
+    nsIntRegion match(const TileAttributes& aTile)
+    {
+      return mDescription.PrimitiveSubregion();
     }
 
-    case PrimitiveType::Tile:
-      return aDescription.PrimitiveSubregion();
+    nsIntRegion match(const ComponentTransferAttributes& aComponentTransfer)
+    {
+      return mInputChangeRegions[0];
+    }
 
-    case PrimitiveType::ConvolveMatrix:
+    nsIntRegion match(const OpacityAttributes& aOpacity)
     {
-      if (atts.GetUint(eConvolveMatrixEdgeMode) != EDGE_MODE_NONE) {
-        return aDescription.PrimitiveSubregion();
+      return UnionOfRegions(mInputChangeRegions);
+    }
+
+    nsIntRegion match(const ConvolveMatrixAttributes& aConvolveMatrix)
+    {
+      if (aConvolveMatrix.mEdgeMode != EDGE_MODE_NONE) {
+        return mDescription.PrimitiveSubregion();
       }
-      Size kernelUnitLength = atts.GetSize(eConvolveMatrixKernelUnitLength);
-      IntSize kernelSize = atts.GetIntSize(eConvolveMatrixKernelSize);
-      IntPoint target = atts.GetIntPoint(eConvolveMatrixTarget);
+      Size kernelUnitLength = aConvolveMatrix.mKernelUnitLength;
+      IntSize kernelSize = aConvolveMatrix.mKernelSize;
+      IntPoint target = aConvolveMatrix.mTarget;
       nsIntMargin m(ceil(kernelUnitLength.width * (target.x)),
                     ceil(kernelUnitLength.height * (target.y)),
                     ceil(kernelUnitLength.width * (kernelSize.width - target.x - 1)),
                     ceil(kernelUnitLength.height * (kernelSize.height - target.y - 1)));
-      return aInputChangeRegions[0].Inflated(m);
+      return mInputChangeRegions[0].Inflated(m);
+    }
+
+    nsIntRegion match(const OffsetAttributes& aOffset)
+    {
+      IntPoint offset = aOffset.mValue;
+      return mInputChangeRegions[0].MovedBy(offset.x, offset.y);
     }
 
-    case PrimitiveType::Offset:
+    nsIntRegion match(const DisplacementMapAttributes& aDisplacementMap)
     {
-      IntPoint offset = atts.GetIntPoint(eOffsetOffset);
-      return aInputChangeRegions[0].MovedBy(offset.x, offset.y);
+      int32_t scale = ceil(std::abs(aDisplacementMap.mScale));
+      return mInputChangeRegions[0].Inflated(nsIntMargin(scale, scale, scale, scale));
+    }
+
+    nsIntRegion match(const TurbulenceAttributes& aTurbulence)
+    {
+      return nsIntRegion();
     }
 
-    case PrimitiveType::DisplacementMap:
+    nsIntRegion match(const CompositeAttributes& aComposite)
     {
-      int32_t scale = ceil(std::abs(atts.GetFloat(eDisplacementMapScale)));
-      return aInputChangeRegions[0].Inflated(nsIntMargin(scale, scale, scale, scale));
+      return UnionOfRegions(mInputChangeRegions);
+    }
+
+    nsIntRegion match(const MergeAttributes& aMerge)
+    {
+      return UnionOfRegions(mInputChangeRegions);
     }
 
-    case PrimitiveType::GaussianBlur:
+    nsIntRegion match(const GaussianBlurAttributes& aGaussianBlur)
     {
-      Size stdDeviation = atts.GetSize(eGaussianBlurStdDeviation);
+      const Size& stdDeviation = aGaussianBlur.mStdDeviation;
       int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
       int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
-      return aInputChangeRegions[0].Inflated(nsIntMargin(dy, dx, dy, dx));
+      return mInputChangeRegions[0].Inflated(nsIntMargin(dy, dx, dy, dx));
     }
 
-    case PrimitiveType::DropShadow:
+    nsIntRegion match(const DropShadowAttributes& aDropShadow)
     {
-      IntPoint offset = atts.GetIntPoint(eDropShadowOffset);
-      nsIntRegion offsetRegion = aInputChangeRegions[0].MovedBy(offset.x, offset.y);
-      Size stdDeviation = atts.GetSize(eDropShadowStdDeviation);
+      IntPoint offset = aDropShadow.mOffset;
+      nsIntRegion offsetRegion = mInputChangeRegions[0].MovedBy(offset.x, offset.y);
+      Size stdDeviation = aDropShadow.mStdDeviation;
       int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
       int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
       nsIntRegion blurRegion = offsetRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
-      blurRegion.Or(blurRegion, aInputChangeRegions[0]);
+      blurRegion.Or(blurRegion, mInputChangeRegions[0]);
       return blurRegion;
     }
 
-    case PrimitiveType::DiffuseLighting:
-    case PrimitiveType::SpecularLighting:
+    nsIntRegion match(const SpecularLightingAttributes& aLighting)
     {
-      Size kernelUnitLength = atts.GetSize(eLightingKernelUnitLength);
+      return match(*(static_cast<const DiffuseLightingAttributes*>(&aLighting)));
+    }
+
+    nsIntRegion match(const DiffuseLightingAttributes& aLighting)
+    {
+      Size kernelUnitLength = aLighting.mKernelUnitLength;
       int32_t dx = ceil(kernelUnitLength.width);
       int32_t dy = ceil(kernelUnitLength.height);
-      return aInputChangeRegions[0].Inflated(nsIntMargin(dy, dx, dy, dx));
+      return mInputChangeRegions[0].Inflated(nsIntMargin(dy, dx, dy, dx));
+    }
+
+    nsIntRegion match(const ImageAttributes& aImage)
+    {
+      return nsIntRegion();
     }
 
-    default:
-      return nsIntRegion();
-  }
+    nsIntRegion match(const ToAlphaAttributes& aToAlpha)
+    {
+      return mInputChangeRegions[0];
+    }
+  };
+
+  return aDescription.Attributes().match(PrimitiveAttributesMatcher(aDescription, aInputChangeRegions));
 }
 
 /* static */ nsIntRegion
 FilterSupport::ComputeResultChangeRegion(const FilterDescription& aFilter,
                                          const nsIntRegion& aSourceGraphicChange,
                                          const nsIntRegion& aFillPaintChange,
                                          const nsIntRegion& aStrokePaintChange)
 {
@@ -1518,153 +1597,224 @@ FilterSupport::ComputeResultChangeRegion
     resultChangeRegions.AppendElement(changeRegion);
   }
 
   MOZ_RELEASE_ASSERT(!resultChangeRegions.IsEmpty());
   return resultChangeRegions[resultChangeRegions.Length() - 1];
 }
 
 static float
-ResultOfZeroUnderTransferFunction(const AttributeMap& aFunctionAttributes)
+ResultOfZeroUnderTransferFunction(const ComponentTransferAttributes& aFunctionAttributes, int32_t channel)
 {
-  switch (aFunctionAttributes.GetUint(eComponentTransferFunctionType)) {
+  switch (aFunctionAttributes.mTypes[channel]) {
     case SVG_FECOMPONENTTRANSFER_TYPE_TABLE:
     {
       const nsTArray<float>& tableValues =
-        aFunctionAttributes.GetFloats(eComponentTransferFunctionTableValues);
+        aFunctionAttributes.mValues[channel];
       if (tableValues.Length() < 2) {
         return 0.0f;
       }
       return tableValues[0];
     }
 
     case SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE:
     {
       const nsTArray<float>& tableValues =
-        aFunctionAttributes.GetFloats(eComponentTransferFunctionTableValues);
+        aFunctionAttributes.mValues[channel];
       if (tableValues.Length() < 1) {
         return 0.0f;
       }
       return tableValues[0];
     }
 
     case SVG_FECOMPONENTTRANSFER_TYPE_LINEAR:
-      return aFunctionAttributes.GetFloat(eComponentTransferFunctionIntercept);
+    {
+      const nsTArray<float>& values =
+        aFunctionAttributes.mValues[channel];
+      return values[kComponentTransferInterceptIndex];
+    }
 
     case SVG_FECOMPONENTTRANSFER_TYPE_GAMMA:
-      return aFunctionAttributes.GetFloat(eComponentTransferFunctionOffset);
+    {
+      const nsTArray<float>& values =
+        aFunctionAttributes.mValues[channel];
+      return values[kComponentTransferOffsetIndex]; 
+    }
 
     case SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY:
     default:
       return 0.0f;
   }
 }
 
 nsIntRegion
 FilterSupport::PostFilterExtentsForPrimitive(const FilterPrimitiveDescription& aDescription,
                                              const nsTArray<nsIntRegion>& aInputExtents)
 {
-  const AttributeMap& atts = aDescription.Attributes();
-  switch (aDescription.Type()) {
+  struct PrimitiveAttributesMatcher {
+    PrimitiveAttributesMatcher(const FilterPrimitiveDescription& aDescription,
+                               const nsTArray<nsIntRegion>& aInputExtents)
+    : mDescription(aDescription)
+    , mInputExtents(aInputExtents)
+    {}
 
-    case PrimitiveType::Empty:
+    const FilterPrimitiveDescription& mDescription;
+    const nsTArray<nsIntRegion>& mInputExtents;
+
+    nsIntRegion match(const EmptyAttributes& aEmptyAttributes)
+    {
       return IntRect();
+    }
 
-    case PrimitiveType::Composite:
+    nsIntRegion match(const BlendAttributes& aBlend)
+    {
+      return ResultChangeRegionForPrimitive(mDescription, mInputExtents);
+    }
+
+    nsIntRegion match(const ColorMatrixAttributes& aColorMatrix)
+    {
+      if (aColorMatrix.mType == (uint32_t)SVG_FECOLORMATRIX_TYPE_MATRIX) {
+        const nsTArray<float>& values = aColorMatrix.mValues;
+        if (values.Length() == 20 && values[19] > 0.0f) {
+          return mDescription.PrimitiveSubregion();
+        }
+      }
+      return mInputExtents[0];
+    }
+
+    nsIntRegion match(const MorphologyAttributes& aMorphology)
+    {
+      uint32_t op = aMorphology.mOperator;
+      if (op == SVG_OPERATOR_ERODE) {
+        return mInputExtents[0];
+      }
+      Size radii = aMorphology.mRadii;
+      int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius);
+      int32_t ry = clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius);
+      return mInputExtents[0].Inflated(nsIntMargin(ry, rx, ry, rx));
+    }
+
+    nsIntRegion match(const FloodAttributes& aFlood)
+    {
+      if (aFlood.mColor.a == 0.0f) {
+        return IntRect();
+      }
+      return mDescription.PrimitiveSubregion();
+    }
+
+    nsIntRegion match(const TileAttributes& aTile)
     {
-      uint32_t op = atts.GetUint(eCompositeOperator);
+      return ResultChangeRegionForPrimitive(mDescription, mInputExtents);
+    }
+
+    nsIntRegion match(const ComponentTransferAttributes& aComponentTransfer)
+    {
+      if (ResultOfZeroUnderTransferFunction(aComponentTransfer, kChannelA) > 0.0f) {
+        return mDescription.PrimitiveSubregion();
+      }
+      return mInputExtents[0];
+    }
+
+    nsIntRegion match(const OpacityAttributes& aOpacity)
+    {
+      if (aOpacity.mOpacity == 0.0f) {
+        return IntRect();
+      }
+      return ResultChangeRegionForPrimitive(mDescription, mInputExtents);
+    }
+
+    nsIntRegion match(const ConvolveMatrixAttributes& aConvolveMatrix)
+    {
+      return ResultChangeRegionForPrimitive(mDescription, mInputExtents);
+    }
+
+    nsIntRegion match(const OffsetAttributes& aOffset)
+    {
+      return ResultChangeRegionForPrimitive(mDescription, mInputExtents);
+    }
+
+    nsIntRegion match(const DisplacementMapAttributes& aDisplacementMap)
+    {
+      return ResultChangeRegionForPrimitive(mDescription, mInputExtents);
+    }
+
+    nsIntRegion match(const TurbulenceAttributes& aTurbulence)
+    {
+      return mDescription.PrimitiveSubregion();
+    }
+
+    nsIntRegion match(const CompositeAttributes& aComposite)
+    {
+      uint32_t op = aComposite.mOperator;
       if (op == SVG_FECOMPOSITE_OPERATOR_ARITHMETIC) {
         // The arithmetic composite primitive can draw outside the bounding
         // box of its source images.
-        const nsTArray<float>& coefficients = atts.GetFloats(eCompositeCoefficients);
+        const nsTArray<float>& coefficients = aComposite.mCoefficients;
         MOZ_ASSERT(coefficients.Length() == 4);
 
         // The calculation is:
         // r = c[0] * in[0] * in[1] + c[1] * in[0] + c[2] * in[1] + c[3]
         nsIntRegion region;
         if (coefficients[0] > 0.0f) {
-          region = aInputExtents[0].Intersect(aInputExtents[1]);
+          region = mInputExtents[0].Intersect(mInputExtents[1]);
         }
         if (coefficients[1] > 0.0f) {
-          region.Or(region, aInputExtents[0]);
+          region.Or(region, mInputExtents[0]);
         }
         if (coefficients[2] > 0.0f) {
-          region.Or(region, aInputExtents[1]);
+          region.Or(region, mInputExtents[1]);
         }
         if (coefficients[3] > 0.0f) {
-          region = aDescription.PrimitiveSubregion();
+          region = mDescription.PrimitiveSubregion();
         }
         return region;
       }
       if (op == SVG_FECOMPOSITE_OPERATOR_IN) {
-        return aInputExtents[0].Intersect(aInputExtents[1]);
+        return mInputExtents[0].Intersect(mInputExtents[1]);
       }
-      return ResultChangeRegionForPrimitive(aDescription, aInputExtents);
+      return ResultChangeRegionForPrimitive(mDescription, mInputExtents);
     }
 
-    case PrimitiveType::Flood:
+    nsIntRegion match(const MergeAttributes& aMerge)
     {
-      if (atts.GetColor(eFloodColor).a == 0.0f) {
-        return IntRect();
-      }
-      return aDescription.PrimitiveSubregion();
+      return ResultChangeRegionForPrimitive(mDescription, mInputExtents);
     }
 
-    case PrimitiveType::ColorMatrix:
+    nsIntRegion match(const GaussianBlurAttributes& aGaussianBlur)
     {
-      if (atts.GetUint(eColorMatrixType) == (uint32_t)SVG_FECOLORMATRIX_TYPE_MATRIX) {
-        const nsTArray<float>& values = atts.GetFloats(eColorMatrixValues);
-        if (values.Length() == 20 && values[19] > 0.0f) {
-          return aDescription.PrimitiveSubregion();
-        }
-      }
-      return aInputExtents[0];
+      return ResultChangeRegionForPrimitive(mDescription, mInputExtents);
+    }
+
+    nsIntRegion match(const DropShadowAttributes& aDropShadow)
+    {
+      return ResultChangeRegionForPrimitive(mDescription, mInputExtents);
     }
 
-    case PrimitiveType::ComponentTransfer:
+    nsIntRegion match(const DiffuseLightingAttributes& aDiffuseLighting)
     {
-      const AttributeMap& functionAttributes =
-        atts.GetAttributeMap(eComponentTransferFunctionA);
-      if (ResultOfZeroUnderTransferFunction(functionAttributes) > 0.0f) {
-        return aDescription.PrimitiveSubregion();
-      }
-      return aInputExtents[0];
+      return mDescription.PrimitiveSubregion();
     }
 
-    case PrimitiveType::Opacity:
+    nsIntRegion match(const SpecularLightingAttributes& aSpecularLighting)
     {
-      if (atts.GetFloat(eOpacityOpacity) == 0.0f) {
-        return IntRect();
-      }
-      return ResultChangeRegionForPrimitive(aDescription, aInputExtents);
+      return mDescription.PrimitiveSubregion();
     }
 
-    case PrimitiveType::Turbulence:
-    case PrimitiveType::Image:
-    case PrimitiveType::DiffuseLighting:
-    case PrimitiveType::SpecularLighting:
+    nsIntRegion match(const ImageAttributes& aImage)
     {
-      return aDescription.PrimitiveSubregion();
+      return mDescription.PrimitiveSubregion();
     }
 
-    case PrimitiveType::Morphology:
+    nsIntRegion match(const ToAlphaAttributes& aToAlpha)
     {
-      uint32_t op = atts.GetUint(eMorphologyOperator);
-      if (op == SVG_OPERATOR_ERODE) {
-        return aInputExtents[0];
-      }
-      Size radii = atts.GetSize(eMorphologyRadii);
-      int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius);
-      int32_t ry = clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius);
-      return aInputExtents[0].Inflated(nsIntMargin(ry, rx, ry, rx));
+      return ResultChangeRegionForPrimitive(mDescription, mInputExtents);
     }
+  };
 
-    default:
-      return ResultChangeRegionForPrimitive(aDescription, aInputExtents);
-  }
+  return aDescription.Attributes().match(PrimitiveAttributesMatcher(aDescription, aInputExtents));
 }
 
 /* static */ nsIntRegion
 FilterSupport::ComputePostFilterExtents(const FilterDescription& aFilter,
                                         const nsIntRegion& aSourceGraphicExtents)
 {
   const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
   MOZ_RELEASE_ASSERT(!primitives.IsEmpty());
@@ -1692,109 +1842,163 @@ FilterSupport::ComputePostFilterExtents(
   return postFilterExtents[postFilterExtents.Length() - 1];
 }
 
 static nsIntRegion
 SourceNeededRegionForPrimitive(const FilterPrimitiveDescription& aDescription,
                                const nsIntRegion& aResultNeededRegion,
                                int32_t aInputIndex)
 {
-  const AttributeMap& atts = aDescription.Attributes();
-  switch (aDescription.Type()) {
+  struct PrimitiveAttributesMatcher {
+    PrimitiveAttributesMatcher(const FilterPrimitiveDescription& aDescription,
+                               const nsIntRegion& aResultNeededRegion,
+                               int32_t aInputIndex)
+    : mDescription(aDescription)
+    , mResultNeededRegion(aResultNeededRegion)
+    , mInputIndex(aInputIndex)
+    {}
+
+    const FilterPrimitiveDescription& mDescription;
+    const nsIntRegion& mResultNeededRegion;
+    const int32_t mInputIndex;
+
+    nsIntRegion match(const EmptyAttributes& aEmptyAttributes)
+    {
+      return nsIntRegion();
+    }
 
-    case PrimitiveType::Flood:
-    case PrimitiveType::Turbulence:
-    case PrimitiveType::Image:
+    nsIntRegion match(const BlendAttributes& aBlend)
+    {
+      return mResultNeededRegion;
+    }
+
+    nsIntRegion match(const ColorMatrixAttributes& aColorMatrix)
+    {
+      return mResultNeededRegion;
+    }
+
+    nsIntRegion match(const MorphologyAttributes& aMorphology)
+    {
+      Size radii = aMorphology.mRadii;
+      int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius);
+      int32_t ry = clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius);
+      return mResultNeededRegion.Inflated(nsIntMargin(ry, rx, ry, rx));
+    }
+
+    nsIntRegion match(const FloodAttributes& aFlood)
+    {
       MOZ_CRASH("GFX: this shouldn't be called for filters without inputs");
       return nsIntRegion();
-
-    case PrimitiveType::Empty:
-      return nsIntRegion();
+    }
 
-    case PrimitiveType::Blend:
-    case PrimitiveType::Composite:
-    case PrimitiveType::Merge:
-    case PrimitiveType::ColorMatrix:
-    case PrimitiveType::ComponentTransfer:
-    case PrimitiveType::ToAlpha:
-    case PrimitiveType::Opacity:
-      return aResultNeededRegion;
-
-    case PrimitiveType::Morphology:
+    nsIntRegion match(const TileAttributes& aTile)
     {
-      Size radii = atts.GetSize(eMorphologyRadii);
-      int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius);
-      int32_t ry = clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius);
-      return aResultNeededRegion.Inflated(nsIntMargin(ry, rx, ry, rx));
+      return IntRect(INT32_MIN/2, INT32_MIN/2, INT32_MAX, INT32_MAX);
     }
 
-    case PrimitiveType::Tile:
-      return IntRect(INT32_MIN/2, INT32_MIN/2, INT32_MAX, INT32_MAX);
+    nsIntRegion match(const ComponentTransferAttributes& aComponentTransfer)
+    {
+      return mResultNeededRegion;
+    }
 
-    case PrimitiveType::ConvolveMatrix:
+    nsIntRegion match(const OpacityAttributes& aOpacity)
     {
-      Size kernelUnitLength = atts.GetSize(eConvolveMatrixKernelUnitLength);
-      IntSize kernelSize = atts.GetIntSize(eConvolveMatrixKernelSize);
-      IntPoint target = atts.GetIntPoint(eConvolveMatrixTarget);
+      return mResultNeededRegion;
+    }
+
+    nsIntRegion match(const ConvolveMatrixAttributes& aConvolveMatrix)
+    {
+      Size kernelUnitLength = aConvolveMatrix.mKernelUnitLength;
+      IntSize kernelSize = aConvolveMatrix.mKernelSize;
+      IntPoint target = aConvolveMatrix.mTarget;
       nsIntMargin m(ceil(kernelUnitLength.width * (kernelSize.width - target.x - 1)),
                     ceil(kernelUnitLength.height * (kernelSize.height - target.y - 1)),
                     ceil(kernelUnitLength.width * (target.x)),
                     ceil(kernelUnitLength.height * (target.y)));
-      return aResultNeededRegion.Inflated(m);
+      return mResultNeededRegion.Inflated(m);
+    }
+
+    nsIntRegion match(const OffsetAttributes& aOffset)
+    {
+      IntPoint offset = aOffset.mValue;
+      return mResultNeededRegion.MovedBy(-nsIntPoint(offset.x, offset.y));
     }
 
-    case PrimitiveType::Offset:
+    nsIntRegion match(const DisplacementMapAttributes& aDisplacementMap)
     {
-      IntPoint offset = atts.GetIntPoint(eOffsetOffset);
-      return aResultNeededRegion.MovedBy(-nsIntPoint(offset.x, offset.y));
+      if (mInputIndex == 1) {
+        return mResultNeededRegion;
+      }
+      int32_t scale = ceil(std::abs(aDisplacementMap.mScale));
+      return mResultNeededRegion.Inflated(nsIntMargin(scale, scale, scale, scale));
+    }
+
+    nsIntRegion match(const TurbulenceAttributes& aTurbulence)
+    {
+      MOZ_CRASH("GFX: this shouldn't be called for filters without inputs");
+      return nsIntRegion();
     }
 
-    case PrimitiveType::DisplacementMap:
+    nsIntRegion match(const CompositeAttributes& aComposite)
     {
-      if (aInputIndex == 1) {
-        return aResultNeededRegion;
-      }
-      int32_t scale = ceil(std::abs(atts.GetFloat(eDisplacementMapScale)));
-      return aResultNeededRegion.Inflated(nsIntMargin(scale, scale, scale, scale));
+      return mResultNeededRegion;
+    }
+
+    nsIntRegion match(const MergeAttributes& aMerge)
+    {
+      return mResultNeededRegion;
     }
 
-    case PrimitiveType::GaussianBlur:
+    nsIntRegion match(const GaussianBlurAttributes& aGaussianBlur)
     {
-      Size stdDeviation = atts.GetSize(eGaussianBlurStdDeviation);
+      const Size& stdDeviation = aGaussianBlur.mStdDeviation;
       int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
       int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
-      return aResultNeededRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
+      return mResultNeededRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
     }
 
-    case PrimitiveType::DropShadow:
+    nsIntRegion match(const DropShadowAttributes& aDropShadow)
     {
-      IntPoint offset = atts.GetIntPoint(eDropShadowOffset);
+      IntPoint offset = aDropShadow.mOffset;
       nsIntRegion offsetRegion =
-        aResultNeededRegion.MovedBy(-nsIntPoint(offset.x, offset.y));
-      Size stdDeviation = atts.GetSize(eDropShadowStdDeviation);
+        mResultNeededRegion.MovedBy(-nsIntPoint(offset.x, offset.y));
+      Size stdDeviation = aDropShadow.mStdDeviation;
       int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
       int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
       nsIntRegion blurRegion = offsetRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
-      blurRegion.Or(blurRegion, aResultNeededRegion);
+      blurRegion.Or(blurRegion, mResultNeededRegion);
       return blurRegion;
     }
 
-    case PrimitiveType::DiffuseLighting:
-    case PrimitiveType::SpecularLighting:
+    nsIntRegion match(const SpecularLightingAttributes& aLighting)
     {
-      Size kernelUnitLength = atts.GetSize(eLightingKernelUnitLength);
+      return match(*(static_cast<const DiffuseLightingAttributes*>(&aLighting)));
+    }
+
+    nsIntRegion match(const DiffuseLightingAttributes& aLighting)
+    {
+      Size kernelUnitLength = aLighting.mKernelUnitLength;
       int32_t dx = ceil(kernelUnitLength.width);
       int32_t dy = ceil(kernelUnitLength.height);
-      return aResultNeededRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
+      return mResultNeededRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
     }
 
-    default:
+    nsIntRegion match(const ImageAttributes& aImage)
+    {
+      MOZ_CRASH("GFX: this shouldn't be called for filters without inputs");
       return nsIntRegion();
-  }
+    }
 
+    nsIntRegion match(const ToAlphaAttributes& aToAlpha)
+    {
+      return mResultNeededRegion;
+    }
+  };
+
+  return aDescription.Attributes().match(PrimitiveAttributesMatcher(aDescription, aResultNeededRegion, aInputIndex));
 }
 
 /* static */ void
 FilterSupport::ComputeSourceNeededRegions(const FilterDescription& aFilter,
                                           const nsIntRegion& aResultNeededRegion,
                                           nsIntRegion& aSourceGraphicNeededRegion,
                                           nsIntRegion& aFillPaintNeededRegion,
                                           nsIntRegion& aStrokePaintNeededRegion)
@@ -1832,23 +2036,25 @@ FilterSupport::ComputeSourceNeededRegion
   aSourceGraphicNeededRegion.And(aSourceGraphicNeededRegion,
                                  firstDescr.FilterSpaceBounds());
 }
 
 // FilterPrimitiveDescription
 
 FilterPrimitiveDescription::FilterPrimitiveDescription()
  : mType(PrimitiveType::Empty)
+ , mAttributes(EmptyAttributes())
  , mOutputColorSpace(ColorSpace::SRGB)
  , mIsTainted(false)
 {
 }
 
 FilterPrimitiveDescription::FilterPrimitiveDescription(PrimitiveType aType)
  : mType(aType)
+ , mAttributes(EmptyAttributes())
  , mOutputColorSpace(ColorSpace::SRGB)
  , mIsTainted(false)
 {
 }
 
 FilterPrimitiveDescription::FilterPrimitiveDescription(const FilterPrimitiveDescription& aOther)
  : mType(aOther.mType)
  , mAttributes(aOther.mAttributes)
@@ -1921,358 +2127,10 @@ FilterPrimitiveDescription::operator==(c
 // FilterDescription
 
 bool
 FilterDescription::operator==(const FilterDescription& aOther) const
 {
   return mPrimitives == aOther.mPrimitives;
 }
 
-// AttributeMap
-
-// A class that wraps different types for easy storage in a hashtable. Only
-// used by AttributeMap.
-struct FilterAttribute {
-  FilterAttribute(const FilterAttribute& aOther);
-
-  ~FilterAttribute();
-
-  bool operator==(const FilterAttribute& aOther) const;
-  bool operator!=(const FilterAttribute& aOther) const
-  {
-    return !(*this == aOther);
-  }
-
-  AttributeType Type() const { return mType; }
-
-#define MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(type, typeLabel)   \
-  explicit FilterAttribute(type aValue)                        \
-   : mType(AttributeType::e##typeLabel), m##typeLabel(aValue)  \
-  {}                                                           \
-  type As##typeLabel() {                                       \
-    MOZ_ASSERT(mType == AttributeType::e##typeLabel);          \
-    return m##typeLabel;                                       \
-  }
-
-#define MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(className)         \
-  explicit FilterAttribute(const className& aValue)            \
-   : mType(AttributeType::e##className), m##className(new className(aValue)) \
-  {}                                                           \
-  className As##className() {                                  \
-    MOZ_ASSERT(mType == AttributeType::e##className);          \
-    return *m##className;                                      \
-  }
-
-  MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(bool, Bool)
-  MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(uint32_t, Uint)
-  MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(float, Float)
-  MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Size)
-  MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(IntSize)
-  MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(IntPoint)
-  MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Matrix)
-  MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Matrix5x4)
-  MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Point3D)
-  MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Color)
-
-#undef MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC
-#undef MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS
-
-  explicit FilterAttribute(AttributeMap&& aValue)
-   : mType(AttributeType::eAttributeMap), mAttributeMap(new AttributeMap(aValue))
-  {}
-
-  AttributeMap* AsAttributeMap() {
-    MOZ_ASSERT(mType == AttributeType::eAttributeMap);
-    return mAttributeMap;
-  }
-
-  FilterAttribute(const float* aValue, uint32_t aLength)
-   : mType(AttributeType::eFloats)
-  {
-    mFloats = new nsTArray<float>();
-    mFloats->AppendElements(aValue, aLength);
-  }
-
-  const nsTArray<float>& AsFloats() const {
-    MOZ_ASSERT(mType == AttributeType::eFloats);
-    return *mFloats;
-  }
-
-private:
-  const AttributeType mType;
-
-  union {
-    bool mBool;
-    uint32_t mUint;
-    float mFloat;
-    Size* mSize;
-    IntSize* mIntSize;
-    IntPoint* mIntPoint;
-    Matrix* mMatrix;
-    Matrix5x4* mMatrix5x4;
-    Point3D* mPoint3D;
-    Color* mColor;
-    AttributeMap* mAttributeMap;
-    nsTArray<float>* mFloats;
-  };
-};
-
-FilterAttribute::FilterAttribute(const FilterAttribute& aOther)
- : mType(aOther.mType)
-{
-  switch (mType) {
-    case AttributeType::eBool:
-      mBool = aOther.mBool;
-      break;
-    case AttributeType::eUint:
-      mUint = aOther.mUint;
-      break;
-    case AttributeType::eFloat:
-      mFloat = aOther.mFloat;
-      break;
-
-#define HANDLE_CLASS(className)                            \
-    case AttributeType::e##className:                      \
-      m##className = new className(*aOther.m##className);  \
-      break;
-
-    HANDLE_CLASS(Size)
-    HANDLE_CLASS(IntSize)
-    HANDLE_CLASS(IntPoint)
-    HANDLE_CLASS(Matrix)
-    HANDLE_CLASS(Matrix5x4)
-    HANDLE_CLASS(Point3D)
-    HANDLE_CLASS(Color)
-    HANDLE_CLASS(AttributeMap)
-
-#undef HANDLE_CLASS
-
-    case AttributeType::eFloats:
-      mFloats = new nsTArray<float>(*aOther.mFloats);
-      break;
-    case AttributeType::Max:
-      break;
-  }
-}
-
-FilterAttribute::~FilterAttribute() {
-  switch (mType) {
-    case AttributeType::Max:
-    case AttributeType::eBool:
-    case AttributeType::eUint:
-    case AttributeType::eFloat:
-      break;
-
-#define HANDLE_CLASS(className)                            \
-    case AttributeType::e##className:                      \
-      delete m##className;                                 \
-      break;
-
-    HANDLE_CLASS(Size)
-    HANDLE_CLASS(IntSize)
-    HANDLE_CLASS(IntPoint)
-    HANDLE_CLASS(Matrix)
-    HANDLE_CLASS(Matrix5x4)
-    HANDLE_CLASS(Point3D)
-    HANDLE_CLASS(Color)
-    HANDLE_CLASS(AttributeMap)
-
-#undef HANDLE_CLASS
-
-    case AttributeType::eFloats:
-      delete mFloats;
-      break;
-  }
-}
-
-bool
-FilterAttribute::operator==(const FilterAttribute& aOther) const
-{
-  if (mType != aOther.mType) {
-    return false;
-  }
-
-  switch (mType) {
-
-#define HANDLE_TYPE(typeName)                              \
-    case AttributeType::e##typeName:                       \
-      return m##typeName == aOther.m##typeName;
-
-    HANDLE_TYPE(Bool)
-    HANDLE_TYPE(Uint)
-    HANDLE_TYPE(Float)
-    HANDLE_TYPE(Size)
-    HANDLE_TYPE(IntSize)
-    HANDLE_TYPE(IntPoint)
-    HANDLE_TYPE(Matrix)
-    HANDLE_TYPE(Matrix5x4)
-    HANDLE_TYPE(Point3D)
-    HANDLE_TYPE(Color)
-    HANDLE_TYPE(AttributeMap)
-    HANDLE_TYPE(Floats)
-
-#undef HANDLE_TYPE
-
-    default:
-      return false;
-  }
-}
-
-typedef FilterAttribute Attribute;
-
-AttributeMap::AttributeMap()
-{
-}
-
-AttributeMap::~AttributeMap()
-{
-}
-
-AttributeMap::AttributeMap(const AttributeMap& aOther)
-{
-  for (auto iter = aOther.mMap.Iter(); !iter.Done(); iter.Next()) {
-    const uint32_t& attributeName = iter.Key();
-    Attribute* attribute = iter.UserData();
-    mMap.Put(attributeName, new Attribute(*attribute));
-  }
-}
-
-AttributeMap&
-AttributeMap::operator=(const AttributeMap& aOther)
-{
-  if (this != &aOther) {
-    mMap.Clear();
-    for (auto iter = aOther.mMap.Iter(); !iter.Done(); iter.Next()) {
-      const uint32_t& attributeName = iter.Key();
-      Attribute* attribute = iter.UserData();
-      mMap.Put(attributeName, new Attribute(*attribute));
-    }
-  }
-  return *this;
-}
-
-AttributeMap::AttributeMap(AttributeMap&& aOther)
-{
-  mMap.SwapElements(aOther.mMap);
-}
-
-AttributeMap&
-AttributeMap::operator=(AttributeMap&& aOther)
-{
-  if (this != &aOther) {
-    mMap.Clear();
-    mMap.SwapElements(aOther.mMap);
-  }
-  return *this;
-}
-
-bool
-AttributeMap::operator==(const AttributeMap& aOther) const
-{
-  if (mMap.Count() != aOther.mMap.Count()) {
-    return false;
-  }
-
-  for (auto iter = aOther.mMap.Iter(); !iter.Done(); iter.Next()) {
-    const uint32_t& attributeName = iter.Key();
-    Attribute* attribute = iter.UserData();
-    Attribute* matchingAttribute = mMap.Get(attributeName);
-    if (!matchingAttribute || *matchingAttribute != *attribute) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
-uint32_t
-AttributeMap::Count() const
-{
-  return mMap.Count();
-}
-
-nsClassHashtable<nsUint32HashKey, FilterAttribute>::Iterator
-AttributeMap::ConstIter() const
-{
-  return mMap.ConstIter();
-}
-
-/* static */ AttributeType
-AttributeMap::GetType(FilterAttribute* aAttribute)
-{
-  return aAttribute->Type();
-}
-
-#define MAKE_ATTRIBUTE_HANDLERS_BASIC(type, typeLabel, defaultValue) \
-  type                                                               \
-  AttributeMap::Get##typeLabel(AttributeName aName) const {          \
-    Attribute* value = mMap.Get(aName);                              \
-    return value ? value->As##typeLabel() : defaultValue;            \
-  }                                                                  \
-  void                                                               \
-  AttributeMap::Set(AttributeName aName, type aValue) {              \
-    mMap.Put(aName, new Attribute(aValue));                          \
-  }
-
-#define MAKE_ATTRIBUTE_HANDLERS_CLASS(className)                     \
-  className                                                          \
-  AttributeMap::Get##className(AttributeName aName) const {          \
-    Attribute* value = mMap.Get(aName);                              \
-    return value ? value->As##className() : className();             \
-  }                                                                  \
-  void                                                               \
-  AttributeMap::Set(AttributeName aName, const className& aValue) {  \
-    mMap.Put(aName, new Attribute(aValue));                          \
-  }
-
-MAKE_ATTRIBUTE_HANDLERS_BASIC(bool, Bool, false)
-MAKE_ATTRIBUTE_HANDLERS_BASIC(uint32_t, Uint, 0)
-MAKE_ATTRIBUTE_HANDLERS_BASIC(float, Float, 0)
-MAKE_ATTRIBUTE_HANDLERS_CLASS(Size)
-MAKE_ATTRIBUTE_HANDLERS_CLASS(IntSize)
-MAKE_ATTRIBUTE_HANDLERS_CLASS(IntPoint)
-MAKE_ATTRIBUTE_HANDLERS_CLASS(Matrix)
-MAKE_ATTRIBUTE_HANDLERS_CLASS(Matrix5x4)
-MAKE_ATTRIBUTE_HANDLERS_CLASS(Point3D)
-MAKE_ATTRIBUTE_HANDLERS_CLASS(Color)
-
-#undef MAKE_ATTRIBUTE_HANDLERS_BASIC
-#undef MAKE_ATTRIBUTE_HANDLERS_CLASS
-
-const AttributeMap sEmptyAttributeMap;
-
-const AttributeMap&
-AttributeMap::GetAttributeMap(AttributeName aName) const {
-  Attribute* value = mMap.Get(aName);
-  return value ? *value->AsAttributeMap() : sEmptyAttributeMap;
-}
-
-const AttributeMap*
-AttributeMap::MaybeGetAttributeMap(AttributeName aName) const {
-  Attribute* value = mMap.Get(aName);
-  if (value) {
-    return value->AsAttributeMap();
-  } else {
-    return nullptr;
-  }
-}
-
-void
-AttributeMap::Set(AttributeName aName, AttributeMap&& aValue) {
-  mMap.Put(aName, new Attribute(std::move(aValue)));
-}
-
-const nsTArray<float>&
-AttributeMap::GetFloats(AttributeName aName) const
-{
-  Attribute* value = mMap.LookupForAdd(aName).OrInsert(
-    [] () { return new Attribute(nullptr, 0); });
-  return value->AsFloats();
-}
-
-void
-AttributeMap::Set(AttributeName aName, const float* aValues, int32_t aLength)
-{
-  mMap.Put(aName, new Attribute(aValues, aLength));
-}
-
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/src/FilterSupport.h
+++ b/gfx/src/FilterSupport.h
@@ -82,168 +82,343 @@ const unsigned short SVG_TURBULENCE_TYPE
 const unsigned short SVG_FECOMPOSITE_OPERATOR_UNKNOWN = 0;
 const unsigned short SVG_FECOMPOSITE_OPERATOR_OVER = 1;
 const unsigned short SVG_FECOMPOSITE_OPERATOR_IN = 2;
 const unsigned short SVG_FECOMPOSITE_OPERATOR_OUT = 3;
 const unsigned short SVG_FECOMPOSITE_OPERATOR_ATOP = 4;
 const unsigned short SVG_FECOMPOSITE_OPERATOR_XOR = 5;
 const unsigned short SVG_FECOMPOSITE_OPERATOR_ARITHMETIC = 6;
 
-enum AttributeName {
-  eBlendBlendmode = 0,
-  eMorphologyRadii,
-  eMorphologyOperator,
-  eColorMatrixType,
-  eColorMatrixValues,
-  eFloodColor,
-  eTileSourceRect,
-  eOpacityOpacity,
-  // In many cases R, G, and B will all use an identical
-  // attribute map - in this case we can reduce unnecessary
-  // copying by having one shared AttributeName
-  eComponentTransferFunctionRGB,
-  eComponentTransferFunctionR,
-  eComponentTransferFunctionG,
-  eComponentTransferFunctionB,
-  eComponentTransferFunctionA,
-  eComponentTransferFunctionType,
-  eComponentTransferFunctionTableValues,
-  eComponentTransferFunctionSlope,
-  eComponentTransferFunctionIntercept,
-  eComponentTransferFunctionAmplitude,
-  eComponentTransferFunctionExponent,
-  eComponentTransferFunctionOffset,
-  eConvolveMatrixKernelSize,
-  eConvolveMatrixKernelMatrix,
-  eConvolveMatrixDivisor,
-  eConvolveMatrixBias,
-  eConvolveMatrixTarget,
-  eConvolveMatrixEdgeMode,
-  eConvolveMatrixKernelUnitLength,
-  eConvolveMatrixPreserveAlpha,
-  eOffsetOffset,
-  eDropShadowStdDeviation,
-  eDropShadowOffset,
-  eDropShadowColor,
-  eDisplacementMapScale,
-  eDisplacementMapXChannel,
-  eDisplacementMapYChannel,
-  eTurbulenceOffset,
-  eTurbulenceBaseFrequency,
-  eTurbulenceNumOctaves,
-  eTurbulenceSeed,
-  eTurbulenceStitchable,
-  eTurbulenceType,
-  eCompositeOperator,
-  eCompositeCoefficients,
-  eGaussianBlurStdDeviation,
-  eLightingLight,
-  eLightingSurfaceScale,
-  eLightingKernelUnitLength,
-  eLightingColor,
-  eDiffuseLightingDiffuseConstant,
-  eSpecularLightingSpecularConstant,
-  eSpecularLightingSpecularExponent,
-  eLightType,
-  eLightTypeNone,
-  eLightTypePoint,
-  eLightTypeSpot,
-  eLightTypeDistant,
-  ePointLightPosition,
-  eSpotLightPosition,
-  eSpotLightPointsAt,
-  eSpotLightFocus,
-  eSpotLightLimitingConeAngle,
-  eDistantLightAzimuth,
-  eDistantLightElevation,
-  eImageInputIndex,
-  eImageFilter,
-  eImageNativeSize,
-  eImageSubregion,
-  eImageTransform,
-  eLastAttributeName
-};
-
 class DrawTarget;
 class SourceSurface;
 struct FilterAttribute;
 
-enum class AttributeType {
-  eBool,
-  eUint,
-  eFloat,
-  eSize,
-  eIntSize,
-  eIntPoint,
-  eMatrix,
-  eMatrix5x4,
-  ePoint3D,
-  eColor,
-  eAttributeMap,
-  eFloats,
-  Max
-};
-
 // Limits
 const float kMaxStdDeviation = 500;
 
-// A class that stores values of different types, keyed by an attribute name.
-// The Get*() methods assert that they're called for the same type that the
-// attribute was Set() with.
-// AttributeMaps can be nested because AttributeMap is a valid attribute type.
-class AttributeMap final {
-public:
-  AttributeMap();
-  AttributeMap(const AttributeMap& aOther);
-  AttributeMap& operator=(const AttributeMap& aOther);
-  AttributeMap(AttributeMap&& aOther);
-  AttributeMap& operator=(AttributeMap&& aOther);
-  bool operator==(const AttributeMap& aOther) const;
-  bool operator!=(const AttributeMap& aOther) const
-  {
-    return !(*this == aOther);
+enum class PrimitiveType {
+  Empty = 0,
+  Blend,
+  Morphology,
+  ColorMatrix,
+  Flood,
+  Tile,
+  ComponentTransfer,
+  Opacity,
+  ConvolveMatrix,
+  Offset,
+  DisplacementMap,
+  Turbulence,
+  Composite,
+  Merge,
+  Image,
+  GaussianBlur,
+  DropShadow,
+  DiffuseLighting,
+  SpecularLighting,
+  ToAlpha,
+  Max
+};
+
+// Simple PrimitiveAttributes:
+
+struct EmptyAttributes {
+  bool operator==(const EmptyAttributes& aOther) const {
+    return true;
+  }
+};
+
+struct BlendAttributes {
+  uint32_t mBlendMode;
+
+  bool operator==(const BlendAttributes& aOther) const {
+    return mBlendMode == aOther.mBlendMode;
+  }
+};
+
+struct MorphologyAttributes {
+  uint32_t mOperator;
+  Size mRadii;
+
+  bool operator==(const MorphologyAttributes& aOther) const {
+    return mOperator == aOther.mOperator &&
+           mRadii == aOther.mRadii;
+  }
+};
+
+struct FloodAttributes {
+  Color mColor;
+
+  bool operator==(const FloodAttributes& aOther) const {
+    return mColor == aOther.mColor;
+  }
+};
+
+struct TileAttributes {
+  bool operator==(const TileAttributes& aOther) const {
+    return true;
+  }
+};
+
+struct OpacityAttributes {
+  float mOpacity;
+
+  bool operator==(const OpacityAttributes& aOther) const {
+    return mOpacity == aOther.mOpacity;
+  }
+};
+
+struct OffsetAttributes {
+  IntPoint mValue;
+
+  bool operator==(const OffsetAttributes& aOther) const {
+    return mValue == aOther.mValue;
   }
-  ~AttributeMap();
+};
+
+struct DisplacementMapAttributes {
+  float mScale;
+  uint32_t mXChannel;
+  uint32_t mYChannel;
+
+  bool operator==(const DisplacementMapAttributes& aOther) const {
+    return mScale == aOther.mScale &&
+           mXChannel == aOther.mXChannel &&
+           mYChannel == aOther.mYChannel;
+  }
+};
+
+struct TurbulenceAttributes {
+  IntPoint mOffset;
+  Size mBaseFrequency;
+  float mSeed;
+  uint32_t mOctaves;
+  bool mStitchable;
+  uint32_t mType;
+
+  bool operator==(const TurbulenceAttributes& aOther) const {
+    return mOffset == aOther.mOffset &&
+           mBaseFrequency == aOther.mBaseFrequency &&
+           mSeed == aOther.mSeed &&
+           mOctaves == aOther.mOctaves &&
+           mStitchable == aOther.mStitchable &&
+           mType == aOther.mType;
+  }
+};
+
+struct MergeAttributes {
+  bool operator==(const MergeAttributes& aOther) const {
+    return true;
+  }
+};
+
+struct ImageAttributes {
+  uint32_t mFilter;
+  uint32_t mInputIndex;
+  Matrix mTransform;
+
+  bool operator==(const ImageAttributes& aOther) const {
+    return mFilter == aOther.mFilter &&
+           mInputIndex == aOther.mInputIndex &&
+           mTransform.ExactlyEquals(aOther.mTransform);
+  }
+};
+
+struct GaussianBlurAttributes {
+  Size mStdDeviation;
+
+  bool operator==(const GaussianBlurAttributes& aOther) const {
+    return mStdDeviation == aOther.mStdDeviation;
+  }
+};
+
+struct DropShadowAttributes {
+  Size mStdDeviation;
+  IntPoint mOffset;
+  Color mColor;
+
+  bool operator==(const DropShadowAttributes& aOther) const {
+    return mStdDeviation == aOther.mStdDeviation &&
+           mOffset == aOther.mOffset &&
+           mColor == aOther.mColor;
+  }
+};
+
+struct ToAlphaAttributes {
+  bool operator==(const ToAlphaAttributes& aOther) const {
+    return true;
+  }
+};
+
+// Complex PrimitiveAttributes:
 
-  void Set(AttributeName aName, bool aValue);
-  void Set(AttributeName aName, uint32_t aValue);
-  void Set(AttributeName aName, float aValue);
-  void Set(AttributeName aName, const Size& aValue);
-  void Set(AttributeName aName, const IntSize& aValue);
-  void Set(AttributeName aName, const IntPoint& aValue);
-  void Set(AttributeName aName, const Matrix& aValue);
-  void Set(AttributeName aName, const Matrix5x4& aValue);
-  void Set(AttributeName aName, const Point3D& aValue);
-  void Set(AttributeName aName, const Color& aValue);
-  void Set(AttributeName aName, AttributeMap&& aValue);
-  void Set(AttributeName aName, const float* aValues, int32_t aLength);
+class ImplicitlyCopyableFloatArray : public nsTArray<float>
+{
+public:
+  ImplicitlyCopyableFloatArray() : nsTArray<float>() {}
+
+  ImplicitlyCopyableFloatArray(ImplicitlyCopyableFloatArray&& aOther)
+  : nsTArray<float>(std::move(aOther)) {}
+
+  ImplicitlyCopyableFloatArray& operator=(ImplicitlyCopyableFloatArray&& aOther)
+  {
+    nsTArray<float>::operator=(std::move(aOther));
+    return *this;
+  }
+
+  ImplicitlyCopyableFloatArray(const ImplicitlyCopyableFloatArray& aOther) = default;
+
+  ImplicitlyCopyableFloatArray& operator=(const ImplicitlyCopyableFloatArray& aOther)
+  {
+    nsTArray<float>::operator=(aOther);
+    return *this;
+  }
+};
+
+struct ColorMatrixAttributes {
+  uint32_t mType;
+  ImplicitlyCopyableFloatArray mValues;
+
+  bool operator==(const ColorMatrixAttributes& aOther) const {
+    return mType == aOther.mType &&
+           mValues == aOther.mValues;
+  }
+};
+
+// If the types for G and B are SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN,
+// assume the R values are RGB - this lets us avoid copies.
+const uint32_t kChannelROrRGB = 0;
+const uint32_t kChannelG = 1;
+const uint32_t kChannelB = 2;
+const uint32_t kChannelA = 3;
+
+const uint32_t kComponentTransferSlopeIndex = 0;
+const uint32_t kComponentTransferInterceptIndex = 1;
+
+const uint32_t kComponentTransferAmplitudeIndex = 0;
+const uint32_t kComponentTransferExponentIndex = 1;
+const uint32_t kComponentTransferOffsetIndex = 2;
+
+struct ComponentTransferAttributes {
+  uint8_t mTypes[4];
+  ImplicitlyCopyableFloatArray mValues[4];
+
+  bool operator==(const ComponentTransferAttributes& aOther) const {
+    return mTypes[0] == aOther.mTypes[0] &&
+           mTypes[1] == aOther.mTypes[1] &&
+           mTypes[2] == aOther.mTypes[2] &&
+           mTypes[3] == aOther.mTypes[3] &&
+           mValues[0] == aOther.mValues[0] &&
+           mValues[1] == aOther.mValues[1] &&
+           mValues[2] == aOther.mValues[2] &&
+           mValues[3] == aOther.mValues[3];
+  }
+};
+
+struct ConvolveMatrixAttributes {
+  IntSize mKernelSize;
+  ImplicitlyCopyableFloatArray mKernelMatrix;
+  float mDivisor;
+  float mBias;
+  IntPoint mTarget;
+  uint32_t mEdgeMode;
+  Size mKernelUnitLength;
+  bool mPreserveAlpha;
 
-  bool GetBool(AttributeName aName) const;
-  uint32_t GetUint(AttributeName aName) const;
-  float GetFloat(AttributeName aName) const;
-  Size GetSize(AttributeName aName) const;
-  IntSize GetIntSize(AttributeName aName) const;
-  IntPoint GetIntPoint(AttributeName aName) const;
-  Matrix GetMatrix(AttributeName aName) const;
-  Matrix5x4 GetMatrix5x4(AttributeName aName) const;
-  Point3D GetPoint3D(AttributeName aName) const;
-  Color GetColor(AttributeName aName) const;
-  const AttributeMap& GetAttributeMap(AttributeName aName) const;
-  const AttributeMap* MaybeGetAttributeMap(AttributeName aName) const;
-  const nsTArray<float>& GetFloats(AttributeName aName) const;
+  bool operator==(const ConvolveMatrixAttributes& aOther) const {
+    return mKernelSize == aOther.mKernelSize &&
+           mKernelMatrix == aOther.mKernelMatrix &&
+           mDivisor == aOther.mDivisor &&
+           mBias == aOther.mBias &&
+           mTarget == aOther.mTarget &&
+           mEdgeMode == aOther.mEdgeMode &&
+           mKernelUnitLength == aOther.mKernelUnitLength &&
+           mPreserveAlpha == aOther.mPreserveAlpha;
+  }
+};
+
+struct CompositeAttributes {
+  uint32_t mOperator;
+  ImplicitlyCopyableFloatArray mCoefficients;
+
+  bool operator==(const CompositeAttributes& aOther) const {
+    return mOperator == aOther.mOperator &&
+           mCoefficients == aOther.mCoefficients;
+  }
+};
+
+enum class LightType {
+  None = 0,
+  Point,
+  Spot,
+  Distant,
+  Max,
+};
+
+const uint32_t kDistantLightAzimuthIndex = 0;
+const uint32_t kDistantLightElevationIndex = 1;
+const uint32_t kDistantLightNumAttributes = 2;
+
+const uint32_t kPointLightPositionXIndex = 0;
+const uint32_t kPointLightPositionYIndex = 1;
+const uint32_t kPointLightPositionZIndex = 2;
+const uint32_t kPointLightNumAttributes = 3;
 
-  uint32_t Count() const;
+const uint32_t kSpotLightPositionXIndex = 0;
+const uint32_t kSpotLightPositionYIndex = 1;
+const uint32_t kSpotLightPositionZIndex = 2;
+const uint32_t kSpotLightPointsAtXIndex = 3;
+const uint32_t kSpotLightPointsAtYIndex = 4;
+const uint32_t kSpotLightPointsAtZIndex = 5;
+const uint32_t kSpotLightFocusIndex = 6;
+const uint32_t kSpotLightLimitingConeAngleIndex = 7;
+const uint32_t kSpotLightNumAttributes = 8;
 
-  nsClassHashtable<nsUint32HashKey, FilterAttribute>::Iterator ConstIter() const;
+struct DiffuseLightingAttributes {
+  LightType mLightType;
+  ImplicitlyCopyableFloatArray mLightValues;
+  float mSurfaceScale;
+  Size mKernelUnitLength;
+  Color mColor;
+  float mLightingConstant;
+  float mSpecularExponent;
 
-  static AttributeType GetType(FilterAttribute* aAttribute);
+  bool operator==(const DiffuseLightingAttributes& aOther) const {
+    return mLightType == aOther.mLightType &&
+           mLightValues == aOther.mLightValues &&
+           mSurfaceScale == aOther.mSurfaceScale &&
+           mKernelUnitLength == aOther.mKernelUnitLength &&
+           mColor == aOther.mColor;
+  }
+};
+
+struct SpecularLightingAttributes : public DiffuseLightingAttributes {
+};
 
-private:
-  mutable nsClassHashtable<nsUint32HashKey, FilterAttribute>  mMap;
-};
+typedef Variant<
+  EmptyAttributes,
+  BlendAttributes,
+  MorphologyAttributes,
+  ColorMatrixAttributes,
+  FloodAttributes,
+  TileAttributes,
+  ComponentTransferAttributes,
+  OpacityAttributes,
+  ConvolveMatrixAttributes,
+  OffsetAttributes,
+  DisplacementMapAttributes,
+  TurbulenceAttributes,
+  CompositeAttributes,
+  MergeAttributes,
+  ImageAttributes,
+  GaussianBlurAttributes,
+  DropShadowAttributes,
+  DiffuseLightingAttributes,
+  SpecularLightingAttributes,
+  ToAlphaAttributes> PrimitiveAttributes;
 
 enum class ColorSpace {
   SRGB,
   LinearRGB,
   Max
 };
 
 enum class AlphaModel {
@@ -272,40 +447,16 @@ public:
   {
     return (uint8_t(mColorSpace) << 1) + uint8_t(mAlphaModel);
   }
 
   ColorSpace mColorSpace;
   AlphaModel mAlphaModel;
 };
 
-enum class PrimitiveType {
-  Empty = 0,
-  Blend,
-  Morphology,
-  ColorMatrix,
-  Flood,
-  Tile,
-  ComponentTransfer,
-  Opacity,
-  ConvolveMatrix,
-  Offset,
-  DisplacementMap,
-  Turbulence,
-  Composite,
-  Merge,
-  Image,
-  GaussianBlur,
-  DropShadow,
-  DiffuseLighting,
-  SpecularLighting,
-  ToAlpha,
-  Max
-};
-
 /**
  * A data structure to carry attributes for a given primitive that's part of a
  * filter. Will be serializable via IPDL, so it must not contain complex
  * functionality.
  * Used as part of a FilterDescription.
  */
 class FilterPrimitiveDescription final {
 public:
@@ -320,18 +471,18 @@ public:
   explicit FilterPrimitiveDescription(PrimitiveType aType);
   FilterPrimitiveDescription(FilterPrimitiveDescription&& aOther);
   FilterPrimitiveDescription& operator=(FilterPrimitiveDescription&& aOther);
   FilterPrimitiveDescription(const FilterPrimitiveDescription& aOther);
   FilterPrimitiveDescription& operator=(const FilterPrimitiveDescription& aOther);
 
   PrimitiveType Type() const { return mType; }
   void SetType(PrimitiveType aType) { mType = aType; }
-  const AttributeMap& Attributes() const { return mAttributes; }
-  AttributeMap& Attributes() { return mAttributes; }
+  const PrimitiveAttributes& Attributes() const { return mAttributes; }
+  PrimitiveAttributes& Attributes() { return mAttributes; }
 
   IntRect PrimitiveSubregion() const { return mFilterPrimitiveSubregion; }
   IntRect FilterSpaceBounds() const { return mFilterSpaceBounds; }
   bool IsTainted() const { return mIsTainted; }
 
   size_t NumberOfInputs() const { return mInputPrimitives.Length(); }
   int32_t InputPrimitiveIndex(size_t aInputIndex) const
   {
@@ -382,17 +533,17 @@ public:
   bool operator==(const FilterPrimitiveDescription& aOther) const;
   bool operator!=(const FilterPrimitiveDescription& aOther) const
   {
     return !(*this == aOther);
   }
 
 private:
   PrimitiveType mType;
-  AttributeMap mAttributes;
+  PrimitiveAttributes mAttributes;
   nsTArray<int32_t> mInputPrimitives;
   IntRect mFilterPrimitiveSubregion;
   IntRect mFilterSpaceBounds;
   nsTArray<ColorSpace> mInputColorSpaces;
   ColorSpace mOutputColorSpace;
   bool mIsTainted;
 };
 
--- a/layout/svg/nsCSSFilterInstance.cpp
+++ b/layout/svg/nsCSSFilterInstance.cpp
@@ -144,181 +144,201 @@ nsCSSFilterInstance::SetAttributesForBlu
 {
   const nsStyleCoord& radiusInFrameSpace = mFilter.GetFilterParameter();
   if (radiusInFrameSpace.GetUnit() != eStyleUnit_Coord) {
     MOZ_ASSERT_UNREACHABLE("unexpected unit");
     return NS_ERROR_FAILURE;
   }
 
   Size radiusInFilterSpace = BlurRadiusToFilterSpace(radiusInFrameSpace.GetCoordValue());
-  aDescr.Attributes().Set(eGaussianBlurStdDeviation, radiusInFilterSpace);
+  GaussianBlurAttributes atts;
+  atts.mStdDeviation = radiusInFilterSpace;
+  aDescr.Attributes() = AsVariant(atts);
   return NS_OK;
 }
 
 nsresult
 nsCSSFilterInstance::SetAttributesForBrightness(FilterPrimitiveDescription& aDescr)
 {
   const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
   float value = styleValue.GetFactorOrPercentValue();
+  float intercept = 0.0f;
+  ComponentTransferAttributes atts;
 
   // Set transfer functions for RGB.
-  AttributeMap brightnessAttrs;
-  brightnessAttrs.Set(eComponentTransferFunctionType,
-                      (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_LINEAR);
-  brightnessAttrs.Set(eComponentTransferFunctionSlope, value);
-  brightnessAttrs.Set(eComponentTransferFunctionIntercept, 0.0f);
-  aDescr.Attributes().Set(eComponentTransferFunctionRGB, std::move(brightnessAttrs));
+  atts.mTypes[kChannelROrRGB] =
+    (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_LINEAR;
+  atts.mTypes[kChannelG] =
+    (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN;
+  atts.mTypes[kChannelB] =
+    (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN;
+  float slopeIntercept[2];
+  slopeIntercept[kComponentTransferSlopeIndex] = value;
+  slopeIntercept[kComponentTransferInterceptIndex] = intercept;
+  atts.mValues[kChannelROrRGB].AppendElements(slopeIntercept, 2);
 
-  // Set identity transfer function for A.
-  AttributeMap identityAttrs;
-  identityAttrs.Set(eComponentTransferFunctionType,
-                    (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY);
-  aDescr.Attributes().Set(eComponentTransferFunctionA, std::move(identityAttrs));
+  atts.mTypes[kChannelA] = 
+    (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY;
 
+  aDescr.Attributes() = AsVariant(std::move(atts));
   return NS_OK;
 }
 
 nsresult
 nsCSSFilterInstance::SetAttributesForContrast(FilterPrimitiveDescription& aDescr)
 {
   const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
   float value = styleValue.GetFactorOrPercentValue();
   float intercept = -(0.5 * value) + 0.5;
+  ComponentTransferAttributes atts;
 
   // Set transfer functions for RGB.
-  AttributeMap contrastAttrs;
-  contrastAttrs.Set(eComponentTransferFunctionType,
-                    (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_LINEAR);
-  contrastAttrs.Set(eComponentTransferFunctionSlope, value);
-  contrastAttrs.Set(eComponentTransferFunctionIntercept, intercept);
-  aDescr.Attributes().Set(eComponentTransferFunctionRGB, std::move(contrastAttrs));
+  atts.mTypes[kChannelROrRGB] =
+    (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_LINEAR;
+  atts.mTypes[kChannelG] =
+    (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN;
+  atts.mTypes[kChannelB] =
+    (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN;
+  float slopeIntercept[2];
+  slopeIntercept[kComponentTransferSlopeIndex] = value;
+  slopeIntercept[kComponentTransferInterceptIndex] = intercept;
+  atts.mValues[kChannelROrRGB].AppendElements(slopeIntercept, 2);
 
-  // Set identity transfer function for A.
-  AttributeMap identityAttrs;
-  identityAttrs.Set(eComponentTransferFunctionType,
-                    (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY);
-  aDescr.Attributes().Set(eComponentTransferFunctionA, std::move(identityAttrs));
+  atts.mTypes[kChannelA] = 
+    (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY;
 
+  aDescr.Attributes() = AsVariant(std::move(atts));
   return NS_OK;
 }
 
 nsresult
 nsCSSFilterInstance::SetAttributesForDropShadow(FilterPrimitiveDescription& aDescr)
 {
   nsCSSShadowArray* shadows = mFilter.GetDropShadow();
   if (!shadows || shadows->Length() != 1) {
     MOZ_ASSERT_UNREACHABLE("Exactly one drop shadow should have been parsed.");
     return NS_ERROR_FAILURE;
   }
 
+  DropShadowAttributes atts;
   nsCSSShadowItem* shadow = shadows->ShadowAt(0);
 
   // Set drop shadow blur radius.
   Size radiusInFilterSpace = BlurRadiusToFilterSpace(shadow->mRadius);
-  aDescr.Attributes().Set(eDropShadowStdDeviation, radiusInFilterSpace);
+  atts.mStdDeviation = radiusInFilterSpace;
 
   // Set offset.
   IntPoint offsetInFilterSpace = OffsetToFilterSpace(shadow->mXOffset, shadow->mYOffset);
-  aDescr.Attributes().Set(eDropShadowOffset, offsetInFilterSpace);
+  atts.mOffset = offsetInFilterSpace;
 
   // Set color. If unspecified, use the CSS color property.
   nscolor shadowColor = shadow->mColor.CalcColor(mShadowFallbackColor);
-  aDescr.Attributes().Set(eDropShadowColor, ToAttributeColor(shadowColor));
+  atts.mColor = ToAttributeColor(shadowColor);
 
+  aDescr.Attributes() = AsVariant(std::move(atts));
   return NS_OK;
 }
 
 nsresult
 nsCSSFilterInstance::SetAttributesForGrayscale(FilterPrimitiveDescription& aDescr)
 {
+  ColorMatrixAttributes atts;
   // Set color matrix type.
-  aDescr.Attributes().Set(eColorMatrixType, (uint32_t)SVG_FECOLORMATRIX_TYPE_SATURATE);
+  atts.mType = (uint32_t)SVG_FECOLORMATRIX_TYPE_SATURATE;
 
   // Set color matrix values.
   const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
   float value = 1 - ClampFactor(styleValue.GetFactorOrPercentValue());
-  aDescr.Attributes().Set(eColorMatrixValues, &value, 1);
+  atts.mValues.AppendElements(&value, 1);
 
+  aDescr.Attributes() = AsVariant(std::move(atts));
   return NS_OK;
 }
 
 nsresult
 nsCSSFilterInstance::SetAttributesForHueRotate(FilterPrimitiveDescription& aDescr)
 {
+  ColorMatrixAttributes atts;
   // Set color matrix type.
-  aDescr.Attributes().Set(eColorMatrixType, (uint32_t)SVG_FECOLORMATRIX_TYPE_HUE_ROTATE);
+  atts.mType = (uint32_t)SVG_FECOLORMATRIX_TYPE_HUE_ROTATE;
 
   // Set color matrix values.
   const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
   float value = styleValue.GetAngleValueInDegrees();
-  aDescr.Attributes().Set(eColorMatrixValues, &value, 1);
+  atts.mValues.AppendElements(&value, 1);
 
+  aDescr.Attributes() = AsVariant(std::move(atts));
   return NS_OK;
 }
 
 nsresult
 nsCSSFilterInstance::SetAttributesForInvert(FilterPrimitiveDescription& aDescr)
 {
+  ComponentTransferAttributes atts;
   const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
   float value = ClampFactor(styleValue.GetFactorOrPercentValue());
 
   // Set transfer functions for RGB.
-  AttributeMap invertAttrs;
   float invertTableValues[2];
   invertTableValues[0] = value;
   invertTableValues[1] = 1 - value;
-  invertAttrs.Set(eComponentTransferFunctionType,
-                  (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_TABLE);
-  invertAttrs.Set(eComponentTransferFunctionTableValues, invertTableValues, 2);
-  aDescr.Attributes().Set(eComponentTransferFunctionRGB, std::move(invertAttrs));
 
-  // Set identity transfer function for A.
-  AttributeMap identityAttrs;
-  identityAttrs.Set(eComponentTransferFunctionType,
-                    (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY);
-  aDescr.Attributes().Set(eComponentTransferFunctionA, std::move(identityAttrs));
+  // Set transfer functions for RGB.
+  atts.mTypes[kChannelROrRGB] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_TABLE;
+  atts.mTypes[kChannelG] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN;
+  atts.mTypes[kChannelB] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN;
+  atts.mValues[kChannelROrRGB].AppendElements(invertTableValues, 2);
 
+  atts.mTypes[kChannelA] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY;
+
+  aDescr.Attributes() = AsVariant(std::move(atts));
   return NS_OK;
 }
 
 nsresult
 nsCSSFilterInstance::SetAttributesForOpacity(FilterPrimitiveDescription& aDescr)
 {
+  OpacityAttributes atts;
   const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
   float value = ClampFactor(styleValue.GetFactorOrPercentValue());
 
-  aDescr.Attributes().Set(eOpacityOpacity, value);
+  atts.mOpacity = value;
+  aDescr.Attributes() = AsVariant(std::move(atts));
   return NS_OK;
 }
 
 nsresult
 nsCSSFilterInstance::SetAttributesForSaturate(FilterPrimitiveDescription& aDescr)
 {
+  ColorMatrixAttributes atts;
   // Set color matrix type.
-  aDescr.Attributes().Set(eColorMatrixType, (uint32_t)SVG_FECOLORMATRIX_TYPE_SATURATE);
+  atts.mType = (uint32_t)SVG_FECOLORMATRIX_TYPE_SATURATE;
 
   // Set color matrix values.
   const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
   float value = styleValue.GetFactorOrPercentValue();
-  aDescr.Attributes().Set(eColorMatrixValues, &value, 1);
+  atts.mValues.AppendElements(&value, 1);
 
+  aDescr.Attributes() = AsVariant(std::move(atts));
   return NS_OK;
 }
 
 nsresult
 nsCSSFilterInstance::SetAttributesForSepia(FilterPrimitiveDescription& aDescr)
 {
+  ColorMatrixAttributes atts;
   // Set color matrix type.
-  aDescr.Attributes().Set(eColorMatrixType, (uint32_t)SVG_FECOLORMATRIX_TYPE_SEPIA);
+  atts.mType = (uint32_t)SVG_FECOLORMATRIX_TYPE_SEPIA;
 
   // Set color matrix values.
   const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
   float value = ClampFactor(styleValue.GetFactorOrPercentValue());
-  aDescr.Attributes().Set(eColorMatrixValues, &value, 1);
+  atts.mValues.AppendElements(&value, 1);
 
+  aDescr.Attributes() = AsVariant(std::move(atts));
   return NS_OK;
 }
 
 Size
 nsCSSFilterInstance::BlurRadiusToFilterSpace(nscoord aRadiusInFrameSpace)
 {
   float radiusInFrameSpaceInCSSPx =
     nsPresContext::AppUnitsToFloatCSSPixels(aRadiusInFrameSpace);
--- a/layout/svg/nsFilterInstance.cpp
+++ b/layout/svg/nsFilterInstance.cpp
@@ -494,17 +494,16 @@ nsFilterInstance::BuildSourceImage(DrawT
   mSourceGraphic.mSourceSurface = offscreenDT->Snapshot();
   mSourceGraphic.mSurfaceRect = neededRect;
 }
 
 void
 nsFilterInstance::Render(gfxContext* aCtx, imgDrawingParams& aImgParams, float aOpacity)
 {
   MOZ_ASSERT(mTargetFrame, "Need a frame for rendering");
-  MOZ_ASSERT(mInitialized);
 
   if (mFilterDescription.mPrimitives.IsEmpty()) {
     // An filter without any primitive. Treat it as success and paint nothing.
     return;
   }
 
   nsIntRect filterRect =
     mPostFilterDirtyRegion.GetBounds().Intersect(OutputFilterSpaceBounds());
@@ -526,33 +525,29 @@ nsFilterInstance::Render(gfxContext* aCt
     mFillPaint.mSourceSurface, mFillPaint.mSurfaceRect,
     mStrokePaint.mSourceSurface, mStrokePaint.mSurfaceRect,
     mInputImages, Point(0, 0), DrawOptions(aOpacity));
 }
 
 nsRegion
 nsFilterInstance::ComputePostFilterDirtyRegion()
 {
-  MOZ_ASSERT(mInitialized);
-
   if (mPreFilterDirtyRegion.IsEmpty() || mFilterDescription.mPrimitives.IsEmpty()) {
     return nsRegion();
   }
 
   nsIntRegion resultChangeRegion =
     FilterSupport::ComputeResultChangeRegion(mFilterDescription,
       mPreFilterDirtyRegion, nsIntRegion(), nsIntRegion());
   return FilterSpaceToFrameSpace(resultChangeRegion);
 }
 
 nsRect
 nsFilterInstance::ComputePostFilterExtents()
 {
-  MOZ_ASSERT(mInitialized);
-
   if (mFilterDescription.mPrimitives.IsEmpty()) {
     return nsRect();
   }
 
   nsIntRegion postFilterExtents =
     FilterSupport::ComputePostFilterExtents(mFilterDescription, mTargetBounds);
   return FilterSpaceToFrameSpace(postFilterExtents.GetBounds());
 }
@@ -562,18 +557,16 @@ nsFilterInstance::ComputeSourceNeededRec
 {
   ComputeNeededBoxes();
   return FilterSpaceToFrameSpace(mSourceGraphic.mNeededBounds);
 }
 
 nsIntRect
 nsFilterInstance::OutputFilterSpaceBounds() const
 {
-  MOZ_ASSERT(mInitialized);
-
   uint32_t numPrimitives = mFilterDescription.mPrimitives.Length();
   if (numPrimitives <= 0)
     return nsIntRect();
 
   return mFilterDescription.mPrimitives[numPrimitives - 1].PrimitiveSubregion();
 }
 
 nsIntRect