grab from latest android



git-svn-id: http://skia.googlecode.com/svn/trunk@27 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/xml/SkBML_Verbs.h b/src/xml/SkBML_Verbs.h
new file mode 100644
index 0000000..86bfede
--- /dev/null
+++ b/src/xml/SkBML_Verbs.h
@@ -0,0 +1,33 @@
+/* libs/graphics/xml/SkBML_Verbs.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#ifndef SkBML_Verbs_DEFINED
+#define SkBML_Verbs_DEFINED
+
+enum Verbs {
+    kStartElem_Value_Verb,
+    kStartElem_Index_Verb,
+    kEndElem_Verb,
+    kAttr_Value_Value_Verb,
+    kAttr_Value_Index_Verb,
+    kAttr_Index_Value_Verb,
+    kAttr_Index_Index_Verb,
+
+    kVerbCount
+};
+
+#endif // SkBML_Verbs_DEFINED
diff --git a/src/xml/SkBML_XMLParser.cpp b/src/xml/SkBML_XMLParser.cpp
new file mode 100644
index 0000000..53b7f61
--- /dev/null
+++ b/src/xml/SkBML_XMLParser.cpp
@@ -0,0 +1,192 @@
+/* libs/graphics/xml/SkBML_XMLParser.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include "SkBML_XMLParser.h"
+#include "SkBML_Verbs.h"
+#include "SkStream.h"
+#include "SkXMLWriter.h"
+
+static uint8_t rbyte(SkStream& s)
+{
+    uint8_t b;
+    size_t size = s.read(&b, 1);
+    SkASSERT(size == 1);
+    return b;
+}
+
+static int rdata(SkStream& s, int data)
+{
+    SkASSERT((data & ~31) == 0);
+    if (data == 31)
+    {
+        data = rbyte(s);
+        if (data == 0xFF)
+        {
+            data = rbyte(s);
+            data = (data << 8) | rbyte(s);
+        }
+    }
+    return data;
+}
+
+static void set(char* array[256], int index, SkStream& s, int data)
+{
+    SkASSERT((unsigned)index <= 255);
+
+    size_t size = rdata(s, data);
+
+    if (array[index] == NULL)
+        array[index] = (char*)sk_malloc_throw(size + 1);
+    else
+    {
+        if (strlen(array[index]) < size)
+            array[index] = (char*)sk_realloc_throw(array[index], size + 1);
+    }
+
+    s.read(array[index], size);
+    array[index][size] = 0;
+}
+
+static void freeAll(char* array[256])
+{
+    for (int i = 0; i < 256; i++)
+        sk_free(array[i]);
+}
+
+struct BMLW {
+    char*   fElems[256];
+    char*   fNames[256];
+    char*   fValues[256];
+
+    // important that these are uint8_t, so we get automatic wrap-around
+    uint8_t  fNextElem, fNextName, fNextValue;
+
+    BMLW()
+    {
+        memset(fElems, 0, sizeof(fElems));
+        memset(fNames, 0, sizeof(fNames));
+        memset(fValues, 0, sizeof(fValues));
+
+        fNextElem = fNextName = fNextValue = 0;
+    }
+    ~BMLW()
+    {
+        freeAll(fElems);
+        freeAll(fNames);
+        freeAll(fValues);
+    }
+};
+
+static void rattr(unsigned verb, SkStream& s, BMLW& rec, SkXMLWriter& writer)
+{
+    int data = verb & 31;
+    verb >>= 5;
+
+    int nameIndex, valueIndex;
+
+    switch (verb) {
+    case kAttr_Value_Value_Verb:
+        nameIndex = rec.fNextName;      // record before the ++
+        set(rec.fNames, rec.fNextName++, s, data);
+        valueIndex = rec.fNextValue;    // record before the ++
+        set(rec.fValues, rec.fNextValue++, s, 31);
+        break;
+    case kAttr_Value_Index_Verb:
+        nameIndex = rec.fNextName;      // record before the ++
+        set(rec.fNames, rec.fNextName++, s, data);
+        valueIndex = rbyte(s);
+        break;
+    case kAttr_Index_Value_Verb:
+        nameIndex = rdata(s, data);
+        valueIndex = rec.fNextValue;    // record before the ++
+        set(rec.fValues, rec.fNextValue++, s, 31);
+        break;
+    case kAttr_Index_Index_Verb:
+        nameIndex = rdata(s, data);
+        valueIndex = rbyte(s);
+        break;
+    default:
+        SkASSERT(!"bad verb");
+        return;
+    }
+    writer.addAttribute(rec.fNames[nameIndex], rec.fValues[valueIndex]);
+}
+
+static void relem(unsigned verb, SkStream& s, BMLW& rec, SkXMLWriter& writer)
+{
+    int data = verb & 31;
+    verb >>= 5;
+
+    int elemIndex;
+
+    if (verb == kStartElem_Value_Verb)
+    {
+        elemIndex = rec.fNextElem;      // record before the ++
+        set(rec.fElems, rec.fNextElem++, s, data);
+    }
+    else
+    {
+        SkASSERT(verb == kStartElem_Index_Verb);
+        elemIndex = rdata(s, data);
+    }
+
+    writer.startElement(rec.fElems[elemIndex]);
+
+    for (;;)
+    {
+        verb = rbyte(s);
+        switch (verb >> 5) {
+        case kAttr_Value_Value_Verb:
+        case kAttr_Value_Index_Verb:
+        case kAttr_Index_Value_Verb:
+        case kAttr_Index_Index_Verb:
+            rattr(verb, s, rec, writer);
+            break;
+        case kStartElem_Value_Verb:
+        case kStartElem_Index_Verb:
+            relem(verb, s, rec, writer);
+            break;
+        case kEndElem_Verb:
+            writer.endElement();
+            return;
+        default:
+            SkASSERT(!"bad verb");
+        }
+    }
+}
+
+void BML_XMLParser::Read(SkStream& s, SkXMLWriter& writer)
+{
+    BMLW rec;
+    writer.writeHeader();
+    relem(rbyte(s), s, rec, writer);
+}
+
+void BML_XMLParser::Read(SkStream& s, SkWStream& output)
+{
+    SkXMLStreamWriter writer(&output);
+    Read(s, writer);
+}
+
+void BML_XMLParser::Read(SkStream& s, SkXMLParser& output)
+{
+    SkXMLParserWriter writer(&output);
+    Read(s, writer);
+}
+
+
+
diff --git a/src/xml/SkDOM.cpp b/src/xml/SkDOM.cpp
new file mode 100644
index 0000000..a9fc31e
--- /dev/null
+++ b/src/xml/SkDOM.cpp
@@ -0,0 +1,512 @@
+/* libs/graphics/xml/SkDOM.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include "SkDOM.h"
+
+/////////////////////////////////////////////////////////////////////////
+
+#include "SkXMLParser.h"
+
+bool SkXMLParser::parse(const SkDOM& dom, const SkDOMNode* node)
+{
+    const char* elemName = dom.getName(node);
+
+    if (this->startElement(elemName))
+        return false;
+    
+    SkDOM::AttrIter iter(dom, node);
+    const char*     name, *value;
+    
+    while ((name = iter.next(&value)) != NULL)
+        if (this->addAttribute(name, value))
+            return false;
+
+    if ((node = dom.getFirstChild(node)) != NULL)
+        do {
+            if (!this->parse(dom, node))
+                return false;
+        } while ((node = dom.getNextSibling(node)) != NULL);
+    
+    return !this->endElement(elemName);
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+struct SkDOMAttr {
+    const char* fName;
+    const char* fValue;
+};
+
+struct SkDOMNode {
+    const char* fName;
+    SkDOMNode*  fFirstChild;
+    SkDOMNode*  fNextSibling;
+    uint16_t    fAttrCount;
+    uint8_t     fType;
+    uint8_t     fPad;
+
+    const SkDOMAttr* attrs() const
+    {
+        return (const SkDOMAttr*)(this + 1);
+    }
+    SkDOMAttr* attrs()
+    {
+        return (SkDOMAttr*)(this + 1);
+    }
+};
+
+/////////////////////////////////////////////////////////////////////////
+
+#define kMinChunkSize   512
+
+SkDOM::SkDOM() : fAlloc(kMinChunkSize), fRoot(NULL)
+{
+}
+
+SkDOM::~SkDOM()
+{
+}
+
+const SkDOM::Node* SkDOM::getRootNode() const
+{
+    return fRoot;
+}
+
+const SkDOM::Node* SkDOM::getFirstChild(const Node* node, const char name[]) const
+{
+    SkASSERT(node);
+    const Node* child = node->fFirstChild;
+
+    if (name)
+    {
+        for (; child != NULL; child = child->fNextSibling)
+            if (!strcmp(name, child->fName))
+                break;
+    }
+    return child;
+}
+
+const SkDOM::Node* SkDOM::getNextSibling(const Node* node, const char name[]) const
+{
+    SkASSERT(node);
+    const Node* sibling = node->fNextSibling;
+    if (name)
+    {
+        for (; sibling != NULL; sibling = sibling->fNextSibling)
+            if (!strcmp(name, sibling->fName))
+                break;
+    }
+    return sibling;
+}
+
+SkDOM::Type SkDOM::getType(const Node* node) const
+{
+    SkASSERT(node);
+    return (Type)node->fType;
+}
+
+const char* SkDOM::getName(const Node* node) const
+{
+    SkASSERT(node);
+    return node->fName;
+}
+
+const char* SkDOM::findAttr(const Node* node, const char name[]) const
+{
+    SkASSERT(node);
+    const Attr* attr = node->attrs();
+    const Attr* stop = attr + node->fAttrCount;
+
+    while (attr < stop)
+    {
+        if (!strcmp(attr->fName, name))
+            return attr->fValue;
+        attr += 1;
+    }
+    return NULL;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+const SkDOM::Attr* SkDOM::getFirstAttr(const Node* node) const
+{
+    return node->fAttrCount ? node->attrs() : NULL;
+}
+
+const SkDOM::Attr* SkDOM::getNextAttr(const Node* node, const Attr* attr) const
+{
+    SkASSERT(node);
+    if (attr == NULL)
+        return NULL;
+    return (attr - node->attrs() + 1) < node->fAttrCount ? attr + 1 : NULL;
+}
+
+const char* SkDOM::getAttrName(const Node* node, const Attr* attr) const
+{
+    SkASSERT(node);
+    SkASSERT(attr);
+    return attr->fName;
+}
+
+const char* SkDOM::getAttrValue(const Node* node, const Attr* attr) const
+{
+    SkASSERT(node);
+    SkASSERT(attr);
+    return attr->fValue;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+SkDOM::AttrIter::AttrIter(const SkDOM&, const SkDOM::Node* node)
+{
+    SkASSERT(node);
+    fAttr = node->attrs();
+    fStop = fAttr + node->fAttrCount;
+}
+
+const char* SkDOM::AttrIter::next(const char** value)
+{
+    const char* name = NULL;
+
+    if (fAttr < fStop)
+    {
+        name = fAttr->fName;
+        if (value)
+            *value = fAttr->fValue;
+        fAttr += 1;
+    }
+    return name;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+#include "SkXMLParser.h"
+#include "SkTDArray.h"
+
+static char* dupstr(SkChunkAlloc* chunk, const char src[])
+{
+    SkASSERT(chunk && src);
+    size_t  len = strlen(src);
+    char*   dst = (char*)chunk->alloc(len + 1, SkChunkAlloc::kThrow_AllocFailType);
+    memcpy(dst, src, len + 1);
+    return dst;
+}
+
+class SkDOMParser : public SkXMLParser {
+    bool fNeedToFlush;
+public:
+    SkDOMParser(SkChunkAlloc* chunk) : SkXMLParser(&fParserError), fAlloc(chunk)
+    {
+        fRoot = NULL;
+        fLevel = 0;
+        fNeedToFlush = true;
+    }
+    SkDOM::Node* getRoot() const { return fRoot; }
+    SkXMLParserError fParserError;
+protected:
+    void flushAttributes()
+    {
+        int attrCount = fAttrs.count();
+
+        SkDOM::Node* node = (SkDOM::Node*)fAlloc->alloc(sizeof(SkDOM::Node) + attrCount * sizeof(SkDOM::Attr),
+                                                        SkChunkAlloc::kThrow_AllocFailType);
+
+        node->fName = fElemName;
+        node->fFirstChild = NULL;
+        node->fAttrCount = SkToU16(attrCount);
+        node->fType = SkDOM::kElement_Type;
+
+        if (fRoot == NULL)
+        {
+            node->fNextSibling = NULL;
+            fRoot = node;
+        }
+        else    // this adds siblings in reverse order. gets corrected in onEndElement()
+        {
+            SkDOM::Node* parent = fParentStack.top();
+            SkASSERT(fRoot && parent);
+            node->fNextSibling = parent->fFirstChild;
+            parent->fFirstChild = node;
+        }
+        *fParentStack.push() = node;
+
+        memcpy(node->attrs(), fAttrs.begin(), attrCount * sizeof(SkDOM::Attr));
+        fAttrs.reset();
+
+    }
+    virtual bool onStartElement(const char elem[])
+    {
+        if (fLevel > 0 && fNeedToFlush)
+            this->flushAttributes();
+        fNeedToFlush = true;
+        fElemName = dupstr(fAlloc, elem);
+        ++fLevel;
+        return false;
+    }
+    virtual bool onAddAttribute(const char name[], const char value[])
+    {
+        SkDOM::Attr* attr = fAttrs.append();
+        attr->fName = dupstr(fAlloc, name);
+        attr->fValue = dupstr(fAlloc, value);
+        return false;
+    }
+    virtual bool onEndElement(const char elem[])
+    {
+        --fLevel;
+        if (fNeedToFlush)
+            this->flushAttributes();
+        fNeedToFlush = false;
+
+        SkDOM::Node* parent;
+
+        fParentStack.pop(&parent);
+
+        SkDOM::Node* child = parent->fFirstChild;
+        SkDOM::Node* prev = NULL;
+        while (child)
+        {
+            SkDOM::Node* next = child->fNextSibling;
+            child->fNextSibling = prev;
+            prev = child;
+            child = next;
+        }
+        parent->fFirstChild = prev;
+        return false;
+    }
+private:
+    SkTDArray<SkDOM::Node*> fParentStack;
+    SkChunkAlloc*   fAlloc;
+    SkDOM::Node*    fRoot;
+
+    // state needed for flushAttributes()
+    SkTDArray<SkDOM::Attr>  fAttrs;
+    char*                   fElemName;
+    int                     fLevel;
+};
+
+const SkDOM::Node* SkDOM::build(const char doc[], size_t len)
+{
+    fAlloc.reset();
+    SkDOMParser parser(&fAlloc);
+    if (!parser.parse(doc, len))
+    {
+        SkDEBUGCODE(SkDebugf("xml parse error, line %d\n", parser.fParserError.getLineNumber());)
+        fRoot = NULL;
+        fAlloc.reset();
+        return NULL;
+    }
+    fRoot = parser.getRoot();
+    return fRoot;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+static void walk_dom(const SkDOM& dom, const SkDOM::Node* node, SkXMLParser* parser)
+{
+    const char* elem = dom.getName(node);
+
+    parser->startElement(elem);
+    
+    SkDOM::AttrIter iter(dom, node);
+    const char*     name;
+    const char*     value;
+    while ((name = iter.next(&value)) != NULL)
+        parser->addAttribute(name, value);
+
+    node = dom.getFirstChild(node, NULL);
+    while (node)
+    {
+        walk_dom(dom, node, parser);
+        node = dom.getNextSibling(node, NULL);
+    }
+
+    parser->endElement(elem);
+}
+
+const SkDOM::Node* SkDOM::copy(const SkDOM& dom, const SkDOM::Node* node)
+{
+    fAlloc.reset();
+    SkDOMParser parser(&fAlloc);
+
+    walk_dom(dom, node, &parser);
+
+    fRoot = parser.getRoot();
+    return fRoot;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+int SkDOM::countChildren(const Node* node, const char elem[]) const
+{
+    int count = 0;
+
+    node = this->getFirstChild(node, elem);
+    while (node)
+    {
+        count += 1;
+        node = this->getNextSibling(node, elem);
+    }
+    return count;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+#include "SkParse.h"
+
+bool SkDOM::findS32(const Node* node, const char name[], int32_t* value) const
+{
+    const char* vstr = this->findAttr(node, name);
+    return vstr && SkParse::FindS32(vstr, value);
+}
+
+bool SkDOM::findScalars(const Node* node, const char name[], SkScalar value[], int count) const
+{
+    const char* vstr = this->findAttr(node, name);
+    return vstr && SkParse::FindScalars(vstr, value, count);
+}
+
+bool SkDOM::findHex(const Node* node, const char name[], uint32_t* value) const
+{
+    const char* vstr = this->findAttr(node, name);
+    return vstr && SkParse::FindHex(vstr, value);
+}
+
+bool SkDOM::findBool(const Node* node, const char name[], bool* value) const
+{
+    const char* vstr = this->findAttr(node, name);
+    return vstr && SkParse::FindBool(vstr, value);
+}
+
+int SkDOM::findList(const Node* node, const char name[], const char list[]) const
+{
+    const char* vstr = this->findAttr(node, name);
+    return vstr ? SkParse::FindList(vstr, list) : -1;
+}
+
+bool SkDOM::hasAttr(const Node* node, const char name[], const char value[]) const
+{
+    const char* vstr = this->findAttr(node, name);
+    return vstr && !strcmp(vstr, value);
+}
+
+bool SkDOM::hasS32(const Node* node, const char name[], int32_t target) const
+{
+    const char* vstr = this->findAttr(node, name);
+    int32_t     value;
+    return vstr && SkParse::FindS32(vstr, &value) && value == target;
+}
+
+bool SkDOM::hasScalar(const Node* node, const char name[], SkScalar target) const
+{
+    const char* vstr = this->findAttr(node, name);
+    SkScalar    value;
+    return vstr && SkParse::FindScalar(vstr, &value) && value == target;
+}
+
+bool SkDOM::hasHex(const Node* node, const char name[], uint32_t target) const
+{
+    const char* vstr = this->findAttr(node, name);
+    uint32_t    value;
+    return vstr && SkParse::FindHex(vstr, &value) && value == target;
+}
+
+bool SkDOM::hasBool(const Node* node, const char name[], bool target) const
+{
+    const char* vstr = this->findAttr(node, name);
+    bool        value;
+    return vstr && SkParse::FindBool(vstr, &value) && value == target;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+static void tab(int level)
+{
+    while (--level >= 0)
+        SkDebugf("\t");
+}
+
+void SkDOM::dump(const Node* node, int level) const
+{
+    if (node == NULL)
+        node = this->getRootNode();
+    if (node)
+    {
+        tab(level);
+        SkDebugf("<%s", this->getName(node));
+
+        const Attr* attr = node->attrs();
+        const Attr* stop = attr + node->fAttrCount;
+        for (; attr < stop; attr++)
+            SkDebugf(" %s=\"%s\"", attr->fName, attr->fValue);
+
+        const Node* child = this->getFirstChild(node);
+        if (child)
+        {
+            SkDebugf(">\n");
+            while (child)
+            {
+                this->dump(child, level+1);
+                child = this->getNextSibling(child);
+            }
+            tab(level);
+            SkDebugf("</%s>\n", node->fName);
+        }
+        else
+            SkDebugf("/>\n");
+    }
+}
+
+void SkDOM::UnitTest()
+{
+#ifdef SK_SUPPORT_UNITTEST
+    static const char gDoc[] = 
+        "<root a='1' b='2'>"
+            "<elem1 c='3' />"
+            "<elem2 d='4' />"
+            "<elem3 e='5'>"
+                "<subelem1/>"
+                "<subelem2 f='6' g='7'/>"
+            "</elem3>"
+            "<elem4 h='8'/>"
+        "</root>"
+        ;
+
+    SkDOM   dom;
+
+    SkASSERT(dom.getRootNode() == NULL);
+
+    const Node* root = dom.build(gDoc, sizeof(gDoc) - 1);
+    SkASSERT(root && dom.getRootNode() == root);
+
+    const char* v = dom.findAttr(root, "a");
+    SkASSERT(v && !strcmp(v, "1"));
+    v = dom.findAttr(root, "b");
+    SkASSERT(v && !strcmp(v, "2"));
+    v = dom.findAttr(root, "c");
+    SkASSERT(v == NULL);
+
+    SkASSERT(dom.getFirstChild(root, "elem1"));
+    SkASSERT(!dom.getFirstChild(root, "subelem1"));
+
+    dom.dump();
+#endif
+}
+
+#endif
+
diff --git a/src/xml/SkJS.cpp b/src/xml/SkJS.cpp
new file mode 100644
index 0000000..03ccba6
--- /dev/null
+++ b/src/xml/SkJS.cpp
@@ -0,0 +1,237 @@
+/* libs/graphics/xml/SkJS.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <jsapi.h>
+
+#include "SkJS.h"
+#include "SkString.h"
+
+#ifdef _WIN32_WCE
+extern "C" {
+    void abort() {
+        SkASSERT(0);
+    }
+
+    unsigned int _control87(unsigned int _new, unsigned int mask ) {
+        SkASSERT(0);
+        return 0;
+    }
+
+    time_t mktime(struct tm *timeptr ) {
+        SkASSERT(0);
+        return 0;
+    }
+
+//  int errno;
+
+    char *strdup(const char *) {
+        SkASSERT(0);
+        return 0;
+    }
+
+    char *strerror(int errnum) {
+        SkASSERT(0);
+        return 0;
+    }
+
+    int isatty(void* fd) {
+        SkASSERT(0);
+        return 0;
+    }
+
+    int putenv(const char *envstring) {
+        SkASSERT(0);
+        return 0;
+    }
+
+    char *getenv(const char *varname) {
+        SkASSERT(0);
+        return 0;
+    }
+
+    void GetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime) {
+        SkASSERT(0);
+    }
+
+    struct tm * localtime(const time_t *timer) {
+        SkASSERT(0);
+        return 0;
+    }
+
+    size_t strftime(char *strDest, size_t maxsize, const char *format,
+        const struct tm *timeptr ) {
+        SkASSERT(0);
+        return 0;
+    }
+
+}
+#endif
+
+static JSBool
+global_enumerate(JSContext *cx, JSObject *obj)
+{
+#ifdef LAZY_STANDARD_CLASSES
+    return JS_EnumerateStandardClasses(cx, obj);
+#else
+    return JS_TRUE;
+#endif
+}
+
+static JSBool
+global_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp)
+{
+#ifdef LAZY_STANDARD_CLASSES
+    if ((flags & JSRESOLVE_ASSIGNING) == 0) {
+        JSBool resolved;
+
+        if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
+            return JS_FALSE;
+        if (resolved) {
+            *objp = obj;
+            return JS_TRUE;
+        }
+    }
+#endif
+
+#if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
+    if ((flags & (JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING)) == 0) {
+        /*
+         * Do this expensive hack only for unoptimized Unix builds, which are
+         * not used for benchmarking.
+         */
+        char *path, *comp, *full;
+        const char *name;
+        JSBool ok, found;
+        JSFunction *fun;
+
+        if (!JSVAL_IS_STRING(id))
+            return JS_TRUE;
+        path = getenv("PATH");
+        if (!path)
+            return JS_TRUE;
+        path = JS_strdup(cx, path);
+        if (!path)
+            return JS_FALSE;
+        name = JS_GetStringBytes(JSVAL_TO_STRING(id));
+        ok = JS_TRUE;
+        for (comp = strtok(path, ":"); comp; comp = strtok(NULL, ":")) {
+            if (*comp != '\0') {
+                full = JS_smprintf("%s/%s", comp, name);
+                if (!full) {
+                    JS_ReportOutOfMemory(cx);
+                    ok = JS_FALSE;
+                    break;
+                }
+            } else {
+                full = (char *)name;
+            }
+            found = (access(full, X_OK) == 0);
+            if (*comp != '\0')
+                free(full);
+            if (found) {
+                fun = JS_DefineFunction(cx, obj, name, Exec, 0, JSPROP_ENUMERATE);
+                ok = (fun != NULL);
+                if (ok)
+                    *objp = obj;
+                break;
+            }
+        }
+        JS_free(cx, path);
+        return ok;
+    }
+#else
+    return JS_TRUE;
+#endif
+}
+
+JSClass global_class = {
+    "global", JSCLASS_NEW_RESOLVE,
+    JS_PropertyStub,  JS_PropertyStub,
+    JS_PropertyStub,  JS_PropertyStub,
+    global_enumerate, (JSResolveOp) global_resolve,
+    JS_ConvertStub,   JS_FinalizeStub
+};
+
+SkJS::SkJS(void* hwnd) : SkOSWindow(hwnd) {
+    if ((fRuntime = JS_NewRuntime(0x100000)) == NULL) {
+        SkASSERT(0);
+        return;
+    }
+    if ((fContext = JS_NewContext(fRuntime, 0x1000)) == NULL) {
+        SkASSERT(0);
+        return;
+    }
+    ;
+    if ((fGlobal = JS_NewObject(fContext, &global_class, NULL, NULL)) == NULL) {
+        SkASSERT(0);
+        return;
+    }
+    if (JS_InitStandardClasses(fContext, fGlobal) == NULL) {
+        SkASSERT(0);
+        return;
+    }
+    setConfig(SkBitmap::kARGB32_Config);
+    updateSize();
+    setVisibleP(true);
+    InitializeDisplayables(getBitmap(), fContext, fGlobal, NULL);
+}
+
+SkJS::~SkJS() {
+    DisposeDisplayables();
+    JS_DestroyContext(fContext);
+    JS_DestroyRuntime(fRuntime);
+    JS_ShutDown();
+}
+
+SkBool SkJS::EvaluateScript(const char* script, jsval* rVal) {
+    return JS_EvaluateScript(fContext, fGlobal, script, strlen(script),
+        "memory" /* no file name */, 0 /* no line number */, rVal);
+}
+
+SkBool SkJS::ValueToString(jsval value, SkString* string) {
+     JSString* str = JS_ValueToString(fContext, value);
+     if (str == NULL)
+         return false;
+     string->set(JS_GetStringBytes(str));
+     return true;
+}
+
+#ifdef SK_DEBUG
+void SkJS::Test(void* hwnd) {
+    SkJS js(hwnd);
+    jsval val;
+    SkBool success = js.EvaluateScript("22/7", &val);
+    SkASSERT(success);
+    SkString string;
+    success = js.ValueToString(val, &string);
+    SkASSERT(success);
+    SkASSERT(strcmp(string.c_str(), "3.142857142857143") == 0);
+    success = js.EvaluateScript(
+        "var rect = new rectangle();"
+        "rect.left = 4;"
+        "rect.top = 10;"
+        "rect.right = 20;"
+        "rect.bottom = 30;"
+        "rect.width = rect.height + 20;"
+        "rect.draw();"
+        , &val);
+    SkASSERT(success);
+    success = js.ValueToString(val, &string);
+    SkASSERT(success);
+}
+#endifASSERT(success);
+
diff --git a/src/xml/SkJSDisplayable.cpp b/src/xml/SkJSDisplayable.cpp
new file mode 100644
index 0000000..d52a7c9
--- /dev/null
+++ b/src/xml/SkJSDisplayable.cpp
@@ -0,0 +1,472 @@
+/* libs/graphics/xml/SkJSDisplayable.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <jsapi.h>
+#include "SkJS.h"
+#include "SkDisplayType.h"
+//#include "SkAnimateColor.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimateSet.h"
+//#include "SkAnimateTransform.h"
+#include "SkCanvas.h"
+//#include "SkDimensions.h"
+#include "SkDisplayAdd.h"
+#include "SkDisplayApply.h"
+//#include "SkDisplayBefore.h"
+#include "SkDisplayEvent.h"
+//#include "SkDisplayFocus.h"
+#include "SkDisplayInclude.h"
+#include "SkDisplayPost.h"
+#include "SkDisplayRandom.h"
+#include "SkDraw3D.h"
+#include "SkDrawBitmap.h"
+#include "SkDrawClip.h"
+#include "SkDrawDash.h"
+#include "SkDrawDiscrete.h"
+#include "SkDrawEmboss.h"
+//#include "SkDrawFont.h"
+#include "SkDrawFull.h"
+#include "SkDrawGradient.h"
+#include "SkDrawLine.h"
+//#include "SkDrawMaskFilter.h"
+#include "SkDrawMatrix.h"
+#include "SkDrawOval.h"
+#include "SkDrawPaint.h"
+#include "SkDrawPath.h"
+#include "SkDrawPoint.h"
+// #include "SkDrawStroke.h"
+#include "SkDrawText.h"
+#include "SkDrawTo.h"
+//#include "SkDrawTransferMode.h"
+#include "SkDrawTransparentShader.h"
+//#include "SkDrawUse.h"
+#include "SkMatrixParts.h"
+#include "SkPathParts.h"
+#include "SkPostParts.h"
+#include "SkScript.h"
+#include "SkSnapshot.h"
+#include "SkTextOnPath.h"
+#include "SkTextToPath.h"
+
+
+class SkJSDisplayable {
+public:
+    SkJSDisplayable() : fDisplayable(NULL) {}
+    ~SkJSDisplayable() { delete fDisplayable; }
+    static void Destructor(JSContext *cx, JSObject *obj);
+    static JSBool GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
+    static JSBool SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
+    static SkCanvas* gCanvas;
+    static SkPaint* gPaint;
+    static JSBool Draw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
+    SkDisplayable* fDisplayable;
+};
+
+SkCanvas* SkJSDisplayable::gCanvas;
+SkPaint* SkJSDisplayable::gPaint;
+
+JSBool SkJSDisplayable::Draw(JSContext *cx, JSObject *obj, uintN argc,
+                                    jsval *argv, jsval *rval)
+{
+    SkJSDisplayable *p = (SkJSDisplayable*) JS_GetPrivate(cx, obj);
+    SkASSERT(p->fDisplayable->isDrawable());
+    SkDrawable* drawable = (SkDrawable*) p->fDisplayable;
+    SkAnimateMaker maker(NULL, gCanvas, gPaint);
+    drawable->draw(maker);
+    return JS_TRUE;
+}
+
+
+JSFunctionSpec SkJSDisplayable_methods[] = 
+{
+    { "draw", SkJSDisplayable::Draw, 1, 0, 0 },
+    { 0 }
+};
+
+static JSPropertySpec* gDisplayableProperties[kNumberOfTypes];
+static JSClass gDisplayableClasses[kNumberOfTypes];
+
+#define JS_INIT(_prefix, _class) \
+static JSBool _class##Constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { \
+    SkJSDisplayable* jsDisplayable = new SkJSDisplayable(); \
+    jsDisplayable->fDisplayable = new _prefix##_class(); \
+    JS_SetPrivate(cx, obj, (void*) jsDisplayable); \
+    return JS_TRUE; \
+} \
+    \
+static JSObject* _class##Init(JSContext *cx, JSObject *obj, JSObject *proto) { \
+    JSObject *newProtoObj = JS_InitClass(cx, obj, proto, &gDisplayableClasses[SkType_##_class], \
+        _class##Constructor, 0, \
+        NULL, SkJSDisplayable_methods , \
+        NULL, NULL); \
+    JS_DefineProperties(cx, newProtoObj, gDisplayableProperties[SkType_##_class]); \
+    return newProtoObj; \
+}
+
+JS_INIT(Sk, Add)
+JS_INIT(Sk, AddCircle)
+JS_INIT(Sk, AddOval)
+JS_INIT(Sk, AddPath)
+JS_INIT(Sk, AddRectangle)
+JS_INIT(Sk, AddRoundRect)
+//JS_INIT(Sk, After)
+JS_INIT(Sk, Apply)
+// JS_INIT(Sk, Animate)
+//JS_INIT(Sk, AnimateColor)
+JS_INIT(Sk, AnimateField)
+//JS_INIT(Sk, AnimateRotate)
+//JS_INIT(Sk, AnimateScale)
+//JS_INIT(Sk, AnimateTranslate)
+JS_INIT(SkDraw, Bitmap)
+JS_INIT(Sk, BaseBitmap)
+//JS_INIT(Sk, Before)
+JS_INIT(SkDraw, BitmapShader)
+JS_INIT(SkDraw, Blur)
+JS_INIT(SkDraw, Clip)
+JS_INIT(SkDraw, Color)
+JS_INIT(Sk, CubicTo)
+JS_INIT(Sk, Dash)
+JS_INIT(Sk, Data)
+//JS_INIT(Sk, Dimensions)
+JS_INIT(Sk, Discrete)
+JS_INIT(Sk, DrawTo)
+JS_INIT(SkDraw, Emboss)
+JS_INIT(SkDisplay, Event)
+// JS_INIT(SkDraw, Font)
+// JS_INIT(Sk, Focus)
+JS_INIT(Sk, Image)
+JS_INIT(Sk, Include)
+// JS_INIT(Sk, Input)
+JS_INIT(Sk, Line)
+JS_INIT(Sk, LinearGradient)
+JS_INIT(Sk, LineTo)
+JS_INIT(SkDraw, Matrix)
+JS_INIT(Sk, Move)
+JS_INIT(Sk, MoveTo)
+JS_INIT(Sk, Oval)
+JS_INIT(SkDraw, Path)
+JS_INIT(SkDraw, Paint)
+JS_INIT(Sk, DrawPoint)
+JS_INIT(Sk, PolyToPoly)
+JS_INIT(Sk, Polygon)
+JS_INIT(Sk, Polyline)
+JS_INIT(Sk, Post)
+JS_INIT(Sk, QuadTo)
+JS_INIT(Sk, RadialGradient)
+JS_INIT(SkDisplay, Random)
+JS_INIT(Sk, RectToRect)
+JS_INIT(Sk, Rectangle)
+JS_INIT(Sk, Remove)
+JS_INIT(Sk, Replace)
+JS_INIT(Sk, Rotate)
+JS_INIT(Sk, RoundRect)
+JS_INIT(Sk, Scale)
+JS_INIT(Sk, Set)
+JS_INIT(Sk, Skew)
+// JS_INIT(Sk, 3D_Camera)
+// JS_INIT(Sk, 3D_Patch)
+#ifdef SK_SUPPORT_IMAGE_ENCODE
+JS_INIT(Sk, Snapshot)
+#endif
+// JS_INIT(SkDraw, Stroke)
+JS_INIT(Sk, Text)
+JS_INIT(Sk, TextOnPath)
+JS_INIT(Sk, TextToPath)
+JS_INIT(Sk, Translate)
+//JS_INIT(Sk, Use)
+
+#if SK_USE_CONDENSED_INFO == 0
+static void GenerateTables() {
+    for (int index = 0; index < kTypeNamesSize; index++) {
+        int infoCount;
+        SkDisplayTypes type = gTypeNames[index].fType;
+        const SkMemberInfo* info = SkDisplayType::GetMembers(NULL /* fMaker */, type, &infoCount);
+        if (info == NULL)
+            continue;
+        gDisplayableProperties[type] = new JSPropertySpec[infoCount + 1];
+        JSPropertySpec* propertySpec = gDisplayableProperties[type];
+        memset(propertySpec, 0, sizeof (JSPropertySpec) * (infoCount + 1));
+        for (int inner = 0; inner < infoCount; inner++) {
+            if (info[inner].fType == SkType_BaseClassInfo)
+                continue;
+            propertySpec[inner].name = info[inner].fName;
+            propertySpec[inner].tinyid = inner;
+            propertySpec[inner].flags = JSPROP_ENUMERATE;
+        }
+        gDisplayableClasses[type].name = gTypeNames[index].fName;
+        gDisplayableClasses[type].flags = JSCLASS_HAS_PRIVATE;
+        gDisplayableClasses[type].addProperty = JS_PropertyStub;
+        gDisplayableClasses[type].delProperty = JS_PropertyStub;
+        gDisplayableClasses[type].getProperty = SkJSDisplayable::GetProperty;
+        gDisplayableClasses[type].setProperty = SkJSDisplayable::SetProperty;
+        gDisplayableClasses[type].enumerate = JS_EnumerateStub;
+        gDisplayableClasses[type].resolve = JS_ResolveStub;
+        gDisplayableClasses[type].convert = JS_ConvertStub;
+        gDisplayableClasses[type].finalize = SkJSDisplayable::Destructor;
+    }
+}
+#endif
+
+void SkJSDisplayable::Destructor(JSContext *cx, JSObject *obj) {
+    delete (SkJSDisplayable*) JS_GetPrivate(cx, obj);
+}
+
+JSBool SkJSDisplayable::GetProperty(JSContext *cx, JSObject *obj, jsval id,
+                                 jsval *vp)
+{
+    if (JSVAL_IS_INT(id) == 0)
+        return JS_TRUE; 
+    SkJSDisplayable *p = (SkJSDisplayable *) JS_GetPrivate(cx, obj);
+    SkDisplayable* displayable = p->fDisplayable;
+    SkDisplayTypes displayableType = displayable->getType();
+    int members;
+    const SkMemberInfo* info = SkDisplayType::GetMembers(NULL /* fMaker */, displayableType, &members);
+    int idIndex = JSVAL_TO_INT(id);
+    SkASSERT(idIndex >= 0 && idIndex < members);
+    info = &info[idIndex];
+    SkDisplayTypes infoType = (SkDisplayTypes) info->fType;
+    SkScalar scalar = 0;
+    S32 s32 = 0;
+    SkString* string= NULL;
+    JSString *str;
+    if (infoType == SkType_MemberProperty) {
+        infoType = info->propertyType();
+        switch (infoType) {
+            case SkType_Scalar: {
+                SkScriptValue scriptValue;
+                bool success = displayable->getProperty(info->propertyIndex(), &scriptValue);
+                SkASSERT(scriptValue.fType == SkType_Scalar);
+                scalar = scriptValue.fOperand.fScalar;
+                } break;
+            default:
+                SkASSERT(0); // !!! unimplemented
+        }
+    } else {
+        SkASSERT(info->fCount == 1);
+        switch (infoType) {
+            case SkType_Boolean:
+            case SkType_Color:
+            case SkType_S32:
+                s32 = *(S32*) info->memberData(displayable);
+                break;
+            case SkType_String:
+                info->getString(displayable, &string);
+                break;
+            case SkType_Scalar:
+                SkOperand operand;
+                info->getValue(displayable, &operand, 1);
+                scalar = operand.fScalar;
+                break;
+            default:
+                SkASSERT(0); // !!! unimplemented
+        }
+    }
+    switch (infoType) {
+        case SkType_Boolean:
+            *vp = BOOLEAN_TO_JSVAL(s32);
+            break;
+        case SkType_Color:
+        case SkType_S32:
+            *vp = INT_TO_JSVAL(s32);
+            break;
+        case SkType_Scalar:
+            if (SkScalarFraction(scalar) == 0)
+                *vp = INT_TO_JSVAL(SkScalarFloor(scalar));
+            else
+#ifdef SK_SCALAR_IS_FLOAT
+            *vp = DOUBLE_TO_JSVAL(scalar);
+#else
+            *vp = DOUBLE_TO_JSVAL(scalar / 65536.0f );
+#endif
+            break;
+        case SkType_String:
+            str = JS_NewStringCopyN(cx, string->c_str(), string->size());
+            *vp = STRING_TO_JSVAL(str);
+            break;
+        default:
+            SkASSERT(0); // !!! unimplemented
+    }
+    return JS_TRUE;
+}
+
+JSBool SkJSDisplayable::SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) {
+    if (JSVAL_IS_INT(id) == 0)
+        return JS_TRUE; 
+    SkJSDisplayable *p = (SkJSDisplayable *) JS_GetPrivate(cx, obj);
+    SkDisplayable* displayable = p->fDisplayable;
+    SkDisplayTypes displayableType = displayable->getType();
+    int members;
+    const SkMemberInfo* info = SkDisplayType::GetMembers(NULL /* fMaker */, displayableType, &members);
+    int idIndex = JSVAL_TO_INT(id);
+    SkASSERT(idIndex >= 0 && idIndex < members);
+    info = &info[idIndex];
+    SkDisplayTypes infoType = info->getType();
+    SkScalar scalar = 0;
+    S32 s32 = 0;
+    SkString string;
+    JSString* str;
+    jsval value = *vp;
+    switch (infoType) {
+        case SkType_Boolean:
+            s32 = JSVAL_TO_BOOLEAN(value);
+            break;
+        case SkType_Color:
+        case SkType_S32:
+            s32 = JSVAL_TO_INT(value);
+            break;
+        case SkType_Scalar:
+            if (JSVAL_IS_INT(value))
+                scalar = SkIntToScalar(JSVAL_TO_INT(value));
+            else {
+                SkASSERT(JSVAL_IS_DOUBLE(value));
+#ifdef SK_SCALAR_IS_FLOAT
+                scalar = (float) *(double*) JSVAL_TO_DOUBLE(value);
+#else
+                scalar = (SkFixed)  (*(double*)JSVAL_TO_DOUBLE(value) * 65536.0);
+#endif
+            }
+            break;
+        case SkType_String:
+            str = JS_ValueToString(cx, value);
+            string.set(JS_GetStringBytes(str));
+            break;
+        default:
+            SkASSERT(0); // !!! unimplemented
+    }
+    if (info->fType == SkType_MemberProperty) {
+        switch (infoType) {
+            case SkType_Scalar: {
+                SkScriptValue scriptValue;
+                scriptValue.fType = SkType_Scalar;
+                scriptValue.fOperand.fScalar = scalar;
+                displayable->setProperty(-1 - (int) info->fOffset, scriptValue);
+                } break;
+            default:
+                SkASSERT(0); // !!! unimplemented
+        }
+    } else {
+        SkASSERT(info->fCount == 1);
+        switch (infoType) {
+            case SkType_Boolean:
+            case SkType_Color:
+            case SkType_S32:
+                s32 = *(S32*) ((const char*) displayable + info->fOffset);
+                break;
+            case SkType_String:
+                info->setString(displayable, &string);
+                break;
+            case SkType_Scalar:
+                SkOperand operand;
+                operand.fScalar = scalar;
+                info->setValue(displayable, &operand, 1);
+                break;
+            default:
+                SkASSERT(0); // !!! unimplemented
+        }
+    }
+    return JS_TRUE;
+}
+
+void SkJS::InitializeDisplayables(const SkBitmap& bitmap, JSContext *cx, JSObject *obj, JSObject *proto) {
+    SkJSDisplayable::gCanvas = new SkCanvas(bitmap);
+    SkJSDisplayable::gPaint = new SkPaint();
+#if SK_USE_CONDENSED_INFO == 0
+    GenerateTables();
+#else
+    SkASSERT(0); // !!! compressed version hasn't been implemented
+#endif
+    AddInit(cx, obj, proto);
+    AddCircleInit(cx, obj, proto);
+    AddOvalInit(cx, obj, proto);
+    AddPathInit(cx, obj, proto);
+    AddRectangleInit(cx, obj, proto);
+    AddRoundRectInit(cx, obj, proto);
+//  AfterInit(cx, obj, proto);
+    ApplyInit(cx, obj, proto);
+    // AnimateInit(cx, obj, proto);
+//  AnimateColorInit(cx, obj, proto);
+    AnimateFieldInit(cx, obj, proto);
+//  AnimateRotateInit(cx, obj, proto);
+//  AnimateScaleInit(cx, obj, proto);
+//  AnimateTranslateInit(cx, obj, proto);
+    BitmapInit(cx, obj, proto);
+//  BaseBitmapInit(cx, obj, proto);
+//  BeforeInit(cx, obj, proto);
+    BitmapShaderInit(cx, obj, proto);
+    BlurInit(cx, obj, proto);
+    ClipInit(cx, obj, proto);
+    ColorInit(cx, obj, proto);
+    CubicToInit(cx, obj, proto);
+    DashInit(cx, obj, proto);
+    DataInit(cx, obj, proto);
+//  DimensionsInit(cx, obj, proto);
+    DiscreteInit(cx, obj, proto);
+    DrawToInit(cx, obj, proto);
+    EmbossInit(cx, obj, proto);
+    EventInit(cx, obj, proto);
+//  FontInit(cx, obj, proto);
+//  FocusInit(cx, obj, proto);
+    ImageInit(cx, obj, proto);
+    IncludeInit(cx, obj, proto);
+//  InputInit(cx, obj, proto);
+    LineInit(cx, obj, proto);
+    LinearGradientInit(cx, obj, proto);
+    LineToInit(cx, obj, proto);
+    MatrixInit(cx, obj, proto);
+    MoveInit(cx, obj, proto);
+    MoveToInit(cx, obj, proto);
+    OvalInit(cx, obj, proto);
+    PathInit(cx, obj, proto);
+    PaintInit(cx, obj, proto);
+    DrawPointInit(cx, obj, proto);
+    PolyToPolyInit(cx, obj, proto);
+    PolygonInit(cx, obj, proto);
+    PolylineInit(cx, obj, proto);
+    PostInit(cx, obj, proto);
+    QuadToInit(cx, obj, proto);
+    RadialGradientInit(cx, obj, proto);
+    RandomInit(cx, obj, proto);
+    RectToRectInit(cx, obj, proto);
+    RectangleInit(cx, obj, proto);
+    RemoveInit(cx, obj, proto);
+    ReplaceInit(cx, obj, proto);
+    RotateInit(cx, obj, proto);
+    RoundRectInit(cx, obj, proto);
+    ScaleInit(cx, obj, proto);
+    SetInit(cx, obj, proto);
+    SkewInit(cx, obj, proto);
+    // 3D_CameraInit(cx, obj, proto);
+    // 3D_PatchInit(cx, obj, proto);
+    #ifdef SK_SUPPORT_IMAGE_ENCODE
+    SnapshotInit(cx, obj, proto);
+    #endif
+//  StrokeInit(cx, obj, proto);
+    TextInit(cx, obj, proto);
+    TextOnPathInit(cx, obj, proto);
+    TextToPathInit(cx, obj, proto);
+    TranslateInit(cx, obj, proto);
+//  UseInit(cx, obj, proto);
+}
+
+void SkJS::DisposeDisplayables() {
+    delete SkJSDisplayable::gPaint;
+    delete SkJSDisplayable::gCanvas;
+    for (int index = 0; index < kTypeNamesSize; index++) {
+        SkDisplayTypes type = gTypeNames[index].fType;
+        delete[] gDisplayableProperties[type];
+    }
+}
diff --git a/src/xml/SkParse.cpp b/src/xml/SkParse.cpp
new file mode 100644
index 0000000..e740280
--- /dev/null
+++ b/src/xml/SkParse.cpp
@@ -0,0 +1,336 @@
+/* libs/graphics/xml/SkParse.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include "SkParse.h"
+
+static inline bool is_between(int c, int min, int max)
+{
+    return (unsigned)(c - min) <= (unsigned)(max - min);
+}
+
+static inline bool is_ws(int c)
+{
+    return is_between(c, 1, 32);
+}
+
+static inline bool is_digit(int c)
+{
+    return is_between(c, '0', '9');
+}
+
+static inline bool is_sep(int c)
+{
+    return is_ws(c) || c == ',' || c == ';';
+}
+
+static int to_hex(int c)
+{
+    if (is_digit(c))
+        return c - '0';
+
+    c |= 0x20;  // make us lower-case
+    if (is_between(c, 'a', 'f'))
+        return c + 10 - 'a';
+    else
+        return -1;
+}
+
+static inline bool is_hex(int c)
+{
+    return to_hex(c) >= 0;
+}
+
+static const char* skip_ws(const char str[])
+{
+    SkASSERT(str);
+    while (is_ws(*str))
+        str++;
+    return str;
+}
+
+static const char* skip_sep(const char str[])
+{
+    SkASSERT(str);
+    while (is_sep(*str))
+        str++;
+    return str;
+}
+
+int SkParse::Count(const char str[]) 
+{
+    char c;
+    int count = 0;
+    goto skipLeading;
+    do {
+        count++;
+        do {
+            if ((c = *str++) == '\0')
+                goto goHome;
+        } while (is_sep(c) == false);
+skipLeading:
+        do {
+            if ((c = *str++) == '\0')
+                goto goHome;
+        } while (is_sep(c));
+    } while (true);
+goHome:
+    return count;
+}
+
+int SkParse::Count(const char str[], char separator) 
+{
+    char c;
+    int count = 0;
+    goto skipLeading;
+    do {
+        count++;
+        do {
+            if ((c = *str++) == '\0')
+                goto goHome;
+        } while (c != separator);
+skipLeading:
+        do {
+            if ((c = *str++) == '\0')
+                goto goHome;
+        } while (c == separator);
+    } while (true);
+goHome:
+    return count;
+}
+
+const char* SkParse::FindHex(const char str[], uint32_t* value)
+{
+    SkASSERT(str);
+    str = skip_ws(str);
+
+    if (!is_hex(*str))
+        return NULL;
+
+    uint32_t n = 0;
+    int max_digits = 8;
+    int digit;
+
+    while ((digit = to_hex(*str)) >= 0)
+    {
+        if (--max_digits < 0)
+            return NULL;
+        n = (n << 4) | digit;
+        str += 1;
+    }
+
+    if (*str == 0 || is_ws(*str))
+    {
+        if (value)
+            *value = n;
+        return str;
+    }
+    return false;
+}
+
+const char* SkParse::FindS32(const char str[], int32_t* value)
+{
+    SkASSERT(str);
+    str = skip_ws(str);
+
+    int sign = 0;
+    if (*str == '-')
+    {
+        sign = -1;
+        str += 1;
+    }
+
+    if (!is_digit(*str))
+        return NULL;
+
+    int n = 0;
+    while (is_digit(*str))
+    {
+        n = 10*n + *str - '0';
+        str += 1;
+    }
+    if (value)
+        *value = (n ^ sign) - sign;
+    return str;
+}
+
+const char* SkParse::FindMSec(const char str[], SkMSec* value)
+{
+    SkASSERT(str);
+    str = skip_ws(str);
+
+    int sign = 0;
+    if (*str == '-')
+    {
+        sign = -1;
+        str += 1;
+    }
+
+    if (!is_digit(*str))
+        return NULL;
+
+    int n = 0;
+    while (is_digit(*str))
+    {
+        n = 10*n + *str - '0';
+        str += 1;
+    }
+    int remaining10s = 3;
+    if (*str == '.') {
+        str++;
+        while (is_digit(*str))
+        {
+            n = 10*n + *str - '0';
+            str += 1;
+            if (--remaining10s == 0)
+                break;
+        }
+    }
+    while (--remaining10s >= 0)
+        n *= 10;
+    if (value)
+        *value = (n ^ sign) - sign;
+    return str;
+}
+
+const char* SkParse::FindScalar(const char str[], SkScalar* value)
+{
+    SkASSERT(str);
+    str = skip_ws(str);
+
+    int sign = 0;
+    if (*str == '-')
+    {
+        sign = -1;
+        str += 1;
+    }
+
+    if (!is_digit(*str) && *str != '.')
+        return NULL;
+
+    int n = 0;
+    while (is_digit(*str))
+    {
+        n = 10*n + *str - '0';
+        if (n > 0x7FFF)
+            return NULL;
+        str += 1;
+    }
+    n <<= 16;
+
+    if (*str == '.')
+    {
+        static const int gFractions[] = { (1 << 24)  / 10, (1 << 24)  / 100, (1 << 24)  / 1000, 
+            (1 << 24)  / 10000, (1 << 24)  / 100000 };
+        str += 1;
+        int d = 0;
+        const int* fraction = gFractions;
+        const int* end = &fraction[SK_ARRAY_COUNT(gFractions)];
+        while (is_digit(*str) && fraction < end)
+            d += (*str++ - '0') * *fraction++;
+        d += 0x80; // round
+        n += d >> 8;
+    }
+    while (is_digit(*str))
+        str += 1;
+    if (value)
+    {
+        n = (n ^ sign) - sign;  // apply the sign
+        *value = SkFixedToScalar(n);
+    }
+    return str;
+}
+
+const char* SkParse::FindScalars(const char str[], SkScalar value[], int count)
+{
+    SkASSERT(count >= 0);
+
+    if (count > 0)
+    {
+        for (;;)
+        {
+            str = SkParse::FindScalar(str, value);
+            if (--count == 0 || str == NULL)
+                break;
+
+            // keep going
+            str = skip_sep(str);
+            if (value)
+                value += 1;
+        }
+    }
+    return str;
+}
+
+static bool lookup_str(const char str[], const char** table, int count)
+{
+    while (--count >= 0)
+        if (!strcmp(str, table[count]))
+            return true;
+    return false;
+}
+
+bool SkParse::FindBool(const char str[], bool* value)
+{
+    static const char* gYes[] = { "yes", "1", "true" };
+    static const char* gNo[] = { "no", "0", "false" };
+
+    if (lookup_str(str, gYes, SK_ARRAY_COUNT(gYes)))
+    {
+        if (value) *value = true;
+        return true;
+    }
+    else if (lookup_str(str, gNo, SK_ARRAY_COUNT(gNo)))
+    {
+        if (value) *value = false;
+        return true;
+    }
+    return false;
+}
+
+int SkParse::FindList(const char target[], const char list[])
+{
+    size_t  len = strlen(target);
+    int     index = 0;
+
+    for (;;)
+    {
+        const char* end = strchr(list, ',');
+        size_t      entryLen;
+
+        if (end == NULL) // last entry
+            entryLen = strlen(list);
+        else
+            entryLen = end - list;
+
+        if (entryLen == len && memcmp(target, list, len) == 0)
+            return index;
+        if (end == NULL)
+            break;
+
+        list = end + 1; // skip the ','
+        index += 1;
+    }
+    return -1;
+}
+
+#ifdef SK_SUPPORT_UNITTEST
+void SkParse::UnitTest() 
+{
+    // !!! additional parse tests go here
+    SkParse::TestColor();
+}
+#endif
diff --git a/src/xml/SkParseColor.cpp b/src/xml/SkParseColor.cpp
new file mode 100644
index 0000000..eca2e38
--- /dev/null
+++ b/src/xml/SkParseColor.cpp
@@ -0,0 +1,546 @@
+/* libs/graphics/xml/SkParseColor.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include "SkParse.h"
+
+#ifdef SK_DEBUG
+#include "SkString.h"
+
+    // compress names 6 chars per long (packed 5 bits/char )
+        // note: little advantage to splitting chars across longs, since 3 longs at 2 unused bits each 
+        // allow for one additional split char (vs. the 18 unsplit chars in the three longs)
+    // use extra two bits to represent:
+        // 00 : final 6 (or fewer) chars (if 'a' is 0x01, zero could have special meaning)
+        // 01 : not final 6 chars
+        // 10 : color
+        // 11 : unused, except as debugging sentinal? (could be -1 for easier test)
+    // !!! the bit to end the word (last) is at the low bit for binary search
+    // lookup first character in offset for quick start
+        // offset is 27-entry table of bytes(?) that trims linear search to at most 21 entries ('d')
+    // shift match into long; set bit 30 if it all doesn't fit
+    // while longs don't match, march forward
+        // if they do match, and bit 30 is set, advance match, clearing bit 30 if
+        // final chars, and advance to next test
+        // if they do match, and bit 30 is clear, get next long (color) and return it
+    // stop at lookup of first char + 1
+static const struct SkNameRGB {
+    const char* name;
+    int rgb;
+} colorNames[] = {
+    { "aliceblue",            0xF0F8FF },
+    { "antiquewhite",         0xFAEBD7 },
+    { "aqua",                 0x00FFFF },
+    { "aquamarine",           0x7FFFD4 },
+    { "azure",                0xF0FFFF },
+    { "beige",                0xF5F5DC },
+    { "bisque",               0xFFE4C4 },
+    { "black",                0x000000 },
+    { "blanchedalmond",       0xFFEBCD },
+    { "blue",                 0x0000FF },
+    { "blueviolet",           0x8A2BE2 },
+    { "brown",                0xA52A2A },
+    { "burlywood",            0xDEB887 },
+    { "cadetblue",            0x5F9EA0 },
+    { "chartreuse",           0x7FFF00 },
+    { "chocolate",            0xD2691E },
+    { "coral",                0xFF7F50 },
+    { "cornflowerblue",       0x6495ED },
+    { "cornsilk",             0xFFF8DC },
+    { "crimson",              0xDC143C },
+    { "cyan",                 0x00FFFF },
+    { "darkblue",             0x00008B },
+    { "darkcyan",             0x008B8B },
+    { "darkgoldenrod",        0xB8860B },
+    { "darkgray",             0xA9A9A9 },
+    { "darkgreen",            0x006400 },
+    { "darkkhaki",            0xBDB76B },
+    { "darkmagenta",          0x8B008B },
+    { "darkolivegreen",       0x556B2F },
+    { "darkorange",           0xFF8C00 },
+    { "darkorchid",           0x9932CC },
+    { "darkred",              0x8B0000 },
+    { "darksalmon",           0xE9967A },
+    { "darkseagreen",         0x8FBC8F },
+    { "darkslateblue",        0x483D8B },
+    { "darkslategray",        0x2F4F4F },
+    { "darkturquoise",        0x00CED1 },
+    { "darkviolet",           0x9400D3 },
+    { "deeppink",             0xFF1493 },
+    { "deepskyblue",          0x00BFFF },
+    { "dimgray",              0x696969 },
+    { "dodgerblue",           0x1E90FF },
+    { "firebrick",            0xB22222 },
+    { "floralwhite",          0xFFFAF0 },
+    { "forestgreen",          0x228B22 },
+    { "fuchsia",              0xFF00FF },
+    { "gainsboro",            0xDCDCDC },
+    { "ghostwhite",           0xF8F8FF },
+    { "gold",                 0xFFD700 },
+    { "goldenrod",            0xDAA520 },
+    { "gray",                 0x808080 },
+    { "green",                0x008000 },
+    { "greenyellow",          0xADFF2F },
+    { "honeydew",             0xF0FFF0 },
+    { "hotpink",              0xFF69B4 },
+    { "indianred",            0xCD5C5C },
+    { "indigo",               0x4B0082 },
+    { "ivory",                0xFFFFF0 },
+    { "khaki",                0xF0E68C },
+    { "lavender",             0xE6E6FA },
+    { "lavenderblush",        0xFFF0F5 },
+    { "lawngreen",            0x7CFC00 },
+    { "lemonchiffon",         0xFFFACD },
+    { "lightblue",            0xADD8E6 },
+    { "lightcoral",           0xF08080 },
+    { "lightcyan",            0xE0FFFF },
+    { "lightgoldenrodyellow", 0xFAFAD2 },
+    { "lightgreen",           0x90EE90 },
+    { "lightgrey",            0xD3D3D3 },
+    { "lightpink",            0xFFB6C1 },
+    { "lightsalmon",          0xFFA07A },
+    { "lightseagreen",        0x20B2AA },
+    { "lightskyblue",         0x87CEFA },
+    { "lightslategray",       0x778899 },
+    { "lightsteelblue",       0xB0C4DE },
+    { "lightyellow",          0xFFFFE0 },
+    { "lime",                 0x00FF00 },
+    { "limegreen",            0x32CD32 },
+    { "linen",                0xFAF0E6 },
+    { "magenta",              0xFF00FF },
+    { "maroon",               0x800000 },
+    { "mediumaquamarine",     0x66CDAA },
+    { "mediumblue",           0x0000CD },
+    { "mediumorchid",         0xBA55D3 },
+    { "mediumpurple",         0x9370DB },
+    { "mediumseagreen",       0x3CB371 },
+    { "mediumslateblue",      0x7B68EE },
+    { "mediumspringgreen",    0x00FA9A },
+    { "mediumturquoise",      0x48D1CC },
+    { "mediumvioletred",      0xC71585 },
+    { "midnightblue",         0x191970 },
+    { "mintcream",            0xF5FFFA },
+    { "mistyrose",            0xFFE4E1 },
+    { "moccasin",             0xFFE4B5 },
+    { "navajowhite",          0xFFDEAD },
+    { "navy",                 0x000080 },
+    { "oldlace",              0xFDF5E6 },
+    { "olive",                0x808000 },
+    { "olivedrab",            0x6B8E23 },
+    { "orange",               0xFFA500 },
+    { "orangered",            0xFF4500 },
+    { "orchid",               0xDA70D6 },
+    { "palegoldenrod",        0xEEE8AA },
+    { "palegreen",            0x98FB98 },
+    { "paleturquoise",        0xAFEEEE },
+    { "palevioletred",        0xDB7093 },
+    { "papayawhip",           0xFFEFD5 },
+    { "peachpuff",            0xFFDAB9 },
+    { "peru",                 0xCD853F },
+    { "pink",                 0xFFC0CB },
+    { "plum",                 0xDDA0DD },
+    { "powderblue",           0xB0E0E6 },
+    { "purple",               0x800080 },
+    { "red",                  0xFF0000 },
+    { "rosybrown",            0xBC8F8F },
+    { "royalblue",            0x4169E1 },
+    { "saddlebrown",          0x8B4513 },
+    { "salmon",               0xFA8072 },
+    { "sandybrown",           0xF4A460 },
+    { "seagreen",             0x2E8B57 },
+    { "seashell",             0xFFF5EE },
+    { "sienna",               0xA0522D },
+    { "silver",               0xC0C0C0 },
+    { "skyblue",              0x87CEEB },
+    { "slateblue",            0x6A5ACD },
+    { "slategray",            0x708090 },
+    { "snow",                 0xFFFAFA },
+    { "springgreen",          0x00FF7F },
+    { "steelblue",            0x4682B4 },
+    { "tan",                  0xD2B48C },
+    { "teal",                 0x008080 },
+    { "thistle",              0xD8BFD8 },
+    { "tomato",               0xFF6347 },
+    { "turquoise",            0x40E0D0 },
+    { "violet",               0xEE82EE },
+    { "wheat",                0xF5DEB3 },
+    { "white",                0xFFFFFF },
+    { "whitesmoke",           0xF5F5F5 },
+    { "yellow",               0xFFFF00 },
+    { "yellowgreen",          0x9ACD32 }
+};
+
+int colorNamesSize = sizeof(colorNames) / sizeof(colorNames[0]);
+
+static void CreateTable() {
+    SkString comment;
+    size_t originalSize = 0;
+    int replacement = 0;
+    for (int index = 0; index < colorNamesSize; index++) {
+        SkNameRGB nameRGB =  colorNames[index];
+        const char* name = nameRGB.name;
+        size_t len = strlen(name);
+        originalSize += len + 9;
+        bool first = true;
+        bool last = false;
+        do {
+            int compressed = 0;
+            const char* start = name;
+            for (int chIndex = 0; chIndex < 6; chIndex++) {
+                compressed <<= 5;
+                compressed |= *name ? *name++ - 'a' + 1 : 0 ;
+            }
+            replacement += sizeof(int);
+            compressed <<= 1;
+            compressed |= 1;
+            if (first) {
+                compressed |= 0x80000000;
+                first = false;
+            }
+            if (len <= 6) { // last
+                compressed &= ~1;
+                last = true;
+            }
+            len -= 6;
+            SkDebugf("0x%08x, ", compressed);
+            comment.append(start, name - start);
+        } while (last == false);
+        replacement += sizeof(int);
+        SkDebugf("0x%08x, ", nameRGB.rgb);
+        SkDebugf("// %s\n", comment.c_str());
+        comment.reset();
+    }
+    SkDebugf("// original = %d : replacement = %d\n", originalSize, replacement);
+    SkASSERT(0); // always stop after creating table
+}
+
+#endif
+
+static const unsigned int gColorNames[] = {
+0x85891945, 0x32a50000, 0x00f0f8ff, // aliceblue
+0x85d44c6b, 0x16e84d0a, 0x00faebd7, // antiquewhite
+0x86350800, 0x0000ffff, // aqua
+0x86350b43, 0x492e2800, 0x007fffd4, // aquamarine
+0x87559140, 0x00f0ffff, // azure
+0x88a93940, 0x00f5f5dc, // beige
+0x89338d4a, 0x00ffe4c4, // bisque
+0x89811ac0, 0x00000000, // black
+0x898170d1, 0x1481635f, 0x38800000, 0x00ffebcd, // blanchedalmond
+0x89952800, 0x000000ff, // blue
+0x89952d93, 0x3d85a000, 0x008a2be2, // blueviolet
+0x8a4fbb80, 0x00a52a2a, // brown
+0x8ab2666f, 0x3de40000, 0x00deb887, // burlywood
+0x8c242d05, 0x32a50000, 0x005f9ea0, // cadetblue
+0x8d019525, 0x16b32800, 0x007fff00, // chartreuse
+0x8d0f1bd9, 0x06850000, 0x00d2691e, // chocolate
+0x8df20b00, 0x00ff7f50, // coral
+0x8df27199, 0x3ee59099, 0x54a00000, 0x006495ed, // cornflowerblue
+0x8df274d3, 0x31600000, 0x00fff8dc, // cornsilk
+0x8e496cdf, 0x38000000, 0x00dc143c, // crimson
+0x8f217000, 0x0000ffff, // cyan
+0x90325899, 0x54a00000, 0x0000008b, // darkblue
+0x903258f3, 0x05c00000, 0x00008b8b, // darkcyan
+0x903259df, 0x3085749f, 0x10000000, 0x00b8860b, // darkgoldenrod
+0x903259e5, 0x07200000, 0x00a9a9a9, // darkgray
+0x903259e5, 0x14ae0000, 0x00006400, // darkgreen
+0x90325ad1, 0x05690000, 0x00bdb76b, // darkkhaki
+0x90325b43, 0x1caea040, 0x008b008b, // darkmagenta
+0x90325bd9, 0x26c53c8b, 0x15c00000, 0x00556b2f, // darkolivegreen
+0x90325be5, 0x05c72800, 0x00ff8c00, // darkorange
+0x90325be5, 0x0d092000, 0x009932cc, // darkorchid
+0x90325c8b, 0x10000000, 0x008b0000, // darkred
+0x90325cc3, 0x31af7000, 0x00e9967a, // darksalmon
+0x90325ccb, 0x04f2295c, 0x008fbc8f, // darkseagreen
+0x90325cd9, 0x0685132b, 0x14000000, 0x00483d8b, // darkslateblue
+0x90325cd9, 0x06853c83, 0x64000000, 0x002f4f4f, // darkslategray
+0x90325d2b, 0x4a357a67, 0x14000000, 0x0000ced1, // darkturquoise
+0x90325d93, 0x3d85a000, 0x009400d3, // darkviolet
+0x90a58413, 0x39600000, 0x00ff1493, // deeppink
+0x90a584d7, 0x644ca940, 0x0000bfff, // deepskyblue
+0x912d3c83, 0x64000000, 0x00696969, // dimgray
+0x91e43965, 0x09952800, 0x001e90ff, // dodgerblue
+0x993228a5, 0x246b0000, 0x00b22222, // firebrick
+0x998f9059, 0x5d09a140, 0x00fffaf0, // floralwhite
+0x99f22ce9, 0x1e452b80, 0x00228b22, // forestgreen
+0x9aa344d3, 0x04000000, 0x00ff00ff, // fuchsia
+0x9c2974c5, 0x3e4f0000, 0x00dcdcdc, // gainsboro
+0x9d0f9d2f, 0x21342800, 0x00f8f8ff, // ghostwhite
+0x9dec2000, 0x00ffd700, // gold
+0x9dec215d, 0x49e40000, 0x00daa520, // goldenrod
+0x9e41c800, 0x00808080, // gray
+0x9e452b80, 0x00008000, // green
+0x9e452bb3, 0x158c7dc0, 0x00adff2f, // greenyellow
+0xa1ee2e49, 0x16e00000, 0x00f0fff0, // honeydew
+0xa1f4825d, 0x2c000000, 0x00ff69b4, // hotpink
+0xa5c4485d, 0x48a40000, 0x00cd5c5c, // indianred
+0xa5c449de, 0x004b0082, // indigo
+0xa6cf9640, 0x00fffff0, // ivory
+0xad015a40, 0x00f0e68c, // khaki
+0xb0362b89, 0x16400000, 0x00e6e6fa, // lavender
+0xb0362b89, 0x16426567, 0x20000000, 0x00fff0f5, // lavenderblush
+0xb03771e5, 0x14ae0000, 0x007cfc00, // lawngreen
+0xb0ad7b87, 0x212633dc, 0x00fffacd, // lemonchiffon
+0xb1274505, 0x32a50000, 0x00add8e6, // lightblue
+0xb1274507, 0x3e416000, 0x00f08080, // lightcoral
+0xb1274507, 0x642e0000, 0x00e0ffff, // lightcyan
+0xb127450f, 0x3d842ba5, 0x3c992b19, 0x3ee00000, 0x00fafad2, // lightgoldenrodyellow
+0xb127450f, 0x48a57000, 0x0090ee90, // lightgreen
+0xb127450f, 0x48b90000, 0x00d3d3d3, // lightgrey
+0xb1274521, 0x25cb0000, 0x00ffb6c1, // lightpink
+0xb1274527, 0x058d7b80, 0x00ffa07a, // lightsalmon
+0xb1274527, 0x1427914b, 0x38000000, 0x0020b2aa, // lightseagreen
+0xb1274527, 0x2f22654a, 0x0087cefa, // lightskyblue
+0xb1274527, 0x303429e5, 0x07200000, 0x00778899, // lightslategray
+0xb1274527, 0x50a56099, 0x54a00000, 0x00b0c4de, // lightsteelblue
+0xb1274533, 0x158c7dc0, 0x00ffffe0, // lightyellow
+0xb12d2800, 0x0000ff00, // lime
+0xb12d29e5, 0x14ae0000, 0x0032cd32, // limegreen
+0xb12e2b80, 0x00faf0e6, // linen
+0xb4272ba9, 0x04000000, 0x00ff00ff, // magenta
+0xb4327bdc, 0x00800000, // maroon
+0xb4a44d5b, 0x06350b43, 0x492e2800, 0x0066cdaa, // mediumaquamarine
+0xb4a44d5b, 0x09952800, 0x000000cd, // mediumblue
+0xb4a44d5b, 0x3e434248, 0x00ba55d3, // mediumorchid
+0xb4a44d5b, 0x42b2830a, 0x009370db, // mediumpurple
+0xb4a44d5b, 0x4ca13c8b, 0x15c00000, 0x003cb371, // mediumseagreen
+0xb4a44d5b, 0x4d81a145, 0x32a50000, 0x007b68ee, // mediumslateblue
+0xb4a44d5b, 0x4e124b8f, 0x1e452b80, 0x0000fa9a, // mediumspringgreen
+0xb4a44d5b, 0x52b28d5f, 0x26650000, 0x0048d1cc, // mediumturquoise
+0xb4a44d5b, 0x592f6169, 0x48a40000, 0x00c71585, // mediumvioletred
+0xb524724f, 0x2282654a, 0x00191970, // midnightblue
+0xb52ea0e5, 0x142d0000, 0x00f5fffa, // mintcream
+0xb533a665, 0x3e650000, 0x00ffe4e1, // mistyrose
+0xb5e31867, 0x25c00000, 0x00ffe4b5, // moccasin
+0xb8360a9f, 0x5d09a140, 0x00ffdead, // navajowhite
+0xb836c800, 0x00000080, // navy
+0xbd846047, 0x14000000, 0x00fdf5e6, // oldlace
+0xbd89b140, 0x00808000, // olive
+0xbd89b149, 0x48220000, 0x006b8e23, // olivedrab
+0xbe4171ca, 0x00ffa500, // orange
+0xbe4171cb, 0x48a40000, 0x00ff4500, // orangered
+0xbe434248, 0x00da70d6, // orchid
+0xc02c29df, 0x3085749f, 0x10000000, 0x00eee8aa, // palegoldenrod
+0xc02c29e5, 0x14ae0000, 0x0098fb98, // palegreen
+0xc02c2d2b, 0x4a357a67, 0x14000000, 0x00afeeee, // paleturquoise
+0xc02c2d93, 0x3d85a48b, 0x10000000, 0x00db7093, // palevioletred
+0xc0300e43, 0x5d098000, 0x00ffefd5, // papayawhip
+0xc0a11a21, 0x54c60000, 0x00ffdab9, // peachpuff
+0xc0b2a800, 0x00cd853f, // peru
+0xc12e5800, 0x00ffc0cb, // pink
+0xc1956800, 0x00dda0dd, // plum
+0xc1f72165, 0x09952800, 0x00b0e0e6, // powderblue
+0xc2b2830a, 0x00800080, // purple
+0xc8a40000, 0x00ff0000, // red
+0xc9f3c8a5, 0x3eee0000, 0x00bc8f8f, // rosybrown
+0xc9f90b05, 0x32a50000, 0x004169e1, // royalblue
+0xcc24230b, 0x0a4fbb80, 0x008b4513, // saddlebrown
+0xcc2c6bdc, 0x00fa8072, // salmon
+0xcc2e2645, 0x49f77000, 0x00f4a460, // sandybrown
+0xcca13c8b, 0x15c00000, 0x002e8b57, // seagreen
+0xcca19a0b, 0x31800000, 0x00fff5ee, // seashell
+0xcd257382, 0x00a0522d, // sienna
+0xcd2cb164, 0x00c0c0c0, // silver
+0xcd79132b, 0x14000000, 0x0087ceeb, // skyblue
+0xcd81a145, 0x32a50000, 0x006a5acd, // slateblue
+0xcd81a14f, 0x48390000, 0x00708090, // slategray
+0xcdcfb800, 0x00fffafa, // snow
+0xce124b8f, 0x1e452b80, 0x0000ff7f, // springgreen
+0xce852b05, 0x32a50000, 0x004682b4, // steelblue
+0xd02e0000, 0x00d2b48c, // tan
+0xd0a16000, 0x00008080, // teal
+0xd1099d19, 0x14000000, 0x00d8bfd8, // thistle
+0xd1ed0d1e, 0x00ff6347, // tomato
+0xd2b28d5f, 0x26650000, 0x0040e0d0, // turquoise
+0xd92f6168, 0x00ee82ee, // violet
+0xdd050d00, 0x00f5deb3, // wheat
+0xdd09a140, 0x00ffffff, // white
+0xdd09a167, 0x35eb2800, 0x00f5f5f5, // whitesmoke
+0xe4ac63ee, 0x00ffff00, // yellow
+0xe4ac63ef, 0x1e452b80, 0x009acd32 // yellowgreen
+}; // original = 2505 : replacement = 1616
+
+
+const char* SkParse::FindNamedColor(const char* name, size_t len, SkColor* color) {
+    const char* namePtr = name;
+    unsigned int sixMatches[4];
+    unsigned int* sixMatchPtr = sixMatches;
+    bool first = true;
+    bool last = false;
+    char ch;
+    do {
+        unsigned int sixMatch = 0;
+        for (int chIndex = 0; chIndex < 6; chIndex++) {
+            sixMatch <<= 5;
+            ch = *namePtr  | 0x20;
+            if (ch < 'a' || ch > 'z')
+                ch = 0;
+            else {
+                ch = ch - 'a' + 1;
+                namePtr++;
+            }
+            sixMatch |= ch ;  // turn 'A' (0x41) into 'a' (0x61);
+        }
+        sixMatch <<= 1;
+        sixMatch |= 1;
+        if (first) {
+            sixMatch |= 0x80000000;
+            first = false;
+        }
+        ch = *namePtr | 0x20;
+        last = ch < 'a' || ch > 'z';
+        if (last) 
+            sixMatch &= ~1;
+        len -= 6;
+        *sixMatchPtr++ = sixMatch;
+    } while (last == false && len > 0);
+    const int colorNameSize = sizeof(gColorNames) / sizeof(unsigned int);
+    int lo = 0;
+    int hi = colorNameSize - 3; // back off to beginning of yellowgreen
+    while (lo <= hi) {
+        int mid = (hi + lo) >> 1;
+        while ((int) gColorNames[mid] >= 0)
+            --mid;
+        sixMatchPtr = sixMatches;
+        while (gColorNames[mid] == *sixMatchPtr) {
+            ++mid;
+            if ((*sixMatchPtr & 1) == 0) { // last
+                *color = gColorNames[mid] | 0xFF000000;
+                return namePtr;
+            }
+            ++sixMatchPtr;
+        }
+        int sixMask = *sixMatchPtr & ~0x80000000;
+        int midMask = gColorNames[mid] & ~0x80000000;
+        if (sixMask > midMask) {
+            lo = mid + 2;   // skip color
+            while ((int) gColorNames[lo] >= 0)
+                ++lo;
+        } else if (hi == mid)
+            return NULL;
+        else
+            hi = mid;
+    }
+    return NULL;
+}
+
+// !!! move to char utilities
+//static int count_separators(const char* str, const char* sep) {
+//  char c;
+//  int separators = 0;
+//  while ((c = *str++) != '\0') {
+//      if (strchr(sep, c) == NULL)
+//          continue;
+//      do {
+//          if ((c = *str++) == '\0')
+//              goto goHome;
+//      } while (strchr(sep, c) != NULL);
+//      separators++;
+//  }
+//goHome:
+//  return separators;
+//}
+
+static inline unsigned nib2byte(unsigned n)
+{
+    SkASSERT((n & ~0xF) == 0);
+    return (n << 4) | n;
+}
+
+const char* SkParse::FindColor(const char* value, SkColor* colorPtr) {
+    unsigned int oldAlpha = SkColorGetA(*colorPtr);
+    if (value[0] == '#') {
+        uint32_t    hex;
+        const char* end = SkParse::FindHex(value + 1, &hex);
+//      SkASSERT(end);
+        if (end == NULL)
+            return end;
+        size_t len = end - value - 1;
+        if (len == 3 || len == 4) {
+            unsigned a = len == 4 ? nib2byte(hex >> 12) : oldAlpha;
+            unsigned r = nib2byte((hex >> 8) & 0xF);
+            unsigned g = nib2byte((hex >> 4) & 0xF);
+            unsigned b = nib2byte(hex & 0xF);
+            *colorPtr = SkColorSetARGB(a, r, g, b);
+            return end;
+        } else if (len == 6 || len == 8) {
+            if (len == 6)
+                hex |= oldAlpha << 24;
+            *colorPtr = hex;
+            return end;
+        } else {
+//          SkASSERT(0);
+            return NULL;
+        }
+//  } else if (strchr(value, ',')) {
+//      SkScalar array[4];
+//      int count = count_separators(value, ",") + 1; // !!! count commas, add 1
+//      SkASSERT(count == 3 || count == 4);
+//      array[0] = SK_Scalar1 * 255;
+//      const char* end = SkParse::FindScalars(value, &array[4 - count], count);
+//      if (end == NULL)
+//          return NULL;
+        // !!! range check for errors?
+//      *colorPtr = SkColorSetARGB(SkScalarRound(array[0]), SkScalarRound(array[1]), 
+//          SkScalarRound(array[2]), SkScalarRound(array[3]));
+//      return end;
+    } else
+        return FindNamedColor(value, strlen(value), colorPtr);
+}
+
+#ifdef SK_DEBUG
+void SkParse::TestColor() {
+    if (false)
+        CreateTable();  // regenerates data table in the output window
+    SkColor result;
+    int index;
+    for (index = 0; index < colorNamesSize; index++) {
+        result = SK_ColorBLACK;
+        SkNameRGB nameRGB = colorNames[index];
+        SkASSERT(FindColor(nameRGB.name, &result) != NULL);
+        SkASSERT(result == (SkColor) (nameRGB.rgb | 0xFF000000));
+    }
+    for (index = 0; index < colorNamesSize; index++) {
+        result = SK_ColorBLACK;
+        SkNameRGB nameRGB = colorNames[index];
+        char bad[24];
+        size_t len = strlen(nameRGB.name);
+        memcpy(bad, nameRGB.name, len);
+        bad[len - 1] -= 1; 
+        SkASSERT(FindColor(bad, &result) == false);
+        bad[len - 1] += 2; 
+        SkASSERT(FindColor(bad, &result) == false);
+    }
+    result = SK_ColorBLACK;
+    SkASSERT(FindColor("lightGrey", &result));
+    SkASSERT(result == 0xffd3d3d3);
+//  SkASSERT(FindColor("12,34,56,78", &result));
+//  SkASSERT(result == ((12 << 24) | (34 << 16) | (56 << 8) | (78 << 0)));
+    result = SK_ColorBLACK;
+    SkASSERT(FindColor("#ABCdef", &result));
+    SkASSERT(result == 0XFFABCdef);
+    SkASSERT(FindColor("#12ABCdef", &result));
+    SkASSERT(result == 0X12ABCdef);
+    result = SK_ColorBLACK;
+    SkASSERT(FindColor("#123", &result));
+    SkASSERT(result == 0Xff112233);
+    SkASSERT(FindColor("#abcd", &result));
+    SkASSERT(result == 0Xaabbccdd);
+    result = SK_ColorBLACK;
+//  SkASSERT(FindColor("71,162,253", &result));
+//  SkASSERT(result == ((0xFF << 24) | (71 << 16) | (162 << 8) | (253 << 0)));
+}
+#endif
+
diff --git a/src/xml/SkXMLParser.cpp b/src/xml/SkXMLParser.cpp
new file mode 100644
index 0000000..da33307
--- /dev/null
+++ b/src/xml/SkXMLParser.cpp
@@ -0,0 +1,95 @@
+/* libs/graphics/xml/SkXMLParser.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include "SkXMLParser.h"
+
+static char const* const gErrorStrings[] = {
+    "empty or missing file ",
+    "unknown element ",
+    "unknown attribute name ",
+    "error in attribute value ",
+    "duplicate ID ",
+    "unknown error "
+};
+
+SkXMLParserError::SkXMLParserError() : fCode(kNoError), fLineNumber(-1),
+    fNativeCode(-1)
+{
+    reset();
+}
+
+SkXMLParserError::~SkXMLParserError()
+{
+    // need a virtual destructor for our subclasses
+}
+
+void SkXMLParserError::getErrorString(SkString* str) const
+{
+    SkASSERT(str);
+    SkString temp;
+    if (fCode != kNoError) {
+        if ((unsigned)fCode < SK_ARRAY_COUNT(gErrorStrings))
+            temp.set(gErrorStrings[fCode - 1]);
+        temp.append(fNoun);
+    } else
+        SkXMLParser::GetNativeErrorString(fNativeCode, &temp);
+    str->append(temp);
+}
+
+void SkXMLParserError::reset() {
+    fCode = kNoError;
+    fLineNumber = -1;
+    fNativeCode = -1;
+}
+
+
+////////////////
+
+SkXMLParser::SkXMLParser(SkXMLParserError* parserError) : fParser(NULL), fError(parserError)
+{
+}
+
+SkXMLParser::~SkXMLParser()
+{
+}
+
+bool SkXMLParser::startElement(const char elem[])
+{
+    return this->onStartElement(elem);
+}
+
+bool SkXMLParser::addAttribute(const char name[], const char value[])
+{
+    return this->onAddAttribute(name, value);
+}
+
+bool SkXMLParser::endElement(const char elem[])
+{
+    return this->onEndElement(elem);
+}
+
+bool SkXMLParser::text(const char text[], int len) 
+{
+    return this->onText(text, len);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool SkXMLParser::onStartElement(const char elem[]) {return false; }
+bool SkXMLParser::onAddAttribute(const char name[], const char value[]) {return false; }
+bool SkXMLParser::onEndElement(const char elem[]) { return false; }
+bool SkXMLParser::onText(const char text[], int len) {return false; }
diff --git a/src/xml/SkXMLPullParser.cpp b/src/xml/SkXMLPullParser.cpp
new file mode 100644
index 0000000..5df75b7
--- /dev/null
+++ b/src/xml/SkXMLPullParser.cpp
@@ -0,0 +1,132 @@
+#include "SkXMLParser.h"
+#include "SkStream.h"
+
+static void reset(SkXMLPullParser::Curr* curr)
+{
+    curr->fEventType = SkXMLPullParser::ERROR;
+    curr->fName = "";
+    curr->fAttrInfoCount = 0;
+    curr->fIsWhitespace = false;
+}
+
+SkXMLPullParser::SkXMLPullParser() : fStream(NULL)
+{
+    fCurr.fEventType = ERROR;
+    fDepth = -1;
+}
+
+SkXMLPullParser::SkXMLPullParser(SkStream* stream) : fStream(NULL)
+{
+    fCurr.fEventType = ERROR;
+    fDepth = 0;
+    
+    this->setStream(stream);
+}
+
+SkXMLPullParser::~SkXMLPullParser()
+{
+    this->setStream(NULL);
+}
+
+SkStream* SkXMLPullParser::setStream(SkStream* stream)
+{
+    if (fStream && !stream)
+        this->onExit();
+
+    SkRefCnt_SafeAssign(fStream, stream);
+
+    if (fStream)
+    {
+        fCurr.fEventType = START_DOCUMENT;
+        this->onInit();
+    }
+    else
+    {
+        fCurr.fEventType = ERROR;
+    }
+    fDepth = 0;
+
+    return fStream;
+}
+
+SkXMLPullParser::EventType SkXMLPullParser::nextToken()
+{
+    switch (fCurr.fEventType) {
+    case ERROR:
+    case END_DOCUMENT:
+        break;
+    case END_TAG:
+        fDepth -= 1;
+        // fall through
+    default:        
+        reset(&fCurr);
+        fCurr.fEventType = this->onNextToken();
+        break;
+    }
+    
+    switch (fCurr.fEventType) {
+    case START_TAG:
+        fDepth += 1;
+        break;
+    default:
+        break;
+    }
+
+    return fCurr.fEventType;
+}
+
+const char* SkXMLPullParser::getName()
+{
+    switch (fCurr.fEventType) {
+    case START_TAG:
+    case END_TAG:
+        return fCurr.fName;
+    default:
+        return NULL;
+    }
+}
+
+const char* SkXMLPullParser::getText()
+{
+    switch (fCurr.fEventType) {
+    case TEXT:
+    case IGNORABLE_WHITESPACE:
+        return fCurr.fName;
+    default:
+        return NULL;
+    }
+}
+
+bool SkXMLPullParser::isWhitespace()
+{
+    switch (fCurr.fEventType) {
+    case IGNORABLE_WHITESPACE:
+        return true;
+    case TEXT:
+    case CDSECT:
+        return fCurr.fIsWhitespace;
+    default:
+        return false;   // unknown/illegal
+    }
+}
+
+int SkXMLPullParser::getAttributeCount()
+{
+    return fCurr.fAttrInfoCount;
+}
+
+void SkXMLPullParser::getAttributeInfo(int index, AttrInfo* info)
+{
+    SkASSERT((unsigned)index < (unsigned)fCurr.fAttrInfoCount);
+    
+    if (info)
+        *info = fCurr.fAttrInfos[index];
+}
+    
+bool SkXMLPullParser::onEntityReplacement(const char name[],
+                                          SkString* replacement)
+{
+    // TODO: std 5 entities here
+    return false;
+}
+
diff --git a/src/xml/SkXMLWriter.cpp b/src/xml/SkXMLWriter.cpp
new file mode 100644
index 0000000..4685d81
--- /dev/null
+++ b/src/xml/SkXMLWriter.cpp
@@ -0,0 +1,342 @@
+/* libs/graphics/xml/SkXMLWriter.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include "SkXMLWriter.h"
+#include "SkStream.h"
+
+SkXMLWriter::SkXMLWriter(bool doEscapeMarkup) : fDoEscapeMarkup(doEscapeMarkup)
+{
+}
+
+SkXMLWriter::~SkXMLWriter()
+{
+    SkASSERT(fElems.count() == 0);
+}
+
+void SkXMLWriter::flush()
+{
+    while (fElems.count())
+        this->endElement();
+}
+
+void SkXMLWriter::addAttribute(const char name[], const char value[])
+{
+    this->addAttributeLen(name, value, strlen(value));
+}
+
+void SkXMLWriter::addS32Attribute(const char name[], int32_t value)
+{
+    SkString    tmp;
+    tmp.appendS32(value);
+    this->addAttribute(name, tmp.c_str());
+}
+
+void SkXMLWriter::addHexAttribute(const char name[], uint32_t value, int minDigits)
+{
+    SkString    tmp("0x");
+    tmp.appendHex(value, minDigits);
+    this->addAttribute(name, tmp.c_str());
+}
+
+void SkXMLWriter::addScalarAttribute(const char name[], SkScalar value)
+{
+    SkString    tmp;
+    tmp.appendScalar(value);
+    this->addAttribute(name, tmp.c_str());
+}
+
+void SkXMLWriter::doEnd(Elem* elem)
+{
+    delete elem;
+}
+
+bool SkXMLWriter::doStart(const char name[], size_t length)
+{
+    int level = fElems.count();
+    bool firstChild = level > 0 && !fElems[level-1]->fHasChildren;
+    if (firstChild)
+        fElems[level-1]->fHasChildren = true;
+    Elem** elem = fElems.push();
+    *elem = new Elem;
+    (*elem)->fName.set(name, length);
+    (*elem)->fHasChildren = 0;
+    return firstChild;
+}
+
+SkXMLWriter::Elem* SkXMLWriter::getEnd() 
+{
+    Elem* elem;
+    fElems.pop(&elem);
+    return elem;
+}
+
+const char* SkXMLWriter::getHeader()
+{
+    static const char gHeader[] = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>";
+    return gHeader;
+}
+
+void SkXMLWriter::startElement(const char name[])
+{
+    this->startElementLen(name, strlen(name));
+}
+
+static const char* escape_char(char c, char storage[2])
+{
+    static const char* gEscapeChars[] = {
+        "<&lt;",
+        ">&gt;",
+        //"\"&quot;",
+        //"'&apos;",
+        "&&amp;"
+    };
+
+    const char** array = gEscapeChars;
+    for (unsigned i = 0; i < SK_ARRAY_COUNT(gEscapeChars); i++)
+    {
+        if (array[i][0] == c)
+            return &array[i][1];
+    }
+    storage[0] = c;
+    storage[1] = 0;
+    return storage;
+}
+
+static size_t escape_markup(char dst[], const char src[], size_t length)
+{
+    size_t      extra = 0;
+    const char* stop = src + length;
+
+    while (src < stop)
+    {
+        char        orig[2];
+        const char* seq = escape_char(*src, orig);
+        size_t      seqSize = strlen(seq);
+
+        if (dst)
+        {
+            memcpy(dst, seq, seqSize);
+            dst += seqSize;
+        }
+
+        // now record the extra size needed
+        extra += seqSize - 1;   // minus one to subtract the original char
+
+        // bump to the next src char
+        src += 1;
+    }
+    return extra;
+}
+
+void SkXMLWriter::addAttributeLen(const char name[], const char value[], size_t length)
+{
+    SkString valueStr;
+
+    if (fDoEscapeMarkup)
+    {
+        size_t   extra = escape_markup(NULL, value, length);
+        if (extra)
+        {
+            valueStr.resize(length + extra);
+            (void)escape_markup(valueStr.writable_str(), value, length);
+            value = valueStr.c_str();
+            length += extra;
+        }
+    }
+    this->onAddAttributeLen(name, value, length);
+}
+
+void SkXMLWriter::startElementLen(const char elem[], size_t length)
+{
+    this->onStartElementLen(elem, length);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+static void write_dom(const SkDOM& dom, const SkDOM::Node* node, SkXMLWriter* w, bool skipRoot)
+{
+    if (!skipRoot)
+    {
+        w->startElement(dom.getName(node));
+
+        SkDOM::AttrIter iter(dom, node);
+        const char* name;
+        const char* value;
+        while ((name = iter.next(&value)) != NULL)
+            w->addAttribute(name, value);
+    }
+
+    node = dom.getFirstChild(node, NULL);
+    while (node)
+    {
+        write_dom(dom, node, w, false);
+        node = dom.getNextSibling(node, NULL);
+    }
+
+    if (!skipRoot)
+        w->endElement();
+}
+
+void SkXMLWriter::writeDOM(const SkDOM& dom, const SkDOM::Node* node, bool skipRoot)
+{
+    if (node)
+        write_dom(dom, node, this, skipRoot);
+}
+
+void SkXMLWriter::writeHeader()
+{
+}
+
+// SkXMLStreamWriter
+
+static void tab(SkWStream& stream, int level)
+{
+    for (int i = 0; i < level; i++)
+        stream.writeText("\t");
+}
+
+SkXMLStreamWriter::SkXMLStreamWriter(SkWStream* stream) : fStream(*stream)
+{
+}
+
+SkXMLStreamWriter::~SkXMLStreamWriter()
+{
+    this->flush();
+}
+
+void SkXMLStreamWriter::onAddAttributeLen(const char name[], const char value[], size_t length)
+{
+    SkASSERT(!fElems.top()->fHasChildren);
+    fStream.writeText(" ");
+    fStream.writeText(name);
+    fStream.writeText("=\"");
+    fStream.write(value, length);
+    fStream.writeText("\"");
+}
+
+void SkXMLStreamWriter::onEndElement()
+{
+    Elem* elem = getEnd();
+    if (elem->fHasChildren)
+    {
+        tab(fStream, fElems.count());
+        fStream.writeText("</");
+        fStream.writeText(elem->fName.c_str());
+        fStream.writeText(">");
+    }
+    else
+        fStream.writeText("/>");
+    fStream.newline();
+    doEnd(elem);
+}
+
+void SkXMLStreamWriter::onStartElementLen(const char name[], size_t length)
+{
+    int level = fElems.count();
+    if (this->doStart(name, length))
+    {
+        // the first child, need to close with >
+        fStream.writeText(">");
+        fStream.newline();
+    }
+
+    tab(fStream, level);
+    fStream.writeText("<");
+    fStream.write(name, length);
+}
+
+void SkXMLStreamWriter::writeHeader()
+{
+    const char* header = getHeader();
+    fStream.write(header, strlen(header));
+    fStream.newline();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkXMLParser.h"
+
+SkXMLParserWriter::SkXMLParserWriter(SkXMLParser* parser)
+    : SkXMLWriter(false), fParser(*parser)
+{
+}
+
+SkXMLParserWriter::~SkXMLParserWriter()
+{
+    this->flush();
+}
+
+void SkXMLParserWriter::onAddAttributeLen(const char name[], const char value[], size_t length)
+{
+    SkASSERT(fElems.count() == 0 || !fElems.top()->fHasChildren);
+    SkString str(value, length);
+    fParser.addAttribute(name, str.c_str());
+}
+
+void SkXMLParserWriter::onEndElement()
+{
+    Elem* elem = this->getEnd();
+    fParser.endElement(elem->fName.c_str());
+    this->doEnd(elem);
+}
+
+void SkXMLParserWriter::onStartElementLen(const char name[], size_t length)
+{
+    (void)this->doStart(name, length);
+    SkString str(name, length);
+    fParser.startElement(str.c_str());
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+void SkXMLStreamWriter::UnitTest()
+{
+#ifdef SK_SUPPORT_UNITTEST
+    SkDebugWStream  s;
+    SkXMLStreamWriter       w(&s);
+
+    w.startElement("elem0");
+    w.addAttribute("hello", "world");
+    w.addS32Attribute("dec", 42);
+    w.addHexAttribute("hex", 0x42, 3);
+#ifdef SK_SCALAR_IS_FLOAT
+    w.addScalarAttribute("scalar", -4.2f);
+#endif
+    w.startElement("elem1");
+        w.endElement();
+        w.startElement("elem1");
+        w.addAttribute("name", "value");
+        w.endElement();
+        w.startElement("elem1");
+            w.startElement("elem2");
+                w.startElement("elem3");
+                w.addAttribute("name", "value");
+                w.endElement();
+            w.endElement();
+            w.startElement("elem2");
+            w.endElement();
+        w.endElement();
+    w.endElement();
+#endif
+}
+
+#endif
+