[SVGDom] Initial linear gradient support

Kind of a big change, to connect several new bits into something useful:

  * ID tracking & lookup
  * new asPaint() node virtual to support shader (and in the future filter) based paint servers
  * <defs>, <linearGradient> and <stop> element support
  * 'href', 'offset', 'stop-color', 'stop-opacity' attribute support
  * IRI/FuncIRI and rgb(...) parsing

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2327233003

Review-Url: https://codereview.chromium.org/2327233003
diff --git a/BUILD.gn b/BUILD.gn
index 20f8d84..7b506e4 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -731,6 +731,7 @@
       "experimental/svg/model/SkSVGDOM.cpp",
       "experimental/svg/model/SkSVGEllipse.cpp",
       "experimental/svg/model/SkSVGLine.cpp",
+      "experimental/svg/model/SkSVGLinearGradient.cpp",
       "experimental/svg/model/SkSVGNode.cpp",
       "experimental/svg/model/SkSVGPath.cpp",
       "experimental/svg/model/SkSVGPoly.cpp",
@@ -738,6 +739,7 @@
       "experimental/svg/model/SkSVGRenderContext.cpp",
       "experimental/svg/model/SkSVGSVG.cpp",
       "experimental/svg/model/SkSVGShape.cpp",
+      "experimental/svg/model/SkSVGStop.cpp",
       "experimental/svg/model/SkSVGTransformableNode.cpp",
       "experimental/svg/model/SkSVGValue.cpp",
     ]
diff --git a/experimental/svg/model/SkSVGAttribute.h b/experimental/svg/model/SkSVGAttribute.h
index 196575d..ce06b1e 100644
--- a/experimental/svg/model/SkSVGAttribute.h
+++ b/experimental/svg/model/SkSVGAttribute.h
@@ -20,11 +20,15 @@
     kFill,
     kFillOpacity,
     kHeight,
+    kHref,
+    kOffset,
     kOpacity,
     kPoints,
     kR,  // <circle>: radius
     kRx, // <ellipse>,<rect>: horizontal (corner) radius
     kRy, // <ellipse>,<rect>: vertical (corner) radius
+    kStopColor,
+    kStopOpacity,
     kStroke,
     kStrokeOpacity,
     kStrokeLineCap,
diff --git a/experimental/svg/model/SkSVGAttributeParser.cpp b/experimental/svg/model/SkSVGAttributeParser.cpp
index f8bfa20..9d2d6b8 100644
--- a/experimental/svg/model/SkSVGAttributeParser.cpp
+++ b/experimental/svg/model/SkSVGAttributeParser.cpp
@@ -113,6 +113,7 @@
     return false;
 }
 
+// https://www.w3.org/TR/SVG/types.html#DataTypeColor
 bool SkSVGAttributeParser::parseNamedColorToken(SkColor* c) {
     if (const char* next = SkParse::FindNamedColor(fCurPos, strlen(fCurPos), c)) {
         fCurPos = next;
@@ -148,7 +149,38 @@
     return true;
 }
 
-// https://www.w3.org/TR/SVG/types.html#DataTypeColor
+bool SkSVGAttributeParser::parseColorComponentToken(int32_t* c) {
+    fCurPos = SkParse::FindS32(fCurPos, c);
+    if (!fCurPos) {
+        return false;
+    }
+
+    if (*fCurPos == '%') {
+        *c = SkScalarRoundToInt(*c * 255.0f / 100);
+        fCurPos++;
+    }
+
+    return true;
+}
+
+bool SkSVGAttributeParser::parseRGBColorToken(SkColor* c) {
+    return this->parseParenthesized("rgb", [this](SkColor* c) -> bool {
+        int32_t r, g, b;
+        if (this->parseColorComponentToken(&r) &&
+            this->parseSepToken() &&
+            this->parseColorComponentToken(&g) &&
+            this->parseSepToken() &&
+            this->parseColorComponentToken(&b)) {
+
+            *c = SkColorSetRGB(static_cast<uint8_t>(r),
+                               static_cast<uint8_t>(g),
+                               static_cast<uint8_t>(b));
+            return true;
+        }
+        return false;
+    }, c);
+}
+
 bool SkSVGAttributeParser::parseColor(SkSVGColorType* color) {
     SkColor c;
 
@@ -157,7 +189,9 @@
 
     // TODO: rgb(...)
     bool parsedValue = false;
-    if (this->parseHexColorToken(&c) || this->parseNamedColorToken(&c)) {
+    if (this->parseHexColorToken(&c)
+        || this->parseNamedColorToken(&c)
+        || this->parseRGBColorToken(&c)) {
         *color = SkSVGColorType(c);
         parsedValue = true;
 
@@ -168,6 +202,31 @@
     return parsedValue && this->parseEOSToken();
 }
 
+// https://www.w3.org/TR/SVG/linking.html#IRIReference
+bool SkSVGAttributeParser::parseIRI(SkSVGStringType* iri) {
+    // consume preceding whitespace
+    this->parseWSToken();
+
+    // we only support local fragments
+    if (!this->parseExpectedStringToken("#")) {
+        return false;
+    }
+    const auto* start = fCurPos;
+    this->advanceWhile([](char c) -> bool { return !is_eos(c) && c != ')'; });
+    if (start == fCurPos) {
+        return false;
+    }
+    *iri = SkString(start, fCurPos - start);
+    return true;
+}
+
+// https://www.w3.org/TR/SVG/types.html#DataTypeFuncIRI
+bool SkSVGAttributeParser::parseFuncIRI(SkSVGStringType* iri) {
+    return this->parseParenthesized("url", [this](SkSVGStringType* iri) -> bool {
+        return this->parseIRI(iri);
+    }, iri);
+}
+
 // https://www.w3.org/TR/SVG/types.html#DataTypeNumber
 bool SkSVGAttributeParser::parseNumber(SkSVGNumberType* number) {
     // consume WS
@@ -363,6 +422,7 @@
 // https://www.w3.org/TR/SVG/painting.html#SpecifyingPaint
 bool SkSVGAttributeParser::parsePaint(SkSVGPaint* paint) {
     SkSVGColorType c;
+    SkSVGStringType iri;
     bool parsedValue = false;
     if (this->parseColor(&c)) {
         *paint = SkSVGPaint(c);
@@ -376,6 +436,9 @@
     } else if (this->parseExpectedStringToken("inherit")) {
         *paint = SkSVGPaint(SkSVGPaint::Type::kInherit);
         parsedValue = true;
+    } else if (this->parseFuncIRI(&iri)) {
+        *paint = SkSVGPaint(iri.value());
+        parsedValue = true;
     }
     return parsedValue && this->parseEOSToken();
 }
diff --git a/experimental/svg/model/SkSVGAttributeParser.h b/experimental/svg/model/SkSVGAttributeParser.h
index d1ead39..c1700a8 100644
--- a/experimental/svg/model/SkSVGAttributeParser.h
+++ b/experimental/svg/model/SkSVGAttributeParser.h
@@ -23,6 +23,7 @@
     bool parseLineCap(SkSVGLineCap*);
     bool parseLineJoin(SkSVGLineJoin*);
     bool parsePoints(SkSVGPointsType*);
+    bool parseIRI(SkSVGStringType*);
 
 private:
     // Stack-only
@@ -41,6 +42,9 @@
     bool parseLengthUnitToken(SkSVGLength::Unit*);
     bool parseNamedColorToken(SkColor*);
     bool parseHexColorToken(SkColor*);
+    bool parseColorComponentToken(int32_t*);
+    bool parseRGBColorToken(SkColor*);
+    bool parseFuncIRI(SkSVGStringType*);
 
     // Transform helpers
     bool parseMatrixToken(SkMatrix*);
diff --git a/experimental/svg/model/SkSVGContainer.h b/experimental/svg/model/SkSVGContainer.h
index 9a3fd62..73c7c18 100644
--- a/experimental/svg/model/SkSVGContainer.h
+++ b/experimental/svg/model/SkSVGContainer.h
@@ -18,13 +18,14 @@
     void appendChild(sk_sp<SkSVGNode>) override;
 
 protected:
-    SkSVGContainer(SkSVGTag);
+    explicit SkSVGContainer(SkSVGTag);
 
     void onRender(const SkSVGRenderContext&) const override;
 
-private:
+    // TODO: add some sort of child iterator, and hide the container.
     SkSTArray<1, sk_sp<SkSVGNode>, true> fChildren;
 
+private:
     typedef SkSVGTransformableNode INHERITED;
 };
 
diff --git a/experimental/svg/model/SkSVGDOM.cpp b/experimental/svg/model/SkSVGDOM.cpp
index c123bc6..d6e4f4c 100644
--- a/experimental/svg/model/SkSVGDOM.cpp
+++ b/experimental/svg/model/SkSVGDOM.cpp
@@ -11,15 +11,18 @@
 #include "SkString.h"
 #include "SkSVGAttributeParser.h"
 #include "SkSVGCircle.h"
+#include "SkSVGDefs.h"
 #include "SkSVGDOM.h"
 #include "SkSVGEllipse.h"
 #include "SkSVGG.h"
 #include "SkSVGLine.h"
+#include "SkSVGLinearGradient.h"
 #include "SkSVGNode.h"
 #include "SkSVGPath.h"
 #include "SkSVGPoly.h"
 #include "SkSVGRect.h"
 #include "SkSVGRenderContext.h"
+#include "SkSVGStop.h"
 #include "SkSVGSVG.h"
 #include "SkSVGTypes.h"
 #include "SkSVGValue.h"
@@ -32,17 +35,37 @@
     SkSVGPaint paint;
     SkSVGAttributeParser parser(stringValue);
     if (!parser.parsePaint(&paint)) {
-        // Until we have paint server support, failing here will cause default/all-black rendering.
-        // It's better to just not draw for now.
-        paint = SkSVGPaint(SkSVGPaint::Type::kNone);
-
-        // return false;
+        return false;
     }
 
     node->setAttribute(attr, SkSVGPaintValue(paint));
     return true;
 }
 
+bool SetColorAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
+                       const char* stringValue) {
+    SkSVGColorType color;
+    SkSVGAttributeParser parser(stringValue);
+    if (!parser.parseColor(&color)) {
+        return false;
+    }
+
+    node->setAttribute(attr, SkSVGColorValue(color));
+    return true;
+}
+
+bool SetIRIAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
+                      const char* stringValue) {
+    SkSVGStringType iri;
+    SkSVGAttributeParser parser(stringValue);
+    if (!parser.parseIRI(&iri)) {
+        return false;
+    }
+
+    node->setAttribute(attr, SkSVGStringValue(iri));
+    return true;
+}
+
 bool SetPathDataAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
                           const char* stringValue) {
     SkPath path;
@@ -222,11 +245,14 @@
     { "fill"           , { SkSVGAttribute::kFill          , SetPaintAttribute     }},
     { "fill-opacity"   , { SkSVGAttribute::kFillOpacity   , SetNumberAttribute    }},
     { "height"         , { SkSVGAttribute::kHeight        , SetLengthAttribute    }},
+    { "offset"         , { SkSVGAttribute::kOffset        , SetLengthAttribute    }},
     { "opacity"        , { SkSVGAttribute::kOpacity       , SetNumberAttribute    }},
     { "points"         , { SkSVGAttribute::kPoints        , SetPointsAttribute    }},
     { "r"              , { SkSVGAttribute::kR             , SetLengthAttribute    }},
     { "rx"             , { SkSVGAttribute::kRx            , SetLengthAttribute    }},
     { "ry"             , { SkSVGAttribute::kRy            , SetLengthAttribute    }},
+    { "stop-color"     , { SkSVGAttribute::kStopColor     , SetColorAttribute     }},
+    { "stop-opacity"   , { SkSVGAttribute::kStopOpacity   , SetNumberAttribute    }},
     { "stroke"         , { SkSVGAttribute::kStroke        , SetPaintAttribute     }},
     { "stroke-linecap" , { SkSVGAttribute::kStrokeLineCap , SetLineCapAttribute   }},
     { "stroke-linejoin", { SkSVGAttribute::kStrokeLineJoin, SetLineJoinAttribute  }},
@@ -239,29 +265,34 @@
     { "x"              , { SkSVGAttribute::kX             , SetLengthAttribute    }},
     { "x1"             , { SkSVGAttribute::kX1            , SetLengthAttribute    }},
     { "x2"             , { SkSVGAttribute::kX2            , SetLengthAttribute    }},
+    { "xlink:href"     , { SkSVGAttribute::kHref          , SetIRIAttribute       }},
     { "y"              , { SkSVGAttribute::kY             , SetLengthAttribute    }},
     { "y1"             , { SkSVGAttribute::kY1            , SetLengthAttribute    }},
     { "y2"             , { SkSVGAttribute::kY2            , SetLengthAttribute    }},
 };
 
 SortedDictionaryEntry<sk_sp<SkSVGNode>(*)()> gTagFactories[] = {
-    { "circle"  , []() -> sk_sp<SkSVGNode> { return SkSVGCircle::Make();       }},
-    { "ellipse" , []() -> sk_sp<SkSVGNode> { return SkSVGEllipse::Make();      }},
-    { "g"       , []() -> sk_sp<SkSVGNode> { return SkSVGG::Make();            }},
-    { "line"    , []() -> sk_sp<SkSVGNode> { return SkSVGLine::Make();         }},
-    { "path"    , []() -> sk_sp<SkSVGNode> { return SkSVGPath::Make();         }},
-    { "polygon" , []() -> sk_sp<SkSVGNode> { return SkSVGPoly::MakePolygon();  }},
-    { "polyline", []() -> sk_sp<SkSVGNode> { return SkSVGPoly::MakePolyline(); }},
-    { "rect"    , []() -> sk_sp<SkSVGNode> { return SkSVGRect::Make();         }},
-    { "svg"     , []() -> sk_sp<SkSVGNode> { return SkSVGSVG::Make();          }},
+    { "circle"        , []() -> sk_sp<SkSVGNode> { return SkSVGCircle::Make();         }},
+    { "defs"          , []() -> sk_sp<SkSVGNode> { return SkSVGDefs::Make();           }},
+    { "ellipse"       , []() -> sk_sp<SkSVGNode> { return SkSVGEllipse::Make();        }},
+    { "g"             , []() -> sk_sp<SkSVGNode> { return SkSVGG::Make();              }},
+    { "line"          , []() -> sk_sp<SkSVGNode> { return SkSVGLine::Make();           }},
+    { "linearGradient", []() -> sk_sp<SkSVGNode> { return SkSVGLinearGradient::Make(); }},
+    { "path"          , []() -> sk_sp<SkSVGNode> { return SkSVGPath::Make();           }},
+    { "polygon"       , []() -> sk_sp<SkSVGNode> { return SkSVGPoly::MakePolygon();    }},
+    { "polyline"      , []() -> sk_sp<SkSVGNode> { return SkSVGPoly::MakePolyline();   }},
+    { "rect"          , []() -> sk_sp<SkSVGNode> { return SkSVGRect::Make();           }},
+    { "stop"          , []() -> sk_sp<SkSVGNode> { return SkSVGStop::Make();           }},
+    { "svg"           , []() -> sk_sp<SkSVGNode> { return SkSVGSVG::Make();            }},
 };
 
 struct ConstructionContext {
-    ConstructionContext() : fParent(nullptr) { }
+    ConstructionContext(SkSVGIDMapper* mapper) : fParent(nullptr), fIDMapper(mapper) {}
     ConstructionContext(const ConstructionContext& other, const sk_sp<SkSVGNode>& newParent)
-        : fParent(newParent.get()) { }
+        : fParent(newParent.get()), fIDMapper(other.fIDMapper) {}
 
     const SkSVGNode* fParent;
+    SkSVGIDMapper*   fIDMapper;
 };
 
 void set_string_attribute(const sk_sp<SkSVGNode>& node, const char* name, const char* value) {
@@ -285,10 +316,15 @@
 }
 
 void parse_node_attributes(const SkDOM& xmlDom, const SkDOM::Node* xmlNode,
-                           const sk_sp<SkSVGNode>& svgNode) {
+                           const sk_sp<SkSVGNode>& svgNode, SkSVGIDMapper* mapper) {
     const char* name, *value;
     SkDOM::AttrIter attrIter(xmlDom, xmlNode);
     while ((name = attrIter.next(&value))) {
+        // We're handling id attributes out of band for now.
+        if (!strcmp(name, "id")) {
+            mapper->set(SkString(value), svgNode);
+            continue;
+        }
         set_string_attribute(svgNode, name, value);
     }
 }
@@ -318,7 +354,7 @@
 
     SkASSERT(SkTo<size_t>(tagIndex) < SK_ARRAY_COUNT(gTagFactories));
     sk_sp<SkSVGNode> node = gTagFactories[tagIndex].fValue();
-    parse_node_attributes(dom, xmlNode, node);
+    parse_node_attributes(dom, xmlNode, node, ctx.fIDMapper);
 
     ConstructionContext localCtx(ctx, node);
     for (auto* child = dom.getFirstChild(xmlNode, nullptr); child;
@@ -341,7 +377,7 @@
 sk_sp<SkSVGDOM> SkSVGDOM::MakeFromDOM(const SkDOM& xmlDom, const SkSize& containerSize) {
     sk_sp<SkSVGDOM> dom = sk_make_sp<SkSVGDOM>(containerSize);
 
-    ConstructionContext ctx;
+    ConstructionContext ctx(&dom->fIDMapper);
     dom->fRoot = construct_svg_node(xmlDom, ctx, xmlDom.getRootNode());
 
     return dom;
@@ -359,6 +395,7 @@
 void SkSVGDOM::render(SkCanvas* canvas) const {
     if (fRoot) {
         SkSVGRenderContext ctx(canvas,
+                               fIDMapper,
                                SkSVGLengthContext(fContainerSize),
                                SkSVGPresentationContext());
         fRoot->render(ctx);
diff --git a/experimental/svg/model/SkSVGDOM.h b/experimental/svg/model/SkSVGDOM.h
index 98f8462..9c59b7c 100644
--- a/experimental/svg/model/SkSVGDOM.h
+++ b/experimental/svg/model/SkSVGDOM.h
@@ -10,6 +10,7 @@
 
 #include "SkRefCnt.h"
 #include "SkSize.h"
+#include "SkSVGIDMapper.h"
 #include "SkTemplates.h"
 
 class SkCanvas;
@@ -33,6 +34,7 @@
 private:
     SkSize           fContainerSize;
     sk_sp<SkSVGNode> fRoot;
+    SkSVGIDMapper    fIDMapper;
 
     typedef SkRefCnt INHERITED;
 };
diff --git a/experimental/svg/model/SkSVGDefs.h b/experimental/svg/model/SkSVGDefs.h
new file mode 100644
index 0000000..3064fe2
--- /dev/null
+++ b/experimental/svg/model/SkSVGDefs.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkSVGDefs_DEFINED
+#define SkSVGDefs_DEFINED
+
+#include "SkSVGHiddenContainer.h"
+
+class SkSVGDefs : public SkSVGHiddenContainer {
+public:
+    virtual ~SkSVGDefs() = default;
+    static sk_sp<SkSVGDefs> Make() { return sk_sp<SkSVGDefs>(new SkSVGDefs()); }
+
+private:
+    SkSVGDefs() : INHERITED(SkSVGTag::kDefs) {}
+
+    typedef SkSVGHiddenContainer INHERITED;
+};
+
+#endif // SkSVGDefs_DEFINED
diff --git a/experimental/svg/model/SkSVGHiddenContainer.h b/experimental/svg/model/SkSVGHiddenContainer.h
new file mode 100644
index 0000000..e224e55
--- /dev/null
+++ b/experimental/svg/model/SkSVGHiddenContainer.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkSVGHiddenContainer_DEFINED
+#define SkSVGHiddenContainer_DEFINED
+
+#include "SkSVGContainer.h"
+
+class SkSVGHiddenContainer : public SkSVGContainer {
+public:
+    virtual ~SkSVGHiddenContainer() = default;
+
+protected:
+    explicit SkSVGHiddenContainer(SkSVGTag t) : INHERITED(t) {}
+
+    void onRender(const SkSVGRenderContext&) const final {}
+
+private:
+    typedef SkSVGContainer INHERITED;
+};
+
+#endif // SkSVGHiddenContainer_DEFINED
diff --git a/experimental/svg/model/SkSVGIDMapper.h b/experimental/svg/model/SkSVGIDMapper.h
new file mode 100644
index 0000000..d88c1f1
--- /dev/null
+++ b/experimental/svg/model/SkSVGIDMapper.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkSVGIDMapper_DEFINED
+#define SkSVGIDMapper_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkTHash.h"
+
+class SkString;
+class SkSVGNode;
+
+using SkSVGIDMapper = SkTHashMap<SkString, sk_sp<SkSVGNode>>;
+
+#endif // SkSVGIDMapper_DEFINED
diff --git a/experimental/svg/model/SkSVGLinearGradient.cpp b/experimental/svg/model/SkSVGLinearGradient.cpp
new file mode 100644
index 0000000..20c27f5
--- /dev/null
+++ b/experimental/svg/model/SkSVGLinearGradient.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkGradientShader.h"
+#include "SkSVGLinearGradient.h"
+#include "SkSVGRenderContext.h"
+#include "SkSVGStop.h"
+#include "SkSVGValue.h"
+
+SkSVGLinearGradient::SkSVGLinearGradient() : INHERITED(SkSVGTag::kLinearGradient) {}
+
+void SkSVGLinearGradient::setHref(const SkSVGStringType& href) {
+    fHref = std::move(href);
+}
+
+void SkSVGLinearGradient::setX1(const SkSVGLength& x1) {
+    fX1 = x1;
+}
+
+void SkSVGLinearGradient::setY1(const SkSVGLength& y1) {
+    fY1 = y1;
+}
+
+void SkSVGLinearGradient::setX2(const SkSVGLength& x2) {
+    fX2 = x2;
+}
+
+void SkSVGLinearGradient::setY2(const SkSVGLength& y2) {
+    fY2 = y2;
+}
+
+void SkSVGLinearGradient::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) {
+    switch (attr) {
+    case SkSVGAttribute::kHref:
+        if (const auto* href = v.as<SkSVGStringValue>()) {
+            this->setHref(*href);
+        }
+        break;
+    case SkSVGAttribute::kX1:
+        if (const auto* x1 = v.as<SkSVGLengthValue>()) {
+            this->setX1(*x1);
+        }
+        break;
+    case SkSVGAttribute::kY1:
+        if (const auto* y1 = v.as<SkSVGLengthValue>()) {
+            this->setY1(*y1);
+        }
+        break;
+    case SkSVGAttribute::kX2:
+        if (const auto* x2 = v.as<SkSVGLengthValue>()) {
+            this->setX2(*x2);
+        }
+        break;
+    case SkSVGAttribute::kY2:
+        if (const auto* y2 = v.as<SkSVGLengthValue>()) {
+            this->setY2(*y2);
+        }
+        break;
+    default:
+        this->INHERITED::onSetAttribute(attr, v);
+    }
+}
+
+// https://www.w3.org/TR/SVG/pservers.html#LinearGradientElementHrefAttribute
+void SkSVGLinearGradient::collectColorStops(const SkSVGRenderContext& ctx,
+                                            SkSTArray<2, SkScalar, true>* pos,
+                                            SkSTArray<2, SkColor, true>* colors) const {
+    // Used to resolve percentage offsets.
+    const SkSVGLengthContext ltx(SkSize::Make(1, 1));
+
+    for (const auto& child : fChildren) {
+        if (child->tag() != SkSVGTag::kStop) {
+            continue;
+        }
+
+        const auto& stop = static_cast<const SkSVGStop&>(*child);
+        colors->push_back(SkColorSetA(stop.stopColor(),
+                                      SkScalarRoundToInt(stop.stopOpacity() * 255)));
+        pos->push_back(SkTPin(ltx.resolve(stop.offset(), SkSVGLengthContext::LengthType::kOther),
+                              0.f, 1.f));
+    }
+
+    SkASSERT(colors->count() == pos->count());
+
+    if (pos->empty() && !fHref.value().isEmpty()) {
+        const auto* ref = ctx.findNodeById(fHref);
+        if (ref && ref->tag() == SkSVGTag::kLinearGradient) {
+            static_cast<const SkSVGLinearGradient*>(ref)->collectColorStops(ctx, pos, colors);
+        }
+    }
+}
+
+bool SkSVGLinearGradient::onAsPaint(const SkSVGRenderContext& ctx, SkPaint* paint) const {
+    const auto& lctx = ctx.lengthContext();
+    const auto x1 = lctx.resolve(fX1, SkSVGLengthContext::LengthType::kHorizontal);
+    const auto y1 = lctx.resolve(fY1, SkSVGLengthContext::LengthType::kVertical);
+    const auto x2 = lctx.resolve(fX2, SkSVGLengthContext::LengthType::kHorizontal);
+    const auto y2 = lctx.resolve(fY2, SkSVGLengthContext::LengthType::kVertical);
+
+    const SkPoint pts[2] = { {x1, y1}, {x2, y2}};
+    SkSTArray<2, SkColor , true> colors;
+    SkSTArray<2, SkScalar, true> pos;
+
+    this->collectColorStops(ctx, &pos, &colors);
+    // TODO:
+    //       * stop (lazy?) sorting
+    //       * href loop detection
+    //       * href attribute inheritance (not just color stops)
+    //       * spreadMethods support
+    //       * objectBoundingBox units support
+
+    paint->setShader(SkGradientShader::MakeLinear(pts, colors.begin(), pos.begin(), colors.count(),
+                                                  SkShader::kClamp_TileMode));
+    return true;
+}
diff --git a/experimental/svg/model/SkSVGLinearGradient.h b/experimental/svg/model/SkSVGLinearGradient.h
new file mode 100644
index 0000000..1a2e332
--- /dev/null
+++ b/experimental/svg/model/SkSVGLinearGradient.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkSVGLinearGradient_DEFINED
+#define SkSVGLinearGradient_DEFINED
+
+#include "SkSVGHiddenContainer.h"
+#include "SkSVGTypes.h"
+
+class SkSVGLinearGradient : public SkSVGHiddenContainer {
+public:
+    virtual ~SkSVGLinearGradient() = default;
+    static sk_sp<SkSVGLinearGradient> Make() {
+        return sk_sp<SkSVGLinearGradient>(new SkSVGLinearGradient());
+    }
+
+    void setHref(const SkSVGStringType&);
+    void setX1(const SkSVGLength&);
+    void setY1(const SkSVGLength&);
+    void setX2(const SkSVGLength&);
+    void setY2(const SkSVGLength&);
+
+protected:
+    bool onAsPaint(const SkSVGRenderContext&, SkPaint*) const override;
+
+    void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override;
+
+private:
+    SkSVGLinearGradient();
+
+    void collectColorStops(const SkSVGRenderContext&,
+                           SkSTArray<2, SkScalar, true>*,
+                           SkSTArray<2, SkColor, true>*) const;
+
+    SkSVGLength fX1 = SkSVGLength(0  , SkSVGLength::Unit::kPercentage);
+    SkSVGLength fY1 = SkSVGLength(0  , SkSVGLength::Unit::kPercentage);
+    SkSVGLength fX2 = SkSVGLength(100, SkSVGLength::Unit::kPercentage);
+    SkSVGLength fY2 = SkSVGLength(0  , SkSVGLength::Unit::kPercentage);
+
+    SkSVGStringType fHref;
+
+    typedef SkSVGHiddenContainer INHERITED;
+};
+
+#endif // SkSVGLinearGradient_DEFINED
diff --git a/experimental/svg/model/SkSVGNode.cpp b/experimental/svg/model/SkSVGNode.cpp
index 5a73ace..012c7d2 100644
--- a/experimental/svg/model/SkSVGNode.cpp
+++ b/experimental/svg/model/SkSVGNode.cpp
@@ -24,6 +24,12 @@
     }
 }
 
+bool SkSVGNode::asPaint(const SkSVGRenderContext& ctx, SkPaint* paint) const {
+    SkSVGRenderContext localContext(ctx);
+
+    return this->onPrepareToRender(&localContext) && this->onAsPaint(localContext, paint);
+}
+
 bool SkSVGNode::onPrepareToRender(SkSVGRenderContext* ctx) const {
     ctx->applyPresentationAttributes(fPresentationAttributes);
     return true;
diff --git a/experimental/svg/model/SkSVGNode.h b/experimental/svg/model/SkSVGNode.h
index c02fd3a..dcdb589 100644
--- a/experimental/svg/model/SkSVGNode.h
+++ b/experimental/svg/model/SkSVGNode.h
@@ -13,18 +13,22 @@
 
 class SkCanvas;
 class SkMatrix;
+class SkPaint;
 class SkSVGRenderContext;
 class SkSVGValue;
 
 enum class SkSVGTag {
     kCircle,
+    kDefs,
     kEllipse,
     kG,
     kLine,
+    kLinearGradient,
     kPath,
     kPolygon,
     kPolyline,
     kRect,
+    kStop,
     kSvg
 };
 
@@ -37,6 +41,7 @@
     virtual void appendChild(sk_sp<SkSVGNode>) = 0;
 
     void render(const SkSVGRenderContext&) const;
+    bool asPaint(const SkSVGRenderContext&, SkPaint*) const;
 
     void setAttribute(SkSVGAttribute, const SkSVGValue&);
 
@@ -60,6 +65,8 @@
 
     virtual void onRender(const SkSVGRenderContext&) const = 0;
 
+    virtual bool onAsPaint(const SkSVGRenderContext&, SkPaint*) const { return false; }
+
     virtual void onSetAttribute(SkSVGAttribute, const SkSVGValue&);
 
 private:
diff --git a/experimental/svg/model/SkSVGRenderContext.cpp b/experimental/svg/model/SkSVGRenderContext.cpp
index d8f7621..46ed5a2 100644
--- a/experimental/svg/model/SkSVGRenderContext.cpp
+++ b/experimental/svg/model/SkSVGRenderContext.cpp
@@ -7,6 +7,7 @@
 
 #include "SkCanvas.h"
 #include "SkSVGAttribute.h"
+#include "SkSVGNode.h"
 #include "SkSVGRenderContext.h"
 #include "SkSVGTypes.h"
 
@@ -98,11 +99,18 @@
     }
 }
 
-void applySvgPaint(const SkSVGPaint& svgPaint, SkPaint* p) {
+void applySvgPaint(const SkSVGRenderContext& ctx, const SkSVGPaint& svgPaint, SkPaint* p) {
     switch (svgPaint.type()) {
     case SkSVGPaint::Type::kColor:
         p->setColor(SkColorSetA(svgPaint.color(), p->getAlpha()));
         break;
+    case SkSVGPaint::Type::kIRI: {
+        const auto* node = ctx.findNodeById(svgPaint.iri());
+        if (!node || !node->asPaint(ctx, p)) {
+            p->setColor(SK_ColorTRANSPARENT);
+        }
+        break;
+    }
     case SkSVGPaint::Type::kCurrentColor:
         SkDebugf("unimplemented 'currentColor' paint type");
         // Fall through.
@@ -120,33 +128,33 @@
 // Commit the selected attribute to the paint cache.
 template <SkSVGAttribute>
 void commitToPaint(const SkSVGPresentationAttributes&,
-                   const SkSVGLengthContext&,
+                   const SkSVGRenderContext&,
                    SkSVGPresentationContext*);
 
 template <>
 void commitToPaint<SkSVGAttribute::kFill>(const SkSVGPresentationAttributes& attrs,
-                                          const SkSVGLengthContext&,
+                                          const SkSVGRenderContext& ctx,
                                           SkSVGPresentationContext* pctx) {
-    applySvgPaint(*attrs.fFill.get(), &pctx->fFillPaint);
+    applySvgPaint(ctx, *attrs.fFill.get(), &pctx->fFillPaint);
 }
 
 template <>
 void commitToPaint<SkSVGAttribute::kStroke>(const SkSVGPresentationAttributes& attrs,
-                                            const SkSVGLengthContext&,
+                                            const SkSVGRenderContext& ctx,
                                             SkSVGPresentationContext* pctx) {
-    applySvgPaint(*attrs.fStroke.get(), &pctx->fStrokePaint);
+    applySvgPaint(ctx, *attrs.fStroke.get(), &pctx->fStrokePaint);
 }
 
 template <>
 void commitToPaint<SkSVGAttribute::kFillOpacity>(const SkSVGPresentationAttributes& attrs,
-                                                 const SkSVGLengthContext&,
+                                                 const SkSVGRenderContext&,
                                                  SkSVGPresentationContext* pctx) {
     pctx->fFillPaint.setAlpha(opacity_to_alpha(*attrs.fFillOpacity.get()));
 }
 
 template <>
 void commitToPaint<SkSVGAttribute::kStrokeLineCap>(const SkSVGPresentationAttributes& attrs,
-                                                   const SkSVGLengthContext&,
+                                                   const SkSVGRenderContext&,
                                                    SkSVGPresentationContext* pctx) {
     const auto& cap = *attrs.fStrokeLineCap.get();
     if (cap.type() != SkSVGLineCap::Type::kInherit) {
@@ -156,7 +164,7 @@
 
 template <>
 void commitToPaint<SkSVGAttribute::kStrokeLineJoin>(const SkSVGPresentationAttributes& attrs,
-                                                    const SkSVGLengthContext&,
+                                                    const SkSVGRenderContext&,
                                                     SkSVGPresentationContext* pctx) {
     const auto& join = *attrs.fStrokeLineJoin.get();
     if (join.type() != SkSVGLineJoin::Type::kInherit) {
@@ -166,17 +174,17 @@
 
 template <>
 void commitToPaint<SkSVGAttribute::kStrokeOpacity>(const SkSVGPresentationAttributes& attrs,
-                                                   const SkSVGLengthContext&,
+                                                   const SkSVGRenderContext&,
                                                    SkSVGPresentationContext* pctx) {
     pctx->fStrokePaint.setAlpha(opacity_to_alpha(*attrs.fStrokeOpacity.get()));
 }
 
 template <>
 void commitToPaint<SkSVGAttribute::kStrokeWidth>(const SkSVGPresentationAttributes& attrs,
-                                                 const SkSVGLengthContext& lctx,
+                                                 const SkSVGRenderContext& ctx,
                                                  SkSVGPresentationContext* pctx) {
-    auto strokeWidth = lctx.resolve(*attrs.fStrokeWidth.get(),
-                                    SkSVGLengthContext::LengthType::kOther);
+    auto strokeWidth = ctx.lengthContext().resolve(*attrs.fStrokeWidth.get(),
+                                                   SkSVGLengthContext::LengthType::kOther);
     pctx->fStrokePaint.setStrokeWidth(strokeWidth);
 }
 
@@ -193,7 +201,10 @@
     fStrokePaint.setAntiAlias(true);
 
     // Commit initial values to the paint cache.
-    SkSVGLengthContext dummy(SkSize::Make(0, 0));
+    SkCanvas dummyCanvas(0, 0);
+    SkSVGRenderContext dummy(&dummyCanvas, SkSVGIDMapper(), SkSVGLengthContext(SkSize::Make(0, 0)),
+                             *this);
+
     commitToPaint<SkSVGAttribute::kFill>(fInherited, dummy, this);
     commitToPaint<SkSVGAttribute::kFillOpacity>(fInherited, dummy, this);
     commitToPaint<SkSVGAttribute::kStroke>(fInherited, dummy, this);
@@ -204,15 +215,18 @@
 }
 
 SkSVGRenderContext::SkSVGRenderContext(SkCanvas* canvas,
+                                       const SkSVGIDMapper& mapper,
                                        const SkSVGLengthContext& lctx,
                                        const SkSVGPresentationContext& pctx)
-    : fLengthContext(lctx)
+    : fIDMapper(mapper)
+    , fLengthContext(lctx)
     , fPresentationContext(pctx)
     , fCanvas(canvas)
     , fCanvasSaveCount(canvas->getSaveCount()) {}
 
 SkSVGRenderContext::SkSVGRenderContext(const SkSVGRenderContext& other)
     : SkSVGRenderContext(other.fCanvas,
+                         other.fIDMapper,
                          *other.fLengthContext,
                          *other.fPresentationContext) {}
 
@@ -220,6 +234,11 @@
     fCanvas->restoreToCount(fCanvasSaveCount);
 }
 
+const SkSVGNode* SkSVGRenderContext::findNodeById(const SkString& id) const {
+    const auto* v = fIDMapper.find(id);
+    return v ? v->get() : nullptr;
+}
+
 void SkSVGRenderContext::applyPresentationAttributes(const SkSVGPresentationAttributes& attrs) {
 
 #define ApplyLazyInheritedAttribute(ATTR)                                               \
@@ -231,7 +250,7 @@
             /* Update the local attribute value */                                      \
             fPresentationContext.writable()->fInherited.f ## ATTR.set(*value);          \
             /* Update the cached paints */                                              \
-            commitToPaint<SkSVGAttribute::k ## ATTR>(attrs, *fLengthContext,            \
+            commitToPaint<SkSVGAttribute::k ## ATTR>(attrs, *this,    \
                                                      fPresentationContext.writable());  \
         }                                                                               \
     } while (false)
diff --git a/experimental/svg/model/SkSVGRenderContext.h b/experimental/svg/model/SkSVGRenderContext.h
index 61f8746..68209a7 100644
--- a/experimental/svg/model/SkSVGRenderContext.h
+++ b/experimental/svg/model/SkSVGRenderContext.h
@@ -12,6 +12,7 @@
 #include "SkRect.h"
 #include "SkSize.h"
 #include "SkSVGAttribute.h"
+#include "SkSVGIDMapper.h"
 #include "SkTLazy.h"
 #include "SkTypes.h"
 
@@ -56,7 +57,8 @@
 
 class SkSVGRenderContext {
 public:
-    SkSVGRenderContext(SkCanvas*, const SkSVGLengthContext&, const SkSVGPresentationContext&);
+    SkSVGRenderContext(SkCanvas*, const SkSVGIDMapper&, const SkSVGLengthContext&,
+                       const SkSVGPresentationContext&);
     SkSVGRenderContext(const SkSVGRenderContext&);
     ~SkSVGRenderContext();
 
@@ -67,6 +69,8 @@
 
     void applyPresentationAttributes(const SkSVGPresentationAttributes&);
 
+    const SkSVGNode* findNodeById(const SkString&) const;
+
     const SkPaint* fillPaint() const;
     const SkPaint* strokePaint() const;
 
@@ -76,6 +80,7 @@
     void* operator new(size_t, void*)                        = delete;
     SkSVGRenderContext& operator=(const SkSVGRenderContext&) = delete;
 
+    const SkSVGIDMapper&                          fIDMapper;
     SkTCopyOnFirstWrite<SkSVGLengthContext>       fLengthContext;
     SkTCopyOnFirstWrite<SkSVGPresentationContext> fPresentationContext;
     SkCanvas*                                     fCanvas;
diff --git a/experimental/svg/model/SkSVGStop.cpp b/experimental/svg/model/SkSVGStop.cpp
new file mode 100644
index 0000000..3abf505
--- /dev/null
+++ b/experimental/svg/model/SkSVGStop.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkSVGRenderContext.h"
+#include "SkSVGStop.h"
+#include "SkSVGValue.h"
+
+SkSVGStop::SkSVGStop() : INHERITED(SkSVGTag::kStop) {}
+
+void SkSVGStop::setOffset(const SkSVGLength& offset) {
+    fOffset = offset;
+}
+
+void SkSVGStop::setStopColor(const SkSVGColorType& color) {
+    fStopColor = color;
+}
+
+void SkSVGStop::setStopOpacity(const SkSVGNumberType& opacity) {
+    fStopOpacity = SkTPin<SkScalar>(opacity.value(), 0, 1);
+}
+
+void SkSVGStop::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) {
+    switch (attr) {
+    case SkSVGAttribute::kOffset:
+        if (const auto* offset = v.as<SkSVGLengthValue>()) {
+            this->setOffset(*offset);
+        }
+        break;
+    case SkSVGAttribute::kStopColor:
+        if (const auto* color = v.as<SkSVGColorValue>()) {
+            this->setStopColor(*color);
+        }
+        break;
+    case SkSVGAttribute::kStopOpacity:
+        if (const auto* opacity = v.as<SkSVGNumberValue>()) {
+            this->setStopOpacity(*opacity);
+        }
+        break;
+    default:
+        this->INHERITED::onSetAttribute(attr, v);
+    }
+}
diff --git a/experimental/svg/model/SkSVGStop.h b/experimental/svg/model/SkSVGStop.h
new file mode 100644
index 0000000..2ffbc5c
--- /dev/null
+++ b/experimental/svg/model/SkSVGStop.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkSVGStop_DEFINED
+#define SkSVGStop_DEFINED
+
+#include "SkSVGHiddenContainer.h"
+#include "SkSVGTypes.h"
+
+class SkSVGLengthContext;
+
+class SkSVGStop : public SkSVGHiddenContainer {
+public:
+    virtual ~SkSVGStop() = default;
+    static sk_sp<SkSVGStop> Make() {
+        return sk_sp<SkSVGStop>(new SkSVGStop());
+    }
+
+    const SkSVGLength& offset() const { return fOffset; }
+    const SkSVGColorType& stopColor() const { return fStopColor; }
+    const SkSVGNumberType& stopOpacity() const { return fStopOpacity; }
+
+    void setOffset(const SkSVGLength&);
+    void setStopColor(const SkSVGColorType&);
+    void setStopOpacity(const SkSVGNumberType&);
+
+protected:
+    void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override;
+
+private:
+    SkSVGStop();
+
+    SkSVGLength          fOffset = SkSVGLength(0  , SkSVGLength::Unit::kPercentage);
+    SkSVGColorType    fStopColor = SkSVGColorType(SK_ColorBLACK);
+    SkSVGNumberType fStopOpacity = SkSVGNumberType(1);
+
+    typedef SkSVGHiddenContainer INHERITED;
+};
+
+#endif // SkSVGStop_DEFINED
diff --git a/experimental/svg/model/SkSVGTypes.h b/experimental/svg/model/SkSVGTypes.h
index 18ec3ce..b2343a1 100644
--- a/experimental/svg/model/SkSVGTypes.h
+++ b/experimental/svg/model/SkSVGTypes.h
@@ -13,6 +13,7 @@
 #include "SkPoint.h"
 #include "SkRect.h"
 #include "SkScalar.h"
+#include "SkString.h"
 #include "SkTDArray.h"
 #include "SkTypes.h"
 
@@ -42,6 +43,7 @@
 
 using SkSVGColorType      = SkSVGPrimitiveTypeWrapper<SkColor >;
 using SkSVGNumberType     = SkSVGPrimitiveTypeWrapper<SkScalar>;
+using SkSVGStringType     = SkSVGPrimitiveTypeWrapper<SkString>;
 using SkSVGViewBoxType    = SkSVGPrimitiveTypeWrapper<SkRect  >;
 using SkSVGTransformType  = SkSVGPrimitiveTypeWrapper<SkMatrix>;
 using SkSVGPointsType     = SkSVGPrimitiveTypeWrapper<SkTDArray<SkPoint>>;
@@ -88,27 +90,33 @@
         kCurrentColor,
         kColor,
         kInherit,
+        kIRI,
     };
 
-    constexpr SkSVGPaint() : fType(Type::kInherit), fColor(SK_ColorBLACK) {}
-    explicit constexpr SkSVGPaint(Type t) : fType(t), fColor(SK_ColorBLACK) {}
-    explicit constexpr SkSVGPaint(const SkSVGColorType& c) : fType(Type::kColor), fColor(c) {}
+    SkSVGPaint() : fType(Type::kInherit), fColor(SK_ColorBLACK) {}
+    explicit SkSVGPaint(Type t) : fType(t), fColor(SK_ColorBLACK) {}
+    explicit SkSVGPaint(const SkSVGColorType& c) : fType(Type::kColor), fColor(c) {}
+    explicit SkSVGPaint(const SkString& iri)
+        : fType(Type::kIRI), fColor(SK_ColorBLACK), fIRI(iri) {}
 
     SkSVGPaint(const SkSVGPaint&)            = default;
     SkSVGPaint& operator=(const SkSVGPaint&) = default;
 
     bool operator==(const SkSVGPaint& other) const {
-        return fType == other.fType && fColor == other.fColor;
+        return fType == other.fType && fColor == other.fColor && fIRI == other.fIRI;
     }
     bool operator!=(const SkSVGPaint& other) const { return !(*this == other); }
 
     Type type() const { return fType; }
     const SkSVGColorType& color() const { SkASSERT(fType == Type::kColor); return fColor; }
+    const SkString& iri() const { SkASSERT(fType == Type::kIRI); return fIRI; }
 
 private:
     Type fType;
 
+    // Logical union.
     SkSVGColorType fColor;
+    SkString       fIRI;
 };
 
 class SkSVGLineCap {
diff --git a/experimental/svg/model/SkSVGValue.h b/experimental/svg/model/SkSVGValue.h
index 8f93bd8..e4673e9 100644
--- a/experimental/svg/model/SkSVGValue.h
+++ b/experimental/svg/model/SkSVGValue.h
@@ -25,6 +25,7 @@
         kPaint,
         kPath,
         kPoints,
+        kString,
         kTransform,
         kViewBox,
     };
@@ -77,5 +78,6 @@
 using SkSVGLineJoinValue  = SkSVGWrapperValue<SkSVGLineJoin     , SkSVGValue::Type::kLineJoin >;
 using SkSVGNumberValue    = SkSVGWrapperValue<SkSVGNumberType   , SkSVGValue::Type::kNumber   >;
 using SkSVGPointsValue    = SkSVGWrapperValue<SkSVGPointsType   , SkSVGValue::Type::kPoints   >;
+using SkSVGStringValue    = SkSVGWrapperValue<SkSVGStringType   , SkSVGValue::Type::kString   >;
 
 #endif // SkSVGValue_DEFINED
diff --git a/gyp/svg.gyp b/gyp/svg.gyp
index 1160b94..fc11fe5 100644
--- a/gyp/svg.gyp
+++ b/gyp/svg.gyp
@@ -52,13 +52,18 @@
         '../experimental/svg/model/SkSVGCircle.cpp',
         '../experimental/svg/model/SkSVGContainer.h',
         '../experimental/svg/model/SkSVGContainer.cpp',
+        '../experimental/svg/model/SkSVGDefs.h',
         '../experimental/svg/model/SkSVGDOM.h',
         '../experimental/svg/model/SkSVGDOM.cpp',
         '../experimental/svg/model/SkSVGEllipse.h',
         '../experimental/svg/model/SkSVGEllipse.cpp',
         '../experimental/svg/model/SkSVGG.h',
+        '../experimental/svg/model/SkSVGHiddenContainer.h',
+        '../experimental/svg/model/SkSVGIDMapper.h',
         '../experimental/svg/model/SkSVGLine.h',
         '../experimental/svg/model/SkSVGLine.cpp',
+        '../experimental/svg/model/SkSVGLinearGradient.h',
+        '../experimental/svg/model/SkSVGLinearGradient.cpp',
         '../experimental/svg/model/SkSVGNode.h',
         '../experimental/svg/model/SkSVGNode.cpp',
         '../experimental/svg/model/SkSVGPath.h',
@@ -71,6 +76,8 @@
         '../experimental/svg/model/SkSVGRenderContext.cpp',
         '../experimental/svg/model/SkSVGShape.h',
         '../experimental/svg/model/SkSVGShape.cpp',
+        '../experimental/svg/model/SkSVGStop.h',
+        '../experimental/svg/model/SkSVGStop.cpp',
         '../experimental/svg/model/SkSVGSVG.h',
         '../experimental/svg/model/SkSVGSVG.cpp',
         '../experimental/svg/model/SkSVGTransformableNode.h',