(Auto)update libjingle 77554188-> 77629208

git-svn-id: http://webrtc.googlecode.com/svn/trunk@7439 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/xmllite/qname.cc b/talk/xmllite/qname.cc
new file mode 100755
index 0000000..0dadb79
--- /dev/null
+++ b/talk/xmllite/qname.cc
@@ -0,0 +1,95 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/xmllite/qname.h"
+
+namespace buzz {
+
+QName::QName() {
+}
+
+QName::QName(const QName& qname)
+    : namespace_(qname.namespace_),
+      local_part_(qname.local_part_) {
+}
+
+QName::QName(const StaticQName& const_value)
+    : namespace_(const_value.ns),
+      local_part_(const_value.local) {
+}
+
+QName::QName(const std::string& ns, const std::string& local)
+    : namespace_(ns),
+      local_part_(local) {
+}
+
+QName::QName(const std::string& merged_or_local) {
+  size_t i = merged_or_local.rfind(':');
+  if (i == std::string::npos) {
+    local_part_ = merged_or_local;
+  } else {
+    namespace_ = merged_or_local.substr(0, i);
+    local_part_ = merged_or_local.substr(i + 1);
+  }
+}
+
+QName::~QName() {
+}
+
+std::string QName::Merged() const {
+  if (namespace_[0] == '\0')
+    return local_part_;
+
+  std::string result;
+  result.reserve(namespace_.length() + 1 + local_part_.length());
+  result += namespace_;
+  result += ':';
+  result += local_part_;
+  return result;
+}
+
+bool QName::IsEmpty() const {
+  return namespace_.empty() && local_part_.empty();
+}
+
+int QName::Compare(const StaticQName& other) const {
+  int result = local_part_.compare(other.local);
+  if (result != 0)
+    return result;
+
+  return namespace_.compare(other.ns);
+}
+
+int QName::Compare(const QName& other) const {
+  int result = local_part_.compare(other.local_part_);
+  if (result != 0)
+    return result;
+
+  return namespace_.compare(other.namespace_);
+}
+
+}  // namespace buzz
diff --git a/talk/xmllite/qname.h b/talk/xmllite/qname.h
new file mode 100755
index 0000000..92e54d0
--- /dev/null
+++ b/talk/xmllite/qname.h
@@ -0,0 +1,100 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_XMLLITE_QNAME_H_
+#define TALK_XMLLITE_QNAME_H_
+
+#include <string>
+
+namespace buzz {
+
+class QName;
+
+// StaticQName is used to represend constant quailified names. They
+// can be initialized statically and don't need intializers code, e.g.
+//   const StaticQName QN_FOO = { "foo_namespace", "foo" };
+//
+// Beside this use case, QName should be used everywhere
+// else. StaticQName instances are implicitly converted to QName
+// objects.
+struct StaticQName {
+  const char* const ns;
+  const char* const local;
+
+  bool operator==(const QName& other) const;
+  bool operator!=(const QName& other) const;
+};
+
+class QName {
+ public:
+  QName();
+  QName(const QName& qname);
+  QName(const StaticQName& const_value);
+  QName(const std::string& ns, const std::string& local);
+  explicit QName(const std::string& merged_or_local);
+  ~QName();
+
+  const std::string& Namespace() const { return namespace_; }
+  const std::string& LocalPart() const { return local_part_; }
+  std::string Merged() const;
+  bool IsEmpty() const;
+
+  int Compare(const StaticQName& other) const;
+  int Compare(const QName& other) const;
+
+  bool operator==(const StaticQName& other) const {
+    return Compare(other) == 0;
+  }
+  bool operator==(const QName& other) const {
+    return Compare(other) == 0;
+  }
+  bool operator!=(const StaticQName& other) const {
+    return Compare(other) != 0;
+  }
+  bool operator!=(const QName& other) const {
+    return Compare(other) != 0;
+  }
+  bool operator<(const QName& other) const {
+    return Compare(other) < 0;
+  }
+
+ private:
+  std::string namespace_;
+  std::string local_part_;
+};
+
+inline bool StaticQName::operator==(const QName& other) const {
+  return other.Compare(*this) == 0;
+}
+
+inline bool StaticQName::operator!=(const QName& other) const {
+  return other.Compare(*this) != 0;
+}
+
+}  // namespace buzz
+
+#endif  // TALK_XMLLITE_QNAME_H_
diff --git a/talk/xmllite/qname_unittest.cc b/talk/xmllite/qname_unittest.cc
new file mode 100755
index 0000000..72c1713
--- /dev/null
+++ b/talk/xmllite/qname_unittest.cc
@@ -0,0 +1,131 @@
+/*
+ * libjingle
+ * Copyright 2004, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string>
+#include "talk/xmllite/qname.h"
+#include "webrtc/base/gunit.h"
+
+using buzz::StaticQName;
+using buzz::QName;
+
+TEST(QNameTest, TestTrivial) {
+  QName name("test");
+  EXPECT_EQ(name.LocalPart(), "test");
+  EXPECT_EQ(name.Namespace(), "");
+}
+
+TEST(QNameTest, TestSplit) {
+  QName name("a:test");
+  EXPECT_EQ(name.LocalPart(), "test");
+  EXPECT_EQ(name.Namespace(), "a");
+  QName name2("a-very:long:namespace:test-this");
+  EXPECT_EQ(name2.LocalPart(), "test-this");
+  EXPECT_EQ(name2.Namespace(), "a-very:long:namespace");
+}
+
+TEST(QNameTest, TestMerge) {
+  QName name("a", "test");
+  EXPECT_EQ(name.LocalPart(), "test");
+  EXPECT_EQ(name.Namespace(), "a");
+  EXPECT_EQ(name.Merged(), "a:test");
+  QName name2("a-very:long:namespace", "test-this");
+  EXPECT_EQ(name2.LocalPart(), "test-this");
+  EXPECT_EQ(name2.Namespace(), "a-very:long:namespace");
+  EXPECT_EQ(name2.Merged(), "a-very:long:namespace:test-this");
+}
+
+TEST(QNameTest, TestAssignment) {
+  QName name("a", "test");
+  // copy constructor
+  QName namecopy(name);
+  EXPECT_EQ(namecopy.LocalPart(), "test");
+  EXPECT_EQ(namecopy.Namespace(), "a");
+  QName nameassigned("");
+  nameassigned = name;
+  EXPECT_EQ(nameassigned.LocalPart(), "test");
+  EXPECT_EQ(nameassigned.Namespace(), "a");
+}
+
+TEST(QNameTest, TestConstAssignment) {
+  StaticQName name = { "a", "test" };
+  QName namecopy(name);
+  EXPECT_EQ(namecopy.LocalPart(), "test");
+  EXPECT_EQ(namecopy.Namespace(), "a");
+  QName nameassigned("");
+  nameassigned = name;
+  EXPECT_EQ(nameassigned.LocalPart(), "test");
+  EXPECT_EQ(nameassigned.Namespace(), "a");
+}
+
+TEST(QNameTest, TestEquality) {
+  QName name("a-very:long:namespace:test-this");
+  QName name2("a-very:long:namespace", "test-this");
+  QName name3("a-very:long:namespaxe", "test-this");
+  EXPECT_TRUE(name == name2);
+  EXPECT_FALSE(name == name3);
+}
+
+TEST(QNameTest, TestCompare) {
+  QName name("a");
+  QName name2("nsa", "a");
+  QName name3("nsa", "b");
+  QName name4("nsb", "b");
+
+  EXPECT_TRUE(name < name2);
+  EXPECT_FALSE(name2 < name);
+
+  EXPECT_FALSE(name2 < name2);
+
+  EXPECT_TRUE(name2 < name3);
+  EXPECT_FALSE(name3 < name2);
+
+  EXPECT_TRUE(name3 < name4);
+  EXPECT_FALSE(name4 < name3);
+}
+
+TEST(QNameTest, TestStaticQName) {
+  const StaticQName const_name1 = { "namespace", "local-name1" };
+  const StaticQName const_name2 = { "namespace", "local-name2" };
+  const QName name("namespace", "local-name1");
+  const QName name1 = const_name1;
+  const QName name2 = const_name2;
+
+  EXPECT_TRUE(name == const_name1);
+  EXPECT_TRUE(const_name1 == name);
+  EXPECT_FALSE(name != const_name1);
+  EXPECT_FALSE(const_name1 != name);
+
+  EXPECT_TRUE(name == name1);
+  EXPECT_TRUE(name1 == name);
+  EXPECT_FALSE(name != name1);
+  EXPECT_FALSE(name1 != name);
+
+  EXPECT_FALSE(name == name2);
+  EXPECT_FALSE(name2 == name);
+  EXPECT_TRUE(name != name2);
+  EXPECT_TRUE(name2 != name);
+}
diff --git a/talk/xmllite/xmlbuilder.cc b/talk/xmllite/xmlbuilder.cc
new file mode 100755
index 0000000..a091ed9
--- /dev/null
+++ b/talk/xmllite/xmlbuilder.cc
@@ -0,0 +1,147 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/xmllite/xmlbuilder.h"
+
+#include <set>
+#include <vector>
+#include "talk/xmllite/xmlconstants.h"
+#include "talk/xmllite/xmlelement.h"
+#include "webrtc/base/common.h"
+
+namespace buzz {
+
+XmlBuilder::XmlBuilder() :
+  pelCurrent_(NULL),
+  pelRoot_(),
+  pvParents_(new std::vector<XmlElement *>()) {
+}
+
+void
+XmlBuilder::Reset() {
+  pelRoot_.reset();
+  pelCurrent_ = NULL;
+  pvParents_->clear();
+}
+
+XmlElement *
+XmlBuilder::BuildElement(XmlParseContext * pctx,
+                              const char * name, const char ** atts) {
+  QName tagName(pctx->ResolveQName(name, false));
+  if (tagName.IsEmpty())
+    return NULL;
+
+  XmlElement * pelNew = new XmlElement(tagName);
+
+  if (!*atts)
+    return pelNew;
+
+  std::set<QName> seenNonlocalAtts;
+
+  while (*atts) {
+    QName attName(pctx->ResolveQName(*atts, true));
+    if (attName.IsEmpty()) {
+      delete pelNew;
+      return NULL;
+    }
+
+    // verify that namespaced names are unique
+    if (!attName.Namespace().empty()) {
+      if (seenNonlocalAtts.count(attName)) {
+        delete pelNew;
+        return NULL;
+      }
+      seenNonlocalAtts.insert(attName);
+    }
+
+    pelNew->AddAttr(attName, std::string(*(atts + 1)));
+    atts += 2;
+  }
+
+  return pelNew;
+}
+
+void
+XmlBuilder::StartElement(XmlParseContext * pctx,
+                              const char * name, const char ** atts) {
+  XmlElement * pelNew = BuildElement(pctx, name, atts);
+  if (pelNew == NULL) {
+    pctx->RaiseError(XML_ERROR_SYNTAX);
+    return;
+  }
+
+  if (!pelCurrent_) {
+    pelCurrent_ = pelNew;
+    pelRoot_.reset(pelNew);
+    pvParents_->push_back(NULL);
+  } else {
+    pelCurrent_->AddElement(pelNew);
+    pvParents_->push_back(pelCurrent_);
+    pelCurrent_ = pelNew;
+  }
+}
+
+void
+XmlBuilder::EndElement(XmlParseContext * pctx, const char * name) {
+  RTC_UNUSED(pctx);
+  RTC_UNUSED(name);
+  pelCurrent_ = pvParents_->back();
+  pvParents_->pop_back();
+}
+
+void
+XmlBuilder::CharacterData(XmlParseContext * pctx,
+                               const char * text, int len) {
+  RTC_UNUSED(pctx);
+  if (pelCurrent_) {
+    pelCurrent_->AddParsedText(text, len);
+  }
+}
+
+void
+XmlBuilder::Error(XmlParseContext * pctx, XML_Error err) {
+  RTC_UNUSED(pctx);
+  RTC_UNUSED(err);
+  pelRoot_.reset(NULL);
+  pelCurrent_ = NULL;
+  pvParents_->clear();
+}
+
+XmlElement *
+XmlBuilder::CreateElement() {
+  return pelRoot_.release();
+}
+
+XmlElement *
+XmlBuilder::BuiltElement() {
+  return pelRoot_.get();
+}
+
+XmlBuilder::~XmlBuilder() {
+}
+
+}  // namespace buzz
diff --git a/talk/xmllite/xmlbuilder.h b/talk/xmllite/xmlbuilder.h
new file mode 100755
index 0000000..38fc009
--- /dev/null
+++ b/talk/xmllite/xmlbuilder.h
@@ -0,0 +1,78 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _xmlbuilder_h_
+#define _xmlbuilder_h_
+
+#include <string>
+#include <vector>
+#include "talk/xmllite/xmlparser.h"
+#include "webrtc/base/scoped_ptr.h"
+
+#ifdef EXPAT_RELATIVE_PATH
+#include "expat.h"
+#else
+#include "third_party/expat/v2_0_1/Source/lib/expat.h"
+#endif  // EXPAT_RELATIVE_PATH
+
+namespace buzz {
+
+class XmlElement;
+class XmlParseContext;
+
+
+class XmlBuilder : public XmlParseHandler {
+public:
+  XmlBuilder();
+
+  static XmlElement * BuildElement(XmlParseContext * pctx,
+                                  const char * name, const char ** atts);
+  virtual void StartElement(XmlParseContext * pctx,
+                            const char * name, const char ** atts);
+  virtual void EndElement(XmlParseContext * pctx, const char * name);
+  virtual void CharacterData(XmlParseContext * pctx,
+                             const char * text, int len);
+  virtual void Error(XmlParseContext * pctx, XML_Error);
+  virtual ~XmlBuilder();
+
+  void Reset();
+
+  // Take ownership of the built element; second call returns NULL
+  XmlElement * CreateElement();
+
+  // Peek at the built element without taking ownership
+  XmlElement * BuiltElement();
+
+private:
+  XmlElement * pelCurrent_;
+  rtc::scoped_ptr<XmlElement> pelRoot_;
+  rtc::scoped_ptr<std::vector<XmlElement*> > pvParents_;
+};
+
+}
+
+#endif
diff --git a/talk/xmllite/xmlbuilder_unittest.cc b/talk/xmllite/xmlbuilder_unittest.cc
new file mode 100755
index 0000000..56304cf
--- /dev/null
+++ b/talk/xmllite/xmlbuilder_unittest.cc
@@ -0,0 +1,194 @@
+/*
+ * libjingle
+ * Copyright 2004, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <iostream>
+#include <sstream>
+#include <string>
+#include "talk/xmllite/xmlbuilder.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/xmlparser.h"
+#include "webrtc/base/common.h"
+#include "webrtc/base/gunit.h"
+
+using buzz::XmlBuilder;
+using buzz::XmlElement;
+using buzz::XmlParser;
+
+TEST(XmlBuilderTest, TestTrivial) {
+  XmlBuilder builder;
+  XmlParser::ParseXml(&builder, "<testing/>");
+  EXPECT_EQ("<testing/>", builder.BuiltElement()->Str());
+}
+
+TEST(XmlBuilderTest, TestAttributes1) {
+  XmlBuilder builder;
+  XmlParser::ParseXml(&builder, "<testing a='b'/>");
+  EXPECT_EQ("<testing a=\"b\"/>", builder.BuiltElement()->Str());
+}
+
+TEST(XmlBuilderTest, TestAttributes2) {
+  XmlBuilder builder;
+  XmlParser::ParseXml(&builder, "<testing e='' long='some text'/>");
+  EXPECT_EQ("<testing e=\"\" long=\"some text\"/>",
+      builder.BuiltElement()->Str());
+}
+
+TEST(XmlBuilderTest, TestNesting1) {
+  XmlBuilder builder;
+  XmlParser::ParseXml(&builder,
+      "<top><first/><second><third></third></second></top>");
+  EXPECT_EQ("<top><first/><second><third/></second></top>",
+      builder.BuiltElement()->Str());
+}
+
+TEST(XmlBuilderTest, TestNesting2) {
+  XmlBuilder builder;
+  XmlParser::ParseXml(&builder,
+    "<top><fifth><deeper><and><deeper/></and><sibling><leaf/>"
+    "</sibling></deeper></fifth><first/><second><third></third>"
+    "</second></top>");
+  EXPECT_EQ("<top><fifth><deeper><and><deeper/></and><sibling><leaf/>"
+    "</sibling></deeper></fifth><first/><second><third/>"
+    "</second></top>", builder.BuiltElement()->Str());
+}
+
+TEST(XmlBuilderTest, TestQuoting1) {
+  XmlBuilder builder;
+  XmlParser::ParseXml(&builder, "<testing a='>'/>");
+  EXPECT_EQ("<testing a=\"&gt;\"/>", builder.BuiltElement()->Str());
+}
+
+TEST(XmlBuilderTest, TestQuoting2) {
+  XmlBuilder builder;
+  XmlParser::ParseXml(&builder, "<testing a='&lt;>&amp;&quot;'/>");
+  EXPECT_EQ("<testing a=\"&lt;&gt;&amp;&quot;\"/>",
+      builder.BuiltElement()->Str());
+}
+
+TEST(XmlBuilderTest, TestQuoting3) {
+  XmlBuilder builder;
+  XmlParser::ParseXml(&builder, "<testing a='so &quot;important&quot;'/>");
+  EXPECT_EQ("<testing a=\"so &quot;important&quot;\"/>",
+      builder.BuiltElement()->Str());
+}
+
+TEST(XmlBuilderTest, TestQuoting4) {
+  XmlBuilder builder;
+  XmlParser::ParseXml(&builder, "<testing a='&quot;important&quot;, yes'/>");
+  EXPECT_EQ("<testing a=\"&quot;important&quot;, yes\"/>",
+      builder.BuiltElement()->Str());
+}
+
+TEST(XmlBuilderTest, TestQuoting5) {
+  XmlBuilder builder;
+  XmlParser::ParseXml(&builder,
+      "<testing a='&lt;what is &quot;important&quot;&gt;'/>");
+  EXPECT_EQ("<testing a=\"&lt;what is &quot;important&quot;&gt;\"/>",
+      builder.BuiltElement()->Str());
+}
+
+TEST(XmlBuilderTest, TestText1) {
+  XmlBuilder builder;
+  XmlParser::ParseXml(&builder, "<testing>></testing>");
+  EXPECT_EQ("<testing>&gt;</testing>", builder.BuiltElement()->Str());
+}
+
+TEST(XmlBuilderTest, TestText2) {
+  XmlBuilder builder;
+  XmlParser::ParseXml(&builder, "<testing>&lt;>&amp;&quot;</testing>");
+  EXPECT_EQ("<testing>&lt;&gt;&amp;\"</testing>",
+      builder.BuiltElement()->Str());
+}
+
+TEST(XmlBuilderTest, TestText3) {
+  XmlBuilder builder;
+  XmlParser::ParseXml(&builder, "<testing>so &lt;important&gt;</testing>");
+  EXPECT_EQ("<testing>so &lt;important&gt;</testing>",
+      builder.BuiltElement()->Str());
+}
+
+TEST(XmlBuilderTest, TestText4) {
+  XmlBuilder builder;
+  XmlParser::ParseXml(&builder, "<testing>&lt;important&gt;, yes</testing>");
+  EXPECT_EQ("<testing>&lt;important&gt;, yes</testing>",
+      builder.BuiltElement()->Str());
+}
+
+TEST(XmlBuilderTest, TestText5) {
+  XmlBuilder builder;
+  XmlParser::ParseXml(&builder,
+      "<testing>importance &amp;&lt;important&gt;&amp;</testing>");
+  EXPECT_EQ("<testing>importance &amp;&lt;important&gt;&amp;</testing>",
+      builder.BuiltElement()->Str());
+}
+
+TEST(XmlBuilderTest, TestNamespace1) {
+  XmlBuilder builder;
+  XmlParser::ParseXml(&builder, "<testing xmlns='foo'/>");
+  EXPECT_EQ("<testing xmlns=\"foo\"/>", builder.BuiltElement()->Str());
+}
+
+TEST(XmlBuilderTest, TestNamespace2) {
+  XmlBuilder builder;
+  XmlParser::ParseXml(&builder, "<testing xmlns:a='foo' a:b='c'/>");
+  EXPECT_EQ("<testing xmlns:a=\"foo\" a:b=\"c\"/>",
+      builder.BuiltElement()->Str());
+}
+
+TEST(XmlBuilderTest, TestNamespace3) {
+  XmlBuilder builder;
+  XmlParser::ParseXml(&builder, "<testing xmlns:a=''/>");
+  EXPECT_TRUE(NULL == builder.BuiltElement());
+}
+
+TEST(XmlBuilderTest, TestNamespace4) {
+  XmlBuilder builder;
+  XmlParser::ParseXml(&builder, "<testing a:b='c'/>");
+  EXPECT_TRUE(NULL == builder.BuiltElement());
+}
+
+TEST(XmlBuilderTest, TestAttrCollision1) {
+  XmlBuilder builder;
+  XmlParser::ParseXml(&builder, "<testing a='first' a='second'/>");
+  EXPECT_TRUE(NULL == builder.BuiltElement());
+}
+
+TEST(XmlBuilderTest, TestAttrCollision2) {
+  XmlBuilder builder;
+  XmlParser::ParseXml(&builder,
+      "<testing xmlns:a='foo' xmlns:b='foo' a:x='c' b:x='d'/>");
+  EXPECT_TRUE(NULL == builder.BuiltElement());
+}
+
+TEST(XmlBuilderTest, TestAttrCollision3) {
+  XmlBuilder builder;
+  XmlParser::ParseXml(&builder,
+      "<testing xmlns:a='foo'><nested xmlns:b='foo' a:x='c' b:x='d'/>"
+      "</testing>");
+  EXPECT_TRUE(NULL == builder.BuiltElement());
+}
+
diff --git a/talk/xmllite/xmlconstants.cc b/talk/xmllite/xmlconstants.cc
new file mode 100755
index 0000000..f94d779
--- /dev/null
+++ b/talk/xmllite/xmlconstants.cc
@@ -0,0 +1,42 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/xmllite/xmlconstants.h"
+
+namespace buzz {
+
+const char STR_EMPTY[] = "";
+const char NS_XML[] = "http://www.w3.org/XML/1998/namespace";
+const char NS_XMLNS[] = "http://www.w3.org/2000/xmlns/";
+const char STR_XMLNS[] = "xmlns";
+const char STR_XML[] = "xml";
+const char STR_VERSION[] = "version";
+const char STR_ENCODING[] = "encoding";
+
+const StaticQName QN_XMLNS = { STR_EMPTY, STR_XMLNS };
+
+}  // namespace buzz
diff --git a/talk/xmllite/xmlconstants.h b/talk/xmllite/xmlconstants.h
new file mode 100755
index 0000000..3e5da98
--- /dev/null
+++ b/talk/xmllite/xmlconstants.h
@@ -0,0 +1,47 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_XMLLITE_XMLCONSTANTS_H_
+#define TALK_XMLLITE_XMLCONSTANTS_H_
+
+#include "talk/xmllite/qname.h"
+
+namespace buzz {
+
+extern const char STR_EMPTY[];
+extern const char NS_XML[];
+extern const char NS_XMLNS[];
+extern const char STR_XMLNS[];
+extern const char STR_XML[];
+extern const char STR_VERSION[];
+extern const char STR_ENCODING[];
+
+extern const StaticQName QN_XMLNS;
+
+}  // namespace buzz
+
+#endif  // TALK_XMLLITE_XMLCONSTANTS_H_
diff --git a/talk/xmllite/xmlelement.cc b/talk/xmllite/xmlelement.cc
new file mode 100755
index 0000000..d76d0f5
--- /dev/null
+++ b/talk/xmllite/xmlelement.cc
@@ -0,0 +1,513 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice, 
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products 
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/xmllite/xmlelement.h"
+
+#include <ostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "talk/xmllite/qname.h"
+#include "talk/xmllite/xmlbuilder.h"
+#include "talk/xmllite/xmlconstants.h"
+#include "talk/xmllite/xmlparser.h"
+#include "talk/xmllite/xmlprinter.h"
+#include "webrtc/base/common.h"
+
+namespace buzz {
+
+XmlChild::~XmlChild() {
+}
+
+bool XmlText::IsTextImpl() const {
+  return true;
+}
+
+XmlElement* XmlText::AsElementImpl() const {
+  return NULL;
+}
+
+XmlText* XmlText::AsTextImpl() const {
+  return const_cast<XmlText *>(this);
+}
+
+void XmlText::SetText(const std::string& text) {
+  text_ = text;
+}
+
+void XmlText::AddParsedText(const char* buf, int len) {
+  text_.append(buf, len);
+}
+
+void XmlText::AddText(const std::string& text) {
+  text_ += text;
+}
+
+XmlText::~XmlText() {
+}
+
+XmlElement::XmlElement(const QName& name) :
+    name_(name),
+    first_attr_(NULL),
+    last_attr_(NULL),
+    first_child_(NULL),
+    last_child_(NULL),
+    cdata_(false) {
+}
+
+XmlElement::XmlElement(const XmlElement& elt) :
+    XmlChild(),
+    name_(elt.name_),
+    first_attr_(NULL),
+    last_attr_(NULL),
+    first_child_(NULL),
+    last_child_(NULL),
+    cdata_(false) {
+
+  // copy attributes
+  XmlAttr* attr;
+  XmlAttr ** plast_attr = &first_attr_;
+  XmlAttr* newAttr = NULL;
+  for (attr = elt.first_attr_; attr; attr = attr->NextAttr()) {
+    newAttr = new XmlAttr(*attr);
+    *plast_attr = newAttr;
+    plast_attr = &(newAttr->next_attr_);
+  }
+  last_attr_ = newAttr;
+
+  // copy children
+  XmlChild* pChild;
+  XmlChild ** ppLast = &first_child_;
+  XmlChild* newChild = NULL;
+
+  for (pChild = elt.first_child_; pChild; pChild = pChild->NextChild()) {
+    if (pChild->IsText()) {
+      newChild = new XmlText(*(pChild->AsText()));
+    } else {
+      newChild = new XmlElement(*(pChild->AsElement()));
+    }
+    *ppLast = newChild;
+    ppLast = &(newChild->next_child_);
+  }
+  last_child_ = newChild;
+
+  cdata_ = elt.cdata_;
+}
+
+XmlElement::XmlElement(const QName& name, bool useDefaultNs) :
+  name_(name),
+  first_attr_(useDefaultNs ? new XmlAttr(QN_XMLNS, name.Namespace()) : NULL),
+  last_attr_(first_attr_),
+  first_child_(NULL),
+  last_child_(NULL),
+  cdata_(false) {
+}
+
+bool XmlElement::IsTextImpl() const {
+  return false;
+}
+
+XmlElement* XmlElement::AsElementImpl() const {
+  return const_cast<XmlElement *>(this);
+}
+
+XmlText* XmlElement::AsTextImpl() const {
+  return NULL;
+}
+
+const std::string XmlElement::BodyText() const {
+  if (first_child_ && first_child_->IsText() && last_child_ == first_child_) {
+    return first_child_->AsText()->Text();
+  }
+
+  return std::string();
+}
+
+void XmlElement::SetBodyText(const std::string& text) {
+  if (text.empty()) {
+    ClearChildren();
+  } else if (first_child_ == NULL) {
+    AddText(text);
+  } else if (first_child_->IsText() && last_child_ == first_child_) {
+    first_child_->AsText()->SetText(text);
+  } else {
+    ClearChildren();
+    AddText(text);
+  }
+}
+
+const QName XmlElement::FirstElementName() const {
+  const XmlElement* element = FirstElement();
+  if (element == NULL)
+    return QName();
+  return element->Name();
+}
+
+XmlAttr* XmlElement::FirstAttr() {
+  return first_attr_;
+}
+
+const std::string XmlElement::Attr(const StaticQName& name) const {
+  XmlAttr* attr;
+  for (attr = first_attr_; attr; attr = attr->next_attr_) {
+    if (attr->name_ == name)
+      return attr->value_;
+  }
+  return std::string();
+}
+
+const std::string XmlElement::Attr(const QName& name) const {
+  XmlAttr* attr;
+  for (attr = first_attr_; attr; attr = attr->next_attr_) {
+    if (attr->name_ == name)
+      return attr->value_;
+  }
+  return std::string();
+}
+
+bool XmlElement::HasAttr(const StaticQName& name) const {
+  XmlAttr* attr;
+  for (attr = first_attr_; attr; attr = attr->next_attr_) {
+    if (attr->name_ == name)
+      return true;
+  }
+  return false;
+}
+
+bool XmlElement::HasAttr(const QName& name) const {
+  XmlAttr* attr;
+  for (attr = first_attr_; attr; attr = attr->next_attr_) {
+    if (attr->name_ == name)
+      return true;
+  }
+  return false;
+}
+
+void XmlElement::SetAttr(const QName& name, const std::string& value) {
+  XmlAttr* attr;
+  for (attr = first_attr_; attr; attr = attr->next_attr_) {
+    if (attr->name_ == name)
+      break;
+  }
+  if (!attr) {
+    attr = new XmlAttr(name, value);
+    if (last_attr_)
+      last_attr_->next_attr_ = attr;
+    else
+      first_attr_ = attr;
+    last_attr_ = attr;
+    return;
+  }
+  attr->value_ = value;
+}
+
+void XmlElement::ClearAttr(const QName& name) {
+  XmlAttr* attr;
+  XmlAttr* last_attr = NULL;
+  for (attr = first_attr_; attr; attr = attr->next_attr_) {
+    if (attr->name_ == name)
+      break;
+    last_attr = attr;
+  }
+  if (!attr)
+    return;
+  if (!last_attr)
+    first_attr_ = attr->next_attr_;
+  else
+    last_attr->next_attr_ = attr->next_attr_;
+  if (last_attr_ == attr)
+    last_attr_ = last_attr;
+  delete attr;
+}
+
+XmlChild* XmlElement::FirstChild() {
+  return first_child_;
+}
+
+XmlElement* XmlElement::FirstElement() {
+  XmlChild* pChild;
+  for (pChild = first_child_; pChild; pChild = pChild->next_child_) {
+    if (!pChild->IsText())
+      return pChild->AsElement();
+  }
+  return NULL;
+}
+
+XmlElement* XmlElement::NextElement() {
+  XmlChild* pChild;
+  for (pChild = next_child_; pChild; pChild = pChild->next_child_) {
+    if (!pChild->IsText())
+      return pChild->AsElement();
+  }
+  return NULL;
+}
+
+XmlElement* XmlElement::FirstWithNamespace(const std::string& ns) {
+  XmlChild* pChild;
+  for (pChild = first_child_; pChild; pChild = pChild->next_child_) {
+    if (!pChild->IsText() && pChild->AsElement()->Name().Namespace() == ns)
+      return pChild->AsElement();
+  }
+  return NULL;
+}
+
+XmlElement *
+XmlElement::NextWithNamespace(const std::string& ns) {
+  XmlChild* pChild;
+  for (pChild = next_child_; pChild; pChild = pChild->next_child_) {
+    if (!pChild->IsText() && pChild->AsElement()->Name().Namespace() == ns)
+      return pChild->AsElement();
+  }
+  return NULL;
+}
+
+XmlElement *
+XmlElement::FirstNamed(const QName& name) {
+  XmlChild* pChild;
+  for (pChild = first_child_; pChild; pChild = pChild->next_child_) {
+    if (!pChild->IsText() && pChild->AsElement()->Name() == name)
+      return pChild->AsElement();
+  }
+  return NULL;
+}
+
+XmlElement *
+XmlElement::FirstNamed(const StaticQName& name) {
+  XmlChild* pChild;
+  for (pChild = first_child_; pChild; pChild = pChild->next_child_) {
+    if (!pChild->IsText() && pChild->AsElement()->Name() == name)
+      return pChild->AsElement();
+  }
+  return NULL;
+}
+
+XmlElement *
+XmlElement::NextNamed(const QName& name) {
+  XmlChild* pChild;
+  for (pChild = next_child_; pChild; pChild = pChild->next_child_) {
+    if (!pChild->IsText() && pChild->AsElement()->Name() == name)
+      return pChild->AsElement();
+  }
+  return NULL;
+}
+
+XmlElement *
+XmlElement::NextNamed(const StaticQName& name) {
+  XmlChild* pChild;
+  for (pChild = next_child_; pChild; pChild = pChild->next_child_) {
+    if (!pChild->IsText() && pChild->AsElement()->Name() == name)
+      return pChild->AsElement();
+  }
+  return NULL;
+}
+
+XmlElement* XmlElement::FindOrAddNamedChild(const QName& name) {
+  XmlElement* child = FirstNamed(name);
+  if (!child) {
+    child = new XmlElement(name);
+    AddElement(child);
+  }
+
+  return child;
+}
+
+const std::string XmlElement::TextNamed(const QName& name) const {
+  XmlChild* pChild;
+  for (pChild = first_child_; pChild; pChild = pChild->next_child_) {
+    if (!pChild->IsText() && pChild->AsElement()->Name() == name)
+      return pChild->AsElement()->BodyText();
+  }
+  return std::string();
+}
+
+void XmlElement::InsertChildAfter(XmlChild* predecessor, XmlChild* next) {
+  if (predecessor == NULL) {
+    next->next_child_ = first_child_;
+    first_child_ = next;
+  }
+  else {
+    next->next_child_ = predecessor->next_child_;
+    predecessor->next_child_ = next;
+  }
+}
+
+void XmlElement::RemoveChildAfter(XmlChild* predecessor) {
+  XmlChild* next;
+
+  if (predecessor == NULL) {
+    next = first_child_;
+    first_child_ = next->next_child_;
+  }
+  else {
+    next = predecessor->next_child_;
+    predecessor->next_child_ = next->next_child_;
+  }
+
+  if (last_child_ == next)
+    last_child_ = predecessor;
+
+  delete next;
+}
+
+void XmlElement::AddAttr(const QName& name, const std::string& value) {
+  ASSERT(!HasAttr(name));
+
+  XmlAttr ** pprev = last_attr_ ? &(last_attr_->next_attr_) : &first_attr_;
+  last_attr_ = (*pprev = new XmlAttr(name, value));
+}
+
+void XmlElement::AddAttr(const QName& name, const std::string& value,
+                         int depth) {
+  XmlElement* element = this;
+  while (depth--) {
+    element = element->last_child_->AsElement();
+  }
+  element->AddAttr(name, value);
+}
+
+void XmlElement::AddParsedText(const char* cstr, int len) {
+  if (len == 0)
+    return;
+
+  if (last_child_ && last_child_->IsText()) {
+    last_child_->AsText()->AddParsedText(cstr, len);
+    return;
+  }
+  XmlChild ** pprev = last_child_ ? &(last_child_->next_child_) : &first_child_;
+  last_child_ = *pprev = new XmlText(cstr, len);
+}
+
+void XmlElement::AddCDATAText(const char* buf, int len) {
+  cdata_ = true;
+  AddParsedText(buf, len);
+}
+
+void XmlElement::AddText(const std::string& text) {
+  if (text == STR_EMPTY)
+    return;
+
+  if (last_child_ && last_child_->IsText()) {
+    last_child_->AsText()->AddText(text);
+    return;
+  }
+  XmlChild ** pprev = last_child_ ? &(last_child_->next_child_) : &first_child_;
+  last_child_ = *pprev = new XmlText(text);
+}
+
+void XmlElement::AddText(const std::string& text, int depth) {
+  // note: the first syntax is ambigious for msvc 6
+  // XmlElement* pel(this);
+  XmlElement* element = this;
+  while (depth--) {
+    element = element->last_child_->AsElement();
+  }
+  element->AddText(text);
+}
+
+void XmlElement::AddElement(XmlElement *child) {
+  if (child == NULL)
+    return;
+
+  XmlChild ** pprev = last_child_ ? &(last_child_->next_child_) : &first_child_;
+  *pprev = child;
+  last_child_ = child;
+  child->next_child_ = NULL;
+}
+
+void XmlElement::AddElement(XmlElement *child, int depth) {
+  XmlElement* element = this;
+  while (depth--) {
+    element = element->last_child_->AsElement();
+  }
+  element->AddElement(child);
+}
+
+void XmlElement::ClearNamedChildren(const QName& name) {
+  XmlChild* prev_child = NULL;
+  XmlChild* next_child;
+  XmlChild* child;
+  for (child = FirstChild(); child; child = next_child) {
+    next_child = child->NextChild();
+    if (!child->IsText() && child->AsElement()->Name() == name)
+    {
+      RemoveChildAfter(prev_child);
+      continue;
+    }
+    prev_child = child;
+  }
+}
+
+void XmlElement::ClearAttributes() {
+  XmlAttr* attr;
+  for (attr = first_attr_; attr; ) {
+    XmlAttr* to_delete = attr;
+    attr = attr->next_attr_;
+    delete to_delete;
+  }
+  first_attr_ = last_attr_ = NULL;
+}
+
+void XmlElement::ClearChildren() {
+  XmlChild* pchild;
+  for (pchild = first_child_; pchild; ) {
+    XmlChild* to_delete = pchild;
+    pchild = pchild->next_child_;
+    delete to_delete;
+  }
+  first_child_ = last_child_ = NULL;
+}
+
+std::string XmlElement::Str() const {
+  std::stringstream ss;
+  XmlPrinter::PrintXml(&ss, this);
+  return ss.str();
+}
+
+XmlElement* XmlElement::ForStr(const std::string& str) {
+  XmlBuilder builder;
+  XmlParser::ParseXml(&builder, str);
+  return builder.CreateElement();
+}
+
+XmlElement::~XmlElement() {
+  XmlAttr* attr;
+  for (attr = first_attr_; attr; ) {
+    XmlAttr* to_delete = attr;
+    attr = attr->next_attr_;
+    delete to_delete;
+  }
+
+  XmlChild* pchild;
+  for (pchild = first_child_; pchild; ) {
+    XmlChild* to_delete = pchild;
+    pchild = pchild->next_child_;
+    delete to_delete;
+  }
+}
+
+}  // namespace buzz
diff --git a/talk/xmllite/xmlelement.h b/talk/xmllite/xmlelement.h
new file mode 100755
index 0000000..044d2c4
--- /dev/null
+++ b/talk/xmllite/xmlelement.h
@@ -0,0 +1,251 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_XMLLITE_XMLELEMENT_H_
+#define TALK_XMLLITE_XMLELEMENT_H_
+
+#include <iosfwd>
+#include <string>
+
+#include "talk/xmllite/qname.h"
+#include "webrtc/base/scoped_ptr.h"
+
+namespace buzz {
+
+class XmlChild;
+class XmlText;
+class XmlElement;
+class XmlAttr;
+
+class XmlChild {
+ public:
+  XmlChild* NextChild() { return next_child_; }
+  const XmlChild* NextChild() const { return next_child_; }
+
+  bool IsText() const { return IsTextImpl(); }
+
+  XmlElement* AsElement() { return AsElementImpl(); }
+  const XmlElement* AsElement() const { return AsElementImpl(); }
+
+  XmlText* AsText() { return AsTextImpl(); }
+  const XmlText* AsText() const { return AsTextImpl(); }
+
+
+ protected:
+  XmlChild() :
+    next_child_(NULL) {
+  }
+
+  virtual bool IsTextImpl() const = 0;
+  virtual XmlElement* AsElementImpl() const = 0;
+  virtual XmlText* AsTextImpl() const = 0;
+
+
+  virtual ~XmlChild();
+
+ private:
+  friend class XmlElement;
+
+  XmlChild(const XmlChild& noimpl);
+
+  XmlChild* next_child_;
+};
+
+class XmlText : public XmlChild {
+ public:
+  explicit XmlText(const std::string& text) :
+    XmlChild(),
+    text_(text) {
+  }
+  explicit XmlText(const XmlText& t) :
+    XmlChild(),
+    text_(t.text_) {
+  }
+  explicit XmlText(const char* cstr, size_t len) :
+    XmlChild(),
+    text_(cstr, len) {
+  }
+  virtual ~XmlText();
+
+  const std::string& Text() const { return text_; }
+  void SetText(const std::string& text);
+  void AddParsedText(const char* buf, int len);
+  void AddText(const std::string& text);
+
+ protected:
+  virtual bool IsTextImpl() const;
+  virtual XmlElement* AsElementImpl() const;
+  virtual XmlText* AsTextImpl() const;
+
+ private:
+  std::string text_;
+};
+
+class XmlAttr {
+ public:
+  XmlAttr* NextAttr() const { return next_attr_; }
+  const QName& Name() const { return name_; }
+  const std::string& Value() const { return value_; }
+
+ private:
+  friend class XmlElement;
+
+  explicit XmlAttr(const QName& name, const std::string& value) :
+    next_attr_(NULL),
+    name_(name),
+    value_(value) {
+  }
+  explicit XmlAttr(const XmlAttr& att) :
+    next_attr_(NULL),
+    name_(att.name_),
+    value_(att.value_) {
+  }
+
+  XmlAttr* next_attr_;
+  QName name_;
+  std::string value_;
+};
+
+class XmlElement : public XmlChild {
+ public:
+  explicit XmlElement(const QName& name);
+  explicit XmlElement(const QName& name, bool useDefaultNs);
+  explicit XmlElement(const XmlElement& elt);
+
+  virtual ~XmlElement();
+
+  const QName& Name() const { return name_; }
+  void SetName(const QName& name) { name_ = name; }
+
+  const std::string BodyText() const;
+  void SetBodyText(const std::string& text);
+
+  const QName FirstElementName() const;
+
+  XmlAttr* FirstAttr();
+  const XmlAttr* FirstAttr() const
+    { return const_cast<XmlElement *>(this)->FirstAttr(); }
+
+  // Attr will return an empty string if the attribute isn't there:
+  // use HasAttr to test presence of an attribute.
+  const std::string Attr(const StaticQName& name) const;
+  const std::string Attr(const QName& name) const;
+  bool HasAttr(const StaticQName& name) const;
+  bool HasAttr(const QName& name) const;
+  void SetAttr(const QName& name, const std::string& value);
+  void ClearAttr(const QName& name);
+
+  XmlChild* FirstChild();
+  const XmlChild* FirstChild() const {
+    return const_cast<XmlElement *>(this)->FirstChild();
+  }
+
+  XmlElement* FirstElement();
+  const XmlElement* FirstElement() const {
+    return const_cast<XmlElement *>(this)->FirstElement();
+  }
+
+  XmlElement* NextElement();
+  const XmlElement* NextElement() const {
+    return const_cast<XmlElement *>(this)->NextElement();
+  }
+
+  XmlElement* FirstWithNamespace(const std::string& ns);
+  const XmlElement* FirstWithNamespace(const std::string& ns) const {
+    return const_cast<XmlElement *>(this)->FirstWithNamespace(ns);
+  }
+
+  XmlElement* NextWithNamespace(const std::string& ns);
+  const XmlElement* NextWithNamespace(const std::string& ns) const {
+    return const_cast<XmlElement *>(this)->NextWithNamespace(ns);
+  }
+
+  XmlElement* FirstNamed(const StaticQName& name);
+  const XmlElement* FirstNamed(const StaticQName& name) const {
+    return const_cast<XmlElement *>(this)->FirstNamed(name);
+  }
+
+  XmlElement* FirstNamed(const QName& name);
+  const XmlElement* FirstNamed(const QName& name) const {
+    return const_cast<XmlElement *>(this)->FirstNamed(name);
+  }
+
+  XmlElement* NextNamed(const StaticQName& name);
+  const XmlElement* NextNamed(const StaticQName& name) const {
+    return const_cast<XmlElement *>(this)->NextNamed(name);
+  }
+
+  XmlElement* NextNamed(const QName& name);
+  const XmlElement* NextNamed(const QName& name) const {
+    return const_cast<XmlElement *>(this)->NextNamed(name);
+  }
+
+  // Finds the first element named 'name'.  If that element can't be found then
+  // adds one and returns it.
+  XmlElement* FindOrAddNamedChild(const QName& name);
+
+  const std::string TextNamed(const QName& name) const;
+
+  void InsertChildAfter(XmlChild* predecessor, XmlChild* new_child);
+  void RemoveChildAfter(XmlChild* predecessor);
+
+  void AddParsedText(const char* buf, int len);
+  // Note: CDATA is not supported by XMPP, therefore using this function will
+  // generate non-XMPP compatible XML.
+  void AddCDATAText(const char* buf, int len);
+  void AddText(const std::string& text);
+  void AddText(const std::string& text, int depth);
+  void AddElement(XmlElement* child);
+  void AddElement(XmlElement* child, int depth);
+  void AddAttr(const QName& name, const std::string& value);
+  void AddAttr(const QName& name, const std::string& value, int depth);
+  void ClearNamedChildren(const QName& name);
+  void ClearAttributes();
+  void ClearChildren();
+
+  static XmlElement* ForStr(const std::string& str);
+  std::string Str() const;
+
+  bool IsCDATA() const { return cdata_; }
+
+ protected:
+  virtual bool IsTextImpl() const;
+  virtual XmlElement* AsElementImpl() const;
+  virtual XmlText* AsTextImpl() const;
+
+ private:
+  QName name_;
+  XmlAttr* first_attr_;
+  XmlAttr* last_attr_;
+  XmlChild* first_child_;
+  XmlChild* last_child_;
+  bool cdata_;
+};
+
+}  // namespace buzz
+
+#endif  // TALK_XMLLITE_XMLELEMENT_H_
diff --git a/talk/xmllite/xmlelement_unittest.cc b/talk/xmllite/xmlelement_unittest.cc
new file mode 100755
index 0000000..5b2e106
--- /dev/null
+++ b/talk/xmllite/xmlelement_unittest.cc
@@ -0,0 +1,275 @@
+/*
+ * libjingle
+ * Copyright 2004, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <iostream>
+#include <sstream>
+#include <string>
+#include "talk/xmllite/xmlelement.h"
+#include "webrtc/base/common.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/thread.h"
+
+using buzz::QName;
+using buzz::XmlAttr;
+using buzz::XmlChild;
+using buzz::XmlElement;
+
+std::ostream& operator<<(std::ostream& os, const QName& name) {
+  os << name.Namespace() << ":" << name.LocalPart();
+  return os;
+}
+
+TEST(XmlElementTest, TestConstructors) {
+  XmlElement elt(QName("google:test", "first"));
+  EXPECT_EQ("<test:first xmlns:test=\"google:test\"/>", elt.Str());
+
+  XmlElement elt2(QName("google:test", "first"), true);
+  EXPECT_EQ("<first xmlns=\"google:test\"/>", elt2.Str());
+}
+
+TEST(XmlElementTest, TestAdd) {
+  XmlElement elt(QName("google:test", "root"), true);
+  elt.AddElement(new XmlElement(QName("google:test", "first")));
+  elt.AddElement(new XmlElement(QName("google:test", "nested")), 1);
+  elt.AddText("nested-value", 2);
+  elt.AddText("between-", 1);
+  elt.AddText("value", 1);
+  elt.AddElement(new XmlElement(QName("google:test", "nested2")), 1);
+  elt.AddElement(new XmlElement(QName("google:test", "second")));
+  elt.AddText("init-value", 1);
+  elt.AddElement(new XmlElement(QName("google:test", "nested3")), 1);
+  elt.AddText("trailing-value", 1);
+
+  // make sure it looks ok overall
+  EXPECT_EQ("<root xmlns=\"google:test\">"
+        "<first><nested>nested-value</nested>between-value<nested2/></first>"
+        "<second>init-value<nested3/>trailing-value</second></root>",
+        elt.Str());
+
+  // make sure text was concatenated
+  XmlChild * pchild =
+    elt.FirstChild()->AsElement()->FirstChild()->NextChild();
+  EXPECT_TRUE(pchild->IsText());
+  EXPECT_EQ("between-value", pchild->AsText()->Text());
+}
+
+TEST(XmlElementTest, TestAttrs) {
+  XmlElement elt(QName("", "root"));
+  elt.SetAttr(QName("", "a"), "avalue");
+  EXPECT_EQ("<root a=\"avalue\"/>", elt.Str());
+
+  elt.SetAttr(QName("", "b"), "bvalue");
+  EXPECT_EQ("<root a=\"avalue\" b=\"bvalue\"/>", elt.Str());
+
+  elt.SetAttr(QName("", "a"), "avalue2");
+  EXPECT_EQ("<root a=\"avalue2\" b=\"bvalue\"/>", elt.Str());
+
+  elt.SetAttr(QName("", "b"), "bvalue2");
+  EXPECT_EQ("<root a=\"avalue2\" b=\"bvalue2\"/>", elt.Str());
+
+  elt.SetAttr(QName("", "c"), "cvalue");
+  EXPECT_EQ("<root a=\"avalue2\" b=\"bvalue2\" c=\"cvalue\"/>", elt.Str());
+
+  XmlAttr * patt = elt.FirstAttr();
+  EXPECT_EQ(QName("", "a"), patt->Name());
+  EXPECT_EQ("avalue2", patt->Value());
+
+  patt = patt->NextAttr();
+  EXPECT_EQ(QName("", "b"), patt->Name());
+  EXPECT_EQ("bvalue2", patt->Value());
+
+  patt = patt->NextAttr();
+  EXPECT_EQ(QName("", "c"), patt->Name());
+  EXPECT_EQ("cvalue", patt->Value());
+
+  patt = patt->NextAttr();
+  EXPECT_TRUE(NULL == patt);
+
+  EXPECT_TRUE(elt.HasAttr(QName("", "a")));
+  EXPECT_TRUE(elt.HasAttr(QName("", "b")));
+  EXPECT_TRUE(elt.HasAttr(QName("", "c")));
+  EXPECT_FALSE(elt.HasAttr(QName("", "d")));
+
+  elt.SetAttr(QName("", "d"), "dvalue");
+  EXPECT_EQ("<root a=\"avalue2\" b=\"bvalue2\" c=\"cvalue\" d=\"dvalue\"/>",
+      elt.Str());
+  EXPECT_TRUE(elt.HasAttr(QName("", "d")));
+
+  elt.ClearAttr(QName("", "z"));  // not found, no effect
+  EXPECT_EQ("<root a=\"avalue2\" b=\"bvalue2\" c=\"cvalue\" d=\"dvalue\"/>",
+      elt.Str());
+
+  elt.ClearAttr(QName("", "b"));
+  EXPECT_EQ("<root a=\"avalue2\" c=\"cvalue\" d=\"dvalue\"/>", elt.Str());
+
+  elt.ClearAttr(QName("", "a"));
+  EXPECT_EQ("<root c=\"cvalue\" d=\"dvalue\"/>", elt.Str());
+
+  elt.ClearAttr(QName("", "d"));
+  EXPECT_EQ("<root c=\"cvalue\"/>", elt.Str());
+
+  elt.ClearAttr(QName("", "c"));
+  EXPECT_EQ("<root/>", elt.Str());
+}
+
+TEST(XmlElementTest, TestBodyText) {
+  XmlElement elt(QName("", "root"));
+  EXPECT_EQ("", elt.BodyText());
+
+  elt.AddText("body value text");
+
+  EXPECT_EQ("body value text", elt.BodyText());
+
+  elt.ClearChildren();
+  elt.AddText("more value ");
+  elt.AddText("text");
+
+  EXPECT_EQ("more value text", elt.BodyText());
+
+  elt.ClearChildren();
+  elt.AddText("decoy");
+  elt.AddElement(new XmlElement(QName("", "dummy")));
+  EXPECT_EQ("", elt.BodyText());
+
+  elt.SetBodyText("replacement");
+  EXPECT_EQ("replacement", elt.BodyText());
+
+  elt.SetBodyText("");
+  EXPECT_TRUE(NULL == elt.FirstChild());
+
+  elt.SetBodyText("goodbye");
+  EXPECT_EQ("goodbye", elt.FirstChild()->AsText()->Text());
+  EXPECT_EQ("goodbye", elt.BodyText());
+}
+
+TEST(XmlElementTest, TestCopyConstructor) {
+  XmlElement * element = XmlElement::ForStr(
+      "<root xmlns='test-foo'>This is a <em a='avalue' b='bvalue'>"
+      "little <b>little</b></em> test</root>");
+
+  XmlElement * pelCopy = new XmlElement(*element);
+  EXPECT_EQ("<root xmlns=\"test-foo\">This is a <em a=\"avalue\" b=\"bvalue\">"
+      "little <b>little</b></em> test</root>", pelCopy->Str());
+  delete pelCopy;
+
+  pelCopy = new XmlElement(*(element->FirstChild()->NextChild()->AsElement()));
+  EXPECT_EQ("<foo:em a=\"avalue\" b=\"bvalue\" xmlns:foo=\"test-foo\">"
+      "little <foo:b>little</foo:b></foo:em>", pelCopy->Str());
+
+  XmlAttr * patt = pelCopy->FirstAttr();
+  EXPECT_EQ(QName("", "a"), patt->Name());
+  EXPECT_EQ("avalue", patt->Value());
+
+  patt = patt->NextAttr();
+  EXPECT_EQ(QName("", "b"), patt->Name());
+  EXPECT_EQ("bvalue", patt->Value());
+
+  patt = patt->NextAttr();
+  EXPECT_TRUE(NULL == patt);
+  delete pelCopy;
+  delete element;
+}
+
+TEST(XmlElementTest, TestNameSearch) {
+  XmlElement * element = XmlElement::ForStr(
+    "<root xmlns='test-foo'>"
+      "<firstname>George</firstname>"
+      "<middlename>X.</middlename>"
+      "some text"
+      "<lastname>Harrison</lastname>"
+      "<firstname>John</firstname>"
+      "<middlename>Y.</middlename>"
+      "<lastname>Lennon</lastname>"
+    "</root>");
+  EXPECT_TRUE(NULL ==
+      element->FirstNamed(QName("", "firstname")));
+  EXPECT_EQ(element->FirstChild(),
+      element->FirstNamed(QName("test-foo", "firstname")));
+  EXPECT_EQ(element->FirstChild()->NextChild(),
+      element->FirstNamed(QName("test-foo", "middlename")));
+  EXPECT_EQ(element->FirstElement()->NextElement(),
+      element->FirstNamed(QName("test-foo", "middlename")));
+  EXPECT_EQ("Harrison",
+      element->TextNamed(QName("test-foo", "lastname")));
+  EXPECT_EQ(element->FirstElement()->NextElement()->NextElement(),
+      element->FirstNamed(QName("test-foo", "lastname")));
+  EXPECT_EQ("John", element->FirstNamed(QName("test-foo", "firstname"))->
+      NextNamed(QName("test-foo", "firstname"))->BodyText());
+  EXPECT_EQ("Y.", element->FirstNamed(QName("test-foo", "middlename"))->
+      NextNamed(QName("test-foo", "middlename"))->BodyText());
+  EXPECT_EQ("Lennon", element->FirstNamed(QName("test-foo", "lastname"))->
+      NextNamed(QName("test-foo", "lastname"))->BodyText());
+  EXPECT_TRUE(NULL == element->FirstNamed(QName("test-foo", "firstname"))->
+      NextNamed(QName("test-foo", "firstname"))->
+      NextNamed(QName("test-foo", "firstname")));
+
+  delete element;
+}
+
+class XmlElementCreatorThread : public rtc::Thread {
+ public:
+  XmlElementCreatorThread(int count, buzz::QName qname) :
+      count_(count), qname_(qname) {}
+
+  virtual ~XmlElementCreatorThread() {
+    Stop();
+  }
+
+  virtual void Run() {
+    std::vector<buzz::XmlElement*> elems;
+    for (int i = 0; i < count_; i++) {
+      elems.push_back(new XmlElement(qname_));
+    }
+    for (int i = 0; i < count_; i++) {
+      delete elems[i];
+    }
+  }
+
+ private:
+  int count_;
+  buzz::QName qname_;
+};
+
+// If XmlElement creation and destruction isn't thread safe,
+// this test should crash.
+TEST(XmlElementTest, TestMultithread) {
+  int thread_count = 2;  // Was 100, but that's too slow.
+  int elem_count = 100;  // Was 100000, but that's too slow.
+  buzz::QName qname("foo", "bar");
+
+  std::vector<rtc::Thread*> threads;
+  for (int i = 0; i < thread_count; i++) {
+    threads.push_back(
+        new XmlElementCreatorThread(elem_count, qname));
+    threads[i]->Start();
+  }
+
+  for (int i = 0; i < thread_count; i++) {
+    threads[i]->Stop();
+    delete threads[i];
+  }
+}
diff --git a/talk/xmllite/xmlnsstack.cc b/talk/xmllite/xmlnsstack.cc
new file mode 100755
index 0000000..bc66b4f
--- /dev/null
+++ b/talk/xmllite/xmlnsstack.cc
@@ -0,0 +1,195 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/xmllite/xmlnsstack.h"
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "talk/xmllite/xmlconstants.h"
+#include "talk/xmllite/xmlelement.h"
+
+namespace buzz {
+
+XmlnsStack::XmlnsStack() :
+  pxmlnsStack_(new std::vector<std::string>),
+  pxmlnsDepthStack_(new std::vector<size_t>) {
+}
+
+XmlnsStack::~XmlnsStack() {}
+
+void XmlnsStack::PushFrame() {
+  pxmlnsDepthStack_->push_back(pxmlnsStack_->size());
+}
+
+void XmlnsStack::PopFrame() {
+  size_t prev_size = pxmlnsDepthStack_->back();
+  pxmlnsDepthStack_->pop_back();
+  if (prev_size < pxmlnsStack_->size()) {
+    pxmlnsStack_->erase(pxmlnsStack_->begin() + prev_size,
+                        pxmlnsStack_->end());
+  }
+}
+
+std::pair<std::string, bool> XmlnsStack::NsForPrefix(
+    const std::string& prefix) {
+  if (prefix.length() >= 3 &&
+      (prefix[0] == 'x' || prefix[0] == 'X') &&
+      (prefix[1] == 'm' || prefix[1] == 'M') &&
+      (prefix[2] == 'l' || prefix[2] == 'L')) {
+    if (prefix == "xml")
+      return std::make_pair(NS_XML, true);
+    if (prefix == "xmlns")
+      return std::make_pair(NS_XMLNS, true);
+    // Other names with xml prefix are illegal.
+    return std::make_pair(STR_EMPTY, false);
+  }
+
+  std::vector<std::string>::iterator pos;
+  for (pos = pxmlnsStack_->end(); pos > pxmlnsStack_->begin(); ) {
+    pos -= 2;
+    if (*pos == prefix)
+      return std::make_pair(*(pos + 1), true);
+  }
+
+  if (prefix == STR_EMPTY)
+    return std::make_pair(STR_EMPTY, true);  // default namespace
+
+  return std::make_pair(STR_EMPTY, false);  // none found
+}
+
+bool XmlnsStack::PrefixMatchesNs(const std::string& prefix,
+                                 const std::string& ns) {
+  const std::pair<std::string, bool> match = NsForPrefix(prefix);
+  return match.second && (match.first == ns);
+}
+
+std::pair<std::string, bool> XmlnsStack::PrefixForNs(const std::string& ns,
+                                                     bool isattr) {
+  if (ns == NS_XML)
+    return std::make_pair(std::string("xml"), true);
+  if (ns == NS_XMLNS)
+    return std::make_pair(std::string("xmlns"), true);
+  if (isattr ? ns == STR_EMPTY : PrefixMatchesNs(STR_EMPTY, ns))
+    return std::make_pair(STR_EMPTY, true);
+
+  std::vector<std::string>::iterator pos;
+  for (pos = pxmlnsStack_->end(); pos > pxmlnsStack_->begin(); ) {
+    pos -= 2;
+    if (*(pos + 1) == ns &&
+        (!isattr || !pos->empty()) && PrefixMatchesNs(*pos, ns))
+      return std::make_pair(*pos, true);
+  }
+
+  return std::make_pair(STR_EMPTY, false); // none found
+}
+
+std::string XmlnsStack::FormatQName(const QName& name, bool isAttr) {
+  std::string prefix(PrefixForNs(name.Namespace(), isAttr).first);
+  if (prefix == STR_EMPTY)
+    return name.LocalPart();
+  else
+    return prefix + ':' + name.LocalPart();
+}
+
+void XmlnsStack::AddXmlns(const std::string & prefix, const std::string & ns) {
+  pxmlnsStack_->push_back(prefix);
+  pxmlnsStack_->push_back(ns);
+}
+
+void XmlnsStack::RemoveXmlns() {
+  pxmlnsStack_->pop_back();
+  pxmlnsStack_->pop_back();
+}
+
+static bool IsAsciiLetter(char ch) {
+  return ((ch >= 'a' && ch <= 'z') ||
+          (ch >= 'A' && ch <= 'Z'));
+}
+
+static std::string AsciiLower(const std::string & s) {
+  std::string result(s);
+  size_t i;
+  for (i = 0; i < result.length(); i++) {
+    if (result[i] >= 'A' && result[i] <= 'Z')
+      result[i] += 'a' - 'A';
+  }
+  return result;
+}
+
+static std::string SuggestPrefix(const std::string & ns) {
+  size_t len = ns.length();
+  size_t i = ns.find_last_of('.');
+  if (i != std::string::npos && len - i <= 4 + 1)
+    len = i; // chop off ".html" or ".xsd" or ".?{0,4}"
+  size_t last = len;
+  while (last > 0) {
+    last -= 1;
+    if (IsAsciiLetter(ns[last])) {
+      size_t first = last;
+      last += 1;
+      while (first > 0) {
+        if (!IsAsciiLetter(ns[first - 1]))
+          break;
+        first -= 1;
+      }
+      if (last - first > 4)
+        last = first + 3;
+      std::string candidate(AsciiLower(ns.substr(first, last - first)));
+      if (candidate.find("xml") != 0)
+        return candidate;
+      break;
+    }
+  }
+  return "ns";
+}
+
+std::pair<std::string, bool> XmlnsStack::AddNewPrefix(const std::string& ns,
+                                                      bool isAttr) {
+  if (PrefixForNs(ns, isAttr).second)
+    return std::make_pair(STR_EMPTY, false);
+
+  std::string base(SuggestPrefix(ns));
+  std::string result(base);
+  int i = 2;
+  while (NsForPrefix(result).second) {
+    std::stringstream ss;
+    ss << base;
+    ss << (i++);
+    ss >> result;
+  }
+  AddXmlns(result, ns);
+  return std::make_pair(result, true);
+}
+
+void XmlnsStack::Reset() {
+  pxmlnsStack_->clear();
+  pxmlnsDepthStack_->clear();
+}
+
+}
diff --git a/talk/xmllite/xmlnsstack.h b/talk/xmllite/xmlnsstack.h
new file mode 100755
index 0000000..a3c4368
--- /dev/null
+++ b/talk/xmllite/xmlnsstack.h
@@ -0,0 +1,62 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_XMLLITE_XMLNSSTACK_H_
+#define TALK_XMLLITE_XMLNSSTACK_H_
+
+#include <string>
+#include <vector>
+#include "talk/xmllite/qname.h"
+#include "webrtc/base/scoped_ptr.h"
+
+namespace buzz {
+
+class XmlnsStack {
+public:
+  XmlnsStack();
+  ~XmlnsStack();
+
+  void AddXmlns(const std::string& prefix, const std::string& ns);
+  void RemoveXmlns();
+  void PushFrame();
+  void PopFrame();
+  void Reset();
+
+  std::pair<std::string, bool> NsForPrefix(const std::string& prefix);
+  bool PrefixMatchesNs(const std::string & prefix, const std::string & ns);
+  std::pair<std::string, bool> PrefixForNs(const std::string& ns, bool isAttr);
+  std::pair<std::string, bool> AddNewPrefix(const std::string& ns, bool isAttr);
+  std::string FormatQName(const QName & name, bool isAttr);
+
+private:
+
+  rtc::scoped_ptr<std::vector<std::string> > pxmlnsStack_;
+  rtc::scoped_ptr<std::vector<size_t> > pxmlnsDepthStack_;
+};
+}
+
+#endif  // TALK_XMLLITE_XMLNSSTACK_H_
diff --git a/talk/xmllite/xmlnsstack_unittest.cc b/talk/xmllite/xmlnsstack_unittest.cc
new file mode 100755
index 0000000..82a39dd
--- /dev/null
+++ b/talk/xmllite/xmlnsstack_unittest.cc
@@ -0,0 +1,258 @@
+/*
+ * libjingle
+ * Copyright 2004, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/xmllite/xmlnsstack.h"
+
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include "talk/xmllite/xmlconstants.h"
+#include "webrtc/base/common.h"
+#include "webrtc/base/gunit.h"
+
+using buzz::NS_XML;
+using buzz::NS_XMLNS;
+using buzz::QName;
+using buzz::XmlnsStack;
+
+TEST(XmlnsStackTest, TestBuiltin) {
+  XmlnsStack stack;
+
+  EXPECT_EQ(std::string(NS_XML), stack.NsForPrefix("xml").first);
+  EXPECT_EQ(std::string(NS_XMLNS), stack.NsForPrefix("xmlns").first);
+  EXPECT_EQ("", stack.NsForPrefix("").first);
+
+  EXPECT_EQ("xml", stack.PrefixForNs(NS_XML, false).first);
+  EXPECT_EQ("xmlns", stack.PrefixForNs(NS_XMLNS, false).first);
+  EXPECT_EQ("", stack.PrefixForNs("", false).first);
+  EXPECT_EQ("", stack.PrefixForNs("", true).first);
+}
+
+TEST(XmlnsStackTest, TestNsForPrefix) {
+ XmlnsStack stack;
+  stack.AddXmlns("pre1", "ns1");
+  stack.AddXmlns("pre2", "ns2");
+  stack.AddXmlns("pre1", "ns3");
+  stack.AddXmlns("", "ns4");
+
+  EXPECT_EQ("ns3", stack.NsForPrefix("pre1").first);
+  EXPECT_TRUE(stack.NsForPrefix("pre1").second);
+  EXPECT_EQ("ns2", stack.NsForPrefix("pre2").first);
+  EXPECT_EQ("ns4", stack.NsForPrefix("").first);
+  EXPECT_EQ("", stack.NsForPrefix("pre3").first);
+  EXPECT_FALSE(stack.NsForPrefix("pre3").second);
+}
+
+TEST(XmlnsStackTest, TestPrefixForNs) {
+  XmlnsStack stack;
+  stack.AddXmlns("pre1", "ns1");
+  stack.AddXmlns("pre2", "ns2");
+  stack.AddXmlns("pre1", "ns3");
+  stack.AddXmlns("pre3", "ns2");
+  stack.AddXmlns("pre4", "ns4");
+  stack.AddXmlns("", "ns4");
+
+  EXPECT_EQ("", stack.PrefixForNs("ns1", false).first);
+  EXPECT_FALSE(stack.PrefixForNs("ns1", false).second);
+  EXPECT_EQ("", stack.PrefixForNs("ns1", true).first);
+  EXPECT_FALSE(stack.PrefixForNs("ns1", true).second);
+  EXPECT_EQ("pre3", stack.PrefixForNs("ns2", false).first);
+  EXPECT_TRUE(stack.PrefixForNs("ns2", false).second);
+  EXPECT_EQ("pre3", stack.PrefixForNs("ns2", true).first);
+  EXPECT_TRUE(stack.PrefixForNs("ns2", true).second);
+  EXPECT_EQ("pre1", stack.PrefixForNs("ns3", false).first);
+  EXPECT_EQ("pre1", stack.PrefixForNs("ns3", true).first);
+  EXPECT_EQ("", stack.PrefixForNs("ns4", false).first);
+  EXPECT_TRUE(stack.PrefixForNs("ns4", false).second);
+  EXPECT_EQ("pre4", stack.PrefixForNs("ns4", true).first);
+  EXPECT_EQ("", stack.PrefixForNs("ns5", false).first);
+  EXPECT_FALSE(stack.PrefixForNs("ns5", false).second);
+  EXPECT_EQ("", stack.PrefixForNs("ns5", true).first);
+  EXPECT_EQ("", stack.PrefixForNs("", false).first);
+  EXPECT_EQ("", stack.PrefixForNs("", true).first);
+
+  stack.AddXmlns("", "ns6");
+  EXPECT_EQ("", stack.PrefixForNs("ns6", false).first);
+  EXPECT_TRUE(stack.PrefixForNs("ns6", false).second);
+  EXPECT_EQ("", stack.PrefixForNs("ns6", true).first);
+  EXPECT_FALSE(stack.PrefixForNs("ns6", true).second);
+}
+
+TEST(XmlnsStackTest, TestFrames) {
+  XmlnsStack stack;
+  stack.PushFrame();
+  stack.AddXmlns("pre1", "ns1");
+  stack.AddXmlns("pre2", "ns2");
+
+  stack.PushFrame();
+  stack.AddXmlns("pre1", "ns3");
+  stack.AddXmlns("pre3", "ns2");
+  stack.AddXmlns("pre4", "ns4");
+
+  stack.PushFrame();
+  stack.PushFrame();
+  stack.AddXmlns("", "ns4");
+
+  // basic test
+  EXPECT_EQ("ns3", stack.NsForPrefix("pre1").first);
+  EXPECT_EQ("ns2", stack.NsForPrefix("pre2").first);
+  EXPECT_EQ("ns2", stack.NsForPrefix("pre3").first);
+  EXPECT_EQ("ns4", stack.NsForPrefix("pre4").first);
+  EXPECT_EQ("", stack.NsForPrefix("pre5").first);
+  EXPECT_FALSE(stack.NsForPrefix("pre5").second);
+  EXPECT_EQ("ns4", stack.NsForPrefix("").first);
+  EXPECT_TRUE(stack.NsForPrefix("").second);
+
+  // pop the default xmlns definition
+  stack.PopFrame();
+  EXPECT_EQ("ns3", stack.NsForPrefix("pre1").first);
+  EXPECT_EQ("ns2", stack.NsForPrefix("pre2").first);
+  EXPECT_EQ("ns2", stack.NsForPrefix("pre3").first);
+  EXPECT_EQ("ns4", stack.NsForPrefix("pre4").first);
+  EXPECT_EQ("", stack.NsForPrefix("pre5").first);
+  EXPECT_FALSE(stack.NsForPrefix("pre5").second);
+  EXPECT_EQ("", stack.NsForPrefix("").first);
+  EXPECT_TRUE(stack.NsForPrefix("").second);
+
+  // pop empty frame (nop)
+  stack.PopFrame();
+  EXPECT_EQ("ns3", stack.NsForPrefix("pre1").first);
+  EXPECT_EQ("ns2", stack.NsForPrefix("pre2").first);
+  EXPECT_EQ("ns2", stack.NsForPrefix("pre3").first);
+  EXPECT_EQ("ns4", stack.NsForPrefix("pre4").first);
+  EXPECT_EQ("", stack.NsForPrefix("pre5").first);
+  EXPECT_FALSE(stack.NsForPrefix("pre5").second);
+  EXPECT_EQ("", stack.NsForPrefix("").first);
+  EXPECT_TRUE(stack.NsForPrefix("").second);
+
+  // pop frame with three defs
+  stack.PopFrame();
+  EXPECT_EQ("ns1", stack.NsForPrefix("pre1").first);
+  EXPECT_EQ("ns2", stack.NsForPrefix("pre2").first);
+  EXPECT_EQ("", stack.NsForPrefix("pre3").first);
+  EXPECT_FALSE(stack.NsForPrefix("pre3").second);
+  EXPECT_EQ("", stack.NsForPrefix("pre4").first);
+  EXPECT_FALSE(stack.NsForPrefix("pre4").second);
+  EXPECT_EQ("", stack.NsForPrefix("pre5").first);
+  EXPECT_FALSE(stack.NsForPrefix("pre5").second);
+  EXPECT_EQ("", stack.NsForPrefix("").first);
+  EXPECT_TRUE(stack.NsForPrefix("").second);
+
+  // pop frame with last two defs
+  stack.PopFrame();
+  EXPECT_FALSE(stack.NsForPrefix("pre1").second);
+  EXPECT_FALSE(stack.NsForPrefix("pre2").second);
+  EXPECT_FALSE(stack.NsForPrefix("pre3").second);
+  EXPECT_FALSE(stack.NsForPrefix("pre4").second);
+  EXPECT_FALSE(stack.NsForPrefix("pre5").second);
+  EXPECT_TRUE(stack.NsForPrefix("").second);
+  EXPECT_EQ("", stack.NsForPrefix("pre1").first);
+  EXPECT_EQ("", stack.NsForPrefix("pre2").first);
+  EXPECT_EQ("", stack.NsForPrefix("pre3").first);
+  EXPECT_EQ("", stack.NsForPrefix("pre4").first);
+  EXPECT_EQ("", stack.NsForPrefix("pre5").first);
+  EXPECT_EQ("", stack.NsForPrefix("").first);
+}
+
+TEST(XmlnsStackTest, TestAddNewPrefix) {
+  XmlnsStack stack;
+
+  // builtin namespaces cannot be added
+  EXPECT_FALSE(stack.AddNewPrefix("", true).second);
+  EXPECT_FALSE(stack.AddNewPrefix("", false).second);
+  EXPECT_FALSE(stack.AddNewPrefix(NS_XML, true).second);
+  EXPECT_FALSE(stack.AddNewPrefix(NS_XML, false).second);
+  EXPECT_FALSE(stack.AddNewPrefix(NS_XMLNS, true).second);
+  EXPECT_FALSE(stack.AddNewPrefix(NS_XMLNS, false).second);
+
+  // namespaces already added cannot be added again.
+  EXPECT_EQ("foo", stack.AddNewPrefix("http://a.b.com/foo.htm", true).first);
+  EXPECT_EQ("bare", stack.AddNewPrefix("http://a.b.com/bare", false).first);
+  EXPECT_EQ("z", stack.AddNewPrefix("z", false).first);
+  EXPECT_FALSE(stack.AddNewPrefix("http://a.b.com/foo.htm", true).second);
+  EXPECT_FALSE(stack.AddNewPrefix("http://a.b.com/bare", true).second);
+  EXPECT_FALSE(stack.AddNewPrefix("z", true).second);
+  EXPECT_FALSE(stack.AddNewPrefix("http://a.b.com/foo.htm", false).second);
+  EXPECT_FALSE(stack.AddNewPrefix("http://a.b.com/bare", false).second);
+  EXPECT_FALSE(stack.AddNewPrefix("z", false).second);
+
+  // default namespace usable by non-attributes only
+  stack.AddXmlns("", "http://my/default");
+  EXPECT_FALSE(stack.AddNewPrefix("http://my/default", false).second);
+  EXPECT_EQ("def", stack.AddNewPrefix("http://my/default", true).first);
+
+  // namespace cannot start with 'xml'
+  EXPECT_EQ("ns", stack.AddNewPrefix("http://a.b.com/xmltest", true).first);
+  EXPECT_EQ("ns2", stack.AddNewPrefix("xmlagain", false).first);
+
+  // verify added namespaces are still defined
+  EXPECT_EQ("http://a.b.com/foo.htm", stack.NsForPrefix("foo").first);
+  EXPECT_TRUE(stack.NsForPrefix("foo").second);
+  EXPECT_EQ("http://a.b.com/bare", stack.NsForPrefix("bare").first);
+  EXPECT_TRUE(stack.NsForPrefix("bare").second);
+  EXPECT_EQ("z", stack.NsForPrefix("z").first);
+  EXPECT_TRUE(stack.NsForPrefix("z").second);
+  EXPECT_EQ("http://my/default", stack.NsForPrefix("").first);
+  EXPECT_TRUE(stack.NsForPrefix("").second);
+  EXPECT_EQ("http://my/default", stack.NsForPrefix("def").first);
+  EXPECT_TRUE(stack.NsForPrefix("def").second);
+  EXPECT_EQ("http://a.b.com/xmltest", stack.NsForPrefix("ns").first);
+  EXPECT_TRUE(stack.NsForPrefix("ns").second);
+  EXPECT_EQ("xmlagain", stack.NsForPrefix("ns2").first);
+  EXPECT_TRUE(stack.NsForPrefix("ns2").second);
+}
+
+TEST(XmlnsStackTest, TestFormatQName) {
+  XmlnsStack stack;
+  stack.AddXmlns("pre1", "ns1");
+  stack.AddXmlns("pre2", "ns2");
+  stack.AddXmlns("pre1", "ns3");
+  stack.AddXmlns("", "ns4");
+
+  EXPECT_EQ("zip",
+      stack.FormatQName(QName("ns1", "zip"), false));  // no match
+  EXPECT_EQ("pre2:abracadabra",
+      stack.FormatQName(QName("ns2", "abracadabra"), false));
+  EXPECT_EQ("pre1:a",
+      stack.FormatQName(QName("ns3", "a"), false));
+  EXPECT_EQ("simple",
+      stack.FormatQName(QName("ns4", "simple"), false));
+  EXPECT_EQ("root",
+      stack.FormatQName(QName("", "root"), false));  // no match
+
+  EXPECT_EQ("zip",
+      stack.FormatQName(QName("ns1", "zip"), true));  // no match
+  EXPECT_EQ("pre2:abracadabra",
+      stack.FormatQName(QName("ns2", "abracadabra"), true));
+  EXPECT_EQ("pre1:a",
+      stack.FormatQName(QName("ns3", "a"), true));
+  EXPECT_EQ("simple",
+      stack.FormatQName(QName("ns4", "simple"), true));  // no match
+  EXPECT_EQ("root",
+      stack.FormatQName(QName("", "root"), true));
+}
diff --git a/talk/xmllite/xmlparser.cc b/talk/xmllite/xmlparser.cc
new file mode 100755
index 0000000..c985b1f
--- /dev/null
+++ b/talk/xmllite/xmlparser.cc
@@ -0,0 +1,278 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/xmllite/xmlparser.h"
+
+#include <string>
+#include <vector>
+
+#include "talk/xmllite/xmlconstants.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/xmlnsstack.h"
+#include "talk/xmllite/xmlnsstack.h"
+#include "webrtc/base/common.h"
+
+namespace buzz {
+
+
+static void
+StartElementCallback(void * userData, const char *name, const char **atts) {
+  (static_cast<XmlParser *>(userData))->ExpatStartElement(name, atts);
+}
+
+static void
+EndElementCallback(void * userData, const char *name) {
+  (static_cast<XmlParser *>(userData))->ExpatEndElement(name);
+}
+
+static void
+CharacterDataCallback(void * userData, const char *text, int len) {
+  (static_cast<XmlParser *>(userData))->ExpatCharacterData(text, len);
+}
+
+static void
+XmlDeclCallback(void * userData, const char * ver, const char * enc, int st) {
+  (static_cast<XmlParser *>(userData))->ExpatXmlDecl(ver, enc, st);
+}
+
+XmlParser::XmlParser(XmlParseHandler *pxph) :
+    pxph_(pxph), sentError_(false) {
+  expat_ = XML_ParserCreate(NULL);
+  XML_SetUserData(expat_, this);
+  XML_SetElementHandler(expat_, StartElementCallback, EndElementCallback);
+  XML_SetCharacterDataHandler(expat_, CharacterDataCallback);
+  XML_SetXmlDeclHandler(expat_, XmlDeclCallback);
+}
+
+void
+XmlParser::Reset() {
+  if (!XML_ParserReset(expat_, NULL)) {
+    XML_ParserFree(expat_);
+    expat_ = XML_ParserCreate(NULL);
+  }
+  XML_SetUserData(expat_, this);
+  XML_SetElementHandler(expat_, StartElementCallback, EndElementCallback);
+  XML_SetCharacterDataHandler(expat_, CharacterDataCallback);
+  XML_SetXmlDeclHandler(expat_, XmlDeclCallback);
+  context_.Reset();
+  sentError_ = false;
+}
+
+static bool
+XmlParser_StartsWithXmlns(const char *name) {
+  return name[0] == 'x' &&
+         name[1] == 'm' &&
+         name[2] == 'l' &&
+         name[3] == 'n' &&
+         name[4] == 's';
+}
+
+void
+XmlParser::ExpatStartElement(const char *name, const char **atts) {
+  if (context_.RaisedError() != XML_ERROR_NONE)
+    return;
+  const char **att;
+  context_.StartElement();
+  for (att = atts; *att; att += 2) {
+    if (XmlParser_StartsWithXmlns(*att)) {
+      if ((*att)[5] == '\0') {
+        context_.StartNamespace("", *(att + 1));
+      }
+      else if ((*att)[5] == ':') {
+        if (**(att + 1) == '\0') {
+          // In XML 1.0 empty namespace illegal with prefix (not in 1.1)
+          context_.RaiseError(XML_ERROR_SYNTAX);
+          return;
+        }
+        context_.StartNamespace((*att) + 6, *(att + 1));
+      }
+    }
+  }
+  context_.SetPosition(XML_GetCurrentLineNumber(expat_),
+                       XML_GetCurrentColumnNumber(expat_),
+                       XML_GetCurrentByteIndex(expat_));
+  pxph_->StartElement(&context_, name, atts);
+}
+
+void
+XmlParser::ExpatEndElement(const char *name) {
+  if (context_.RaisedError() != XML_ERROR_NONE)
+    return;
+  context_.EndElement();
+  context_.SetPosition(XML_GetCurrentLineNumber(expat_),
+                       XML_GetCurrentColumnNumber(expat_),
+                       XML_GetCurrentByteIndex(expat_));
+  pxph_->EndElement(&context_, name);
+}
+
+void
+XmlParser::ExpatCharacterData(const char *text, int len) {
+  if (context_.RaisedError() != XML_ERROR_NONE)
+    return;
+  context_.SetPosition(XML_GetCurrentLineNumber(expat_),
+                       XML_GetCurrentColumnNumber(expat_),
+                       XML_GetCurrentByteIndex(expat_));
+  pxph_->CharacterData(&context_, text, len);
+}
+
+void
+XmlParser::ExpatXmlDecl(const char * ver, const char * enc, int standalone) {
+  if (context_.RaisedError() != XML_ERROR_NONE)
+    return;
+
+  if (ver && std::string("1.0") != ver) {
+    context_.RaiseError(XML_ERROR_SYNTAX);
+    return;
+  }
+
+  if (standalone == 0) {
+    context_.RaiseError(XML_ERROR_SYNTAX);
+    return;
+  }
+
+  if (enc && !((enc[0] == 'U' || enc[0] == 'u') &&
+               (enc[1] == 'T' || enc[1] == 't') &&
+               (enc[2] == 'F' || enc[2] == 'f') &&
+                enc[3] == '-' && enc[4] =='8')) {
+    context_.RaiseError(XML_ERROR_INCORRECT_ENCODING);
+    return;
+  }
+
+}
+
+bool
+XmlParser::Parse(const char *data, size_t len, bool isFinal) {
+  if (sentError_)
+    return false;
+
+  if (XML_Parse(expat_, data, static_cast<int>(len), isFinal) !=
+      XML_STATUS_OK) {
+    context_.SetPosition(XML_GetCurrentLineNumber(expat_),
+                         XML_GetCurrentColumnNumber(expat_),
+                         XML_GetCurrentByteIndex(expat_));
+    context_.RaiseError(XML_GetErrorCode(expat_));
+  }
+
+  if (context_.RaisedError() != XML_ERROR_NONE) {
+    sentError_ = true;
+    pxph_->Error(&context_, context_.RaisedError());
+    return false;
+  }
+
+  return true;
+}
+
+XmlParser::~XmlParser() {
+  XML_ParserFree(expat_);
+}
+
+void
+XmlParser::ParseXml(XmlParseHandler *pxph, std::string text) {
+  XmlParser parser(pxph);
+  parser.Parse(text.c_str(), text.length(), true);
+}
+
+XmlParser::ParseContext::ParseContext() :
+    xmlnsstack_(),
+    raised_(XML_ERROR_NONE),
+    line_number_(0),
+    column_number_(0),
+    byte_index_(0) {
+}
+
+void
+XmlParser::ParseContext::StartNamespace(const char *prefix, const char *ns) {
+  xmlnsstack_.AddXmlns(*prefix ? prefix : STR_EMPTY, ns);
+}
+
+void
+XmlParser::ParseContext::StartElement() {
+  xmlnsstack_.PushFrame();
+}
+
+void
+XmlParser::ParseContext::EndElement() {
+  xmlnsstack_.PopFrame();
+}
+
+QName
+XmlParser::ParseContext::ResolveQName(const char* qname, bool isAttr) {
+  const char *c;
+  for (c = qname; *c; ++c) {
+    if (*c == ':') {
+      const std::pair<std::string, bool> result =
+          xmlnsstack_.NsForPrefix(std::string(qname, c - qname));
+      if (!result.second)
+        return QName();
+      return QName(result.first, c + 1);
+    }
+  }
+  if (isAttr)
+    return QName(STR_EMPTY, qname);
+
+  std::pair<std::string, bool> result = xmlnsstack_.NsForPrefix(STR_EMPTY);
+  if (!result.second)
+    return QName();
+
+  return QName(result.first, qname);
+}
+
+void
+XmlParser::ParseContext::Reset() {
+  xmlnsstack_.Reset();
+  raised_ = XML_ERROR_NONE;
+}
+
+void
+XmlParser::ParseContext::SetPosition(int line, int column,
+                                          long byte_index) {
+  line_number_ = line;
+  column_number_ = column;
+  byte_index_ = byte_index;
+}
+
+void
+XmlParser::ParseContext::GetPosition(unsigned long * line,
+                                     unsigned long * column,
+                                     unsigned long * byte_index) {
+  if (line != NULL) {
+    *line = static_cast<unsigned long>(line_number_);
+  }
+
+  if (column != NULL) {
+    *column = static_cast<unsigned long>(column_number_);
+  }
+
+  if (byte_index != NULL) {
+    *byte_index = static_cast<unsigned long>(byte_index_);
+  }
+}
+
+XmlParser::ParseContext::~ParseContext() {
+}
+
+}  // namespace buzz
diff --git a/talk/xmllite/xmlparser.h b/talk/xmllite/xmlparser.h
new file mode 100755
index 0000000..4a79858
--- /dev/null
+++ b/talk/xmllite/xmlparser.h
@@ -0,0 +1,120 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_XMLLITE_XMLPARSER_H_
+#define TALK_XMLLITE_XMLPARSER_H_
+
+#include <string>
+
+#include "talk/xmllite/xmlnsstack.h"
+#ifdef EXPAT_RELATIVE_PATH
+#include "expat.h"
+#else
+#include "third_party/expat/v2_0_1/Source/lib/expat.h"
+#endif  // EXPAT_RELATIVE_PATH
+
+struct XML_ParserStruct;
+typedef struct XML_ParserStruct* XML_Parser;
+
+namespace buzz {
+
+class XmlParseHandler;
+class XmlParseContext;
+class XmlParser;
+
+class XmlParseContext {
+public:
+  virtual ~XmlParseContext() {}
+  virtual QName ResolveQName(const char * qname, bool isAttr) = 0;
+  virtual void RaiseError(XML_Error err) = 0;
+  virtual void GetPosition(unsigned long * line, unsigned long * column,
+                           unsigned long * byte_index) = 0;
+};
+
+class XmlParseHandler {
+public:
+  virtual ~XmlParseHandler() {}
+  virtual void StartElement(XmlParseContext * pctx,
+               const char * name, const char ** atts) = 0;
+  virtual void EndElement(XmlParseContext * pctx,
+               const char * name) = 0;
+  virtual void CharacterData(XmlParseContext * pctx,
+               const char * text, int len) = 0;
+  virtual void Error(XmlParseContext * pctx,
+               XML_Error errorCode) = 0;
+};
+
+class XmlParser {
+public:
+  static void ParseXml(XmlParseHandler * pxph, std::string text);
+
+  explicit XmlParser(XmlParseHandler * pxph);
+  bool Parse(const char * data, size_t len, bool isFinal);
+  void Reset();
+  virtual ~XmlParser();
+
+  // expat callbacks
+  void ExpatStartElement(const char * name, const char ** atts);
+  void ExpatEndElement(const char * name);
+  void ExpatCharacterData(const char * text, int len);
+  void ExpatXmlDecl(const char * ver, const char * enc, int standalone);
+
+private:
+
+  class ParseContext : public XmlParseContext {
+  public:
+    ParseContext();
+    virtual ~ParseContext();
+    virtual QName ResolveQName(const char * qname, bool isAttr);
+    virtual void RaiseError(XML_Error err) { if (!raised_) raised_ = err; }
+    virtual void GetPosition(unsigned long * line, unsigned long * column,
+                             unsigned long * byte_index);
+    XML_Error RaisedError() { return raised_; }
+    void Reset();
+
+    void StartElement();
+    void EndElement();
+    void StartNamespace(const char * prefix, const char * ns);
+    void SetPosition(int line, int column, long byte_index);
+
+  private:
+    XmlnsStack xmlnsstack_;
+    XML_Error raised_;
+    XML_Size line_number_;
+    XML_Size column_number_;
+    XML_Index byte_index_;
+  };
+
+  ParseContext context_;
+  XML_Parser expat_;
+  XmlParseHandler * pxph_;
+  bool sentError_;
+};
+
+}  // namespace buzz
+
+#endif  // TALK_XMLLITE_XMLPARSER_H_
diff --git a/talk/xmllite/xmlparser_unittest.cc b/talk/xmllite/xmlparser_unittest.cc
new file mode 100755
index 0000000..322f8be
--- /dev/null
+++ b/talk/xmllite/xmlparser_unittest.cc
@@ -0,0 +1,302 @@
+/*
+ * libjingle
+ * Copyright 2004, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <iostream>
+#include <sstream>
+#include <string>
+#include "talk/xmllite/qname.h"
+#include "talk/xmllite/xmlparser.h"
+#include "webrtc/base/common.h"
+#include "webrtc/base/gunit.h"
+
+using buzz::QName;
+using buzz::XmlParser;
+using buzz::XmlParseContext;
+using buzz::XmlParseHandler;
+
+class XmlParserTestHandler : public XmlParseHandler {
+ public:
+  virtual void StartElement(XmlParseContext * pctx,
+                            const char * name, const char ** atts) {
+    ss_ << "START (" << pctx->ResolveQName(name, false).Merged();
+    while (*atts) {
+      ss_ << ", " << pctx->ResolveQName(*atts, true).Merged()
+          << "='" << *(atts+1) << "'";
+      atts += 2;
+    }
+    ss_ << ") ";
+  }
+  virtual void EndElement(XmlParseContext * pctx, const char * name) {
+    RTC_UNUSED(pctx);
+    RTC_UNUSED(name);
+    ss_ << "END ";
+  }
+  virtual void CharacterData(XmlParseContext * pctx,
+                             const char * text, int len) {
+    RTC_UNUSED(pctx);
+    ss_ << "TEXT (" << std::string(text, len) << ") ";
+  }
+  virtual void Error(XmlParseContext * pctx, XML_Error code) {
+    RTC_UNUSED(pctx);
+    ss_ << "ERROR (" << static_cast<int>(code) << ") ";
+  }
+  virtual ~XmlParserTestHandler() {
+  }
+
+  std::string Str() {
+    return ss_.str();
+  }
+
+  std::string StrClear() {
+    std::string result = ss_.str();
+    ss_.str("");
+    return result;
+  }
+
+ private:
+  std::stringstream ss_;
+};
+
+
+TEST(XmlParserTest, TestTrivial) {
+  XmlParserTestHandler handler;
+  XmlParser::ParseXml(&handler, "<testing/>");
+  EXPECT_EQ("START (testing) END ", handler.Str());
+}
+
+TEST(XmlParserTest, TestAttributes) {
+  {
+    XmlParserTestHandler handler;
+    XmlParser::ParseXml(&handler, "<testing a='b'/>");
+    EXPECT_EQ("START (testing, a='b') END ", handler.Str());
+  }
+  {
+    XmlParserTestHandler handler;
+    XmlParser::ParseXml(&handler, "<testing e='' long='some text'/>");
+    EXPECT_EQ("START (testing, e='', long='some text') END ", handler.Str());
+  }
+}
+
+TEST(XmlParserTest, TestNesting) {
+  {
+    XmlParserTestHandler handler;
+    XmlParser::ParseXml(&handler,
+        "<top><first/><second><third></third></second></top>");
+    EXPECT_EQ("START (top) START (first) END START (second) START (third) "
+        "END END END ", handler.Str());
+  }
+  {
+    XmlParserTestHandler handler;
+    XmlParser::ParseXml(&handler, "<top><fifth><deeper><and><deeper/></and>"
+        "<sibling><leaf/></sibling></deeper></fifth><first/><second>"
+        "<third></third></second></top>");
+    EXPECT_EQ("START (top) START (fifth) START (deeper) START (and) START "
+            "(deeper) END END START (sibling) START (leaf) END END END "
+            "END START (first) END START (second) START (third) END END END ",
+            handler.Str());
+  }
+}
+
+TEST(XmlParserTest, TestXmlDecl) {
+  {
+    XmlParserTestHandler handler;
+    XmlParser::ParseXml(&handler, "<?xml version=\"1.0\"?><testing/>");
+    EXPECT_EQ("START (testing) END ", handler.Str());
+  }
+  {
+    XmlParserTestHandler handler;
+    XmlParser::ParseXml(&handler,
+        "<?xml version=\"1.0\" encoding=\"utf-8\"?><testing/>");
+    EXPECT_EQ("START (testing) END ", handler.Str());
+  }
+  {
+    XmlParserTestHandler handler;
+    XmlParser::ParseXml(&handler,
+        "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+        "<testing/>");
+    EXPECT_EQ("START (testing) END ", handler.Str());
+  }
+}
+
+TEST(XmlParserTest, TestNamespace) {
+  {
+    XmlParserTestHandler handler;
+    XmlParser::ParseXml(&handler, "<top xmlns='my-namespace' a='b'/>");
+    EXPECT_EQ("START (my-namespace:top, xmlns='my-namespace', a='b') END ",
+        handler.Str());
+  }
+  {
+    XmlParserTestHandler handler;
+    XmlParser::ParseXml(&handler, "<foo:top xmlns:foo='my-namespace' "
+          "a='b' foo:c='d'/>");
+    EXPECT_EQ("START (my-namespace:top, "
+        "http://www.w3.org/2000/xmlns/:foo='my-namespace', "
+        "a='b', my-namespace:c='d') END ", handler.Str());
+  }
+  {
+    XmlParserTestHandler handler;
+    XmlParser::ParseXml(&handler, "<top><nested xmlns='my-namespace'><leaf/>"
+        "</nested><sibling/></top>");
+    EXPECT_EQ("START (top) START (my-namespace:nested, xmlns='my-namespace') "
+        "START (my-namespace:leaf) END END START (sibling) END END ",
+        handler.Str());
+  }
+}
+
+TEST(XmlParserTest, TestIncremental) {
+  XmlParserTestHandler handler;
+  XmlParser parser(&handler);
+  std::string fragment;
+
+  fragment = "<stream:stream";
+  parser.Parse(fragment.c_str(), fragment.length(), false);
+  EXPECT_EQ("", handler.StrClear());
+
+  fragment = " id=\"abcdefg\" xmlns=\"";
+  parser.Parse(fragment.c_str(), fragment.length(), false);
+  EXPECT_EQ("", handler.StrClear());
+
+  fragment = "j:c\" xmlns:stream='hm";
+  parser.Parse(fragment.c_str(), fragment.length(), false);
+  EXPECT_EQ("", handler.StrClear());
+
+  fragment = "ph'><test";
+  parser.Parse(fragment.c_str(), fragment.length(), false);
+  EXPECT_EQ("START (hmph:stream, id='abcdefg', xmlns='j:c', "
+      "http://www.w3.org/2000/xmlns/:stream='hmph') ", handler.StrClear());
+
+  fragment = "ing/><again/>abracad";
+  parser.Parse(fragment.c_str(), fragment.length(), false);
+  EXPECT_EQ("START (j:c:testing) END START (j:c:again) END TEXT (abracad) ",
+      handler.StrClear());
+
+  fragment = "abra</stream:";
+  parser.Parse(fragment.c_str(), fragment.length(), false);
+  EXPECT_EQ("TEXT (abra) ", handler.StrClear());
+
+  fragment = "stream>";
+  parser.Parse(fragment.c_str(), fragment.length(), false);
+  EXPECT_EQ("END ", handler.StrClear());
+}
+
+TEST(XmlParserTest, TestReset) {
+  {
+    XmlParserTestHandler handler;
+    XmlParser parser(&handler);
+    std::string fragment;
+
+    fragment = "<top><first/><second><third></third>";
+    parser.Parse(fragment.c_str(), fragment.length(), false);
+    EXPECT_EQ("START (top) START (first) END START (second) START (third) END ",
+        handler.StrClear());
+
+    parser.Reset();
+    fragment = "<tip><first/><second><third></third>";
+    parser.Parse(fragment.c_str(), fragment.length(), false);
+    EXPECT_EQ("START (tip) START (first) END START (second) START (third) END ",
+        handler.StrClear());
+  }
+  {
+    XmlParserTestHandler handler;
+    XmlParser parser(&handler);
+    std::string fragment;
+
+    fragment = "<top xmlns='m'>";
+    parser.Parse(fragment.c_str(), fragment.length(), false);
+    EXPECT_EQ("START (m:top, xmlns='m') ", handler.StrClear());
+
+    fragment = "<testing/><frag";
+    parser.Parse(fragment.c_str(), fragment.length(), false);
+    EXPECT_EQ("START (m:testing) END ", handler.StrClear());
+
+    parser.Reset();
+    fragment = "<testing><fragment/";
+    parser.Parse(fragment.c_str(), fragment.length(), false);
+    EXPECT_EQ("START (testing) ", handler.StrClear());
+
+    fragment = ">";
+    parser.Parse(fragment.c_str(), fragment.length(), false);
+    EXPECT_EQ("START (fragment) END ", handler.StrClear());
+  }
+}
+
+TEST(XmlParserTest, TestError) {
+  {
+    XmlParserTestHandler handler;
+    XmlParser::ParseXml(&handler, "junk");
+    EXPECT_EQ("ERROR (2) ", handler.Str());
+  }
+  {
+    XmlParserTestHandler handler;
+    XmlParser::ParseXml(&handler, "<top/> garbage ");
+    EXPECT_EQ("START (top) END ERROR (9) ", handler.Str());
+  }
+  {
+    XmlParserTestHandler handler;
+    XmlParser::ParseXml(&handler, "<-hm->");
+    EXPECT_EQ("ERROR (4) ", handler.Str());
+  }
+  {
+    XmlParserTestHandler handler;
+    XmlParser::ParseXml(&handler, "<hello>&foobar;</hello>");
+    EXPECT_EQ("START (hello) ERROR (11) ", handler.Str());
+  }
+  {
+    XmlParserTestHandler handler;
+    XmlParser::ParseXml(&handler,
+        "<!DOCTYPE HTML PUBLIC \"foobar\" \"barfoo\">");
+    EXPECT_EQ("ERROR (3) ", handler.Str());
+  }
+  {
+    // XmlParser requires utf-8
+    XmlParserTestHandler handler;
+    XmlParser::ParseXml(&handler,
+        "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?><test/>");
+    EXPECT_EQ("ERROR (19) ", handler.Str());
+  }
+  {
+    // XmlParser requires version 1.0
+    XmlParserTestHandler handler;
+    XmlParser::ParseXml(&handler,
+        "<?xml version=\"2.0\"?><test/>");
+    EXPECT_EQ("ERROR (2) ", handler.Str());
+  }
+  {
+    // XmlParser requires standalone documents
+    XmlParserTestHandler handler;
+    XmlParser::ParseXml(&handler,
+        "<?xml version=\"1.0\" standalone=\"no\"?><test/>");
+    EXPECT_EQ("ERROR (2) ", handler.Str());
+  }
+  {
+    // XmlParser doesn't like empty namespace URIs
+    XmlParserTestHandler handler;
+    XmlParser::ParseXml(&handler,
+        "<test xmlns:foo='' foo:bar='huh?'>");
+    EXPECT_EQ("ERROR (2) ", handler.Str());
+  }
+}
diff --git a/talk/xmllite/xmlprinter.cc b/talk/xmllite/xmlprinter.cc
new file mode 100755
index 0000000..1350454
--- /dev/null
+++ b/talk/xmllite/xmlprinter.cc
@@ -0,0 +1,191 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/xmllite/xmlprinter.h"
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "talk/xmllite/xmlconstants.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/xmlnsstack.h"
+
+namespace buzz {
+
+class XmlPrinterImpl {
+public:
+  XmlPrinterImpl(std::ostream* pout, XmlnsStack* ns_stack);
+  void PrintElement(const XmlElement* element);
+  void PrintQuotedValue(const std::string& text);
+  void PrintBodyText(const std::string& text);
+  void PrintCDATAText(const std::string& text);
+
+private:
+  std::ostream *pout_;
+  XmlnsStack* ns_stack_;
+};
+
+void XmlPrinter::PrintXml(std::ostream* pout, const XmlElement* element) {
+  XmlnsStack ns_stack;
+  PrintXml(pout, element, &ns_stack);
+}
+
+void XmlPrinter::PrintXml(std::ostream* pout, const XmlElement* element,
+                          XmlnsStack* ns_stack) {
+  XmlPrinterImpl printer(pout, ns_stack);
+  printer.PrintElement(element);
+}
+
+XmlPrinterImpl::XmlPrinterImpl(std::ostream* pout, XmlnsStack* ns_stack)
+    : pout_(pout),
+      ns_stack_(ns_stack) {
+}
+
+void XmlPrinterImpl::PrintElement(const XmlElement* element) {
+  ns_stack_->PushFrame();
+
+  // first go through attrs of pel to add xmlns definitions
+  const XmlAttr* attr;
+  for (attr = element->FirstAttr(); attr; attr = attr->NextAttr()) {
+    if (attr->Name() == QN_XMLNS) {
+      ns_stack_->AddXmlns(STR_EMPTY, attr->Value());
+    } else if (attr->Name().Namespace() == NS_XMLNS) {
+      ns_stack_->AddXmlns(attr->Name().LocalPart(),
+                          attr->Value());
+    }
+  }
+
+  // then go through qnames to make sure needed xmlns definitons are added
+  std::vector<std::string> new_ns;
+  std::pair<std::string, bool> prefix;
+  prefix = ns_stack_->AddNewPrefix(element->Name().Namespace(), false);
+  if (prefix.second) {
+    new_ns.push_back(prefix.first);
+    new_ns.push_back(element->Name().Namespace());
+  }
+
+  for (attr = element->FirstAttr(); attr; attr = attr->NextAttr()) {
+    prefix = ns_stack_->AddNewPrefix(attr->Name().Namespace(), true);
+    if (prefix.second) {
+      new_ns.push_back(prefix.first);
+      new_ns.push_back(attr->Name().Namespace());
+    }
+  }
+
+  // print the element name
+  *pout_ << '<' << ns_stack_->FormatQName(element->Name(), false);
+
+  // and the attributes
+  for (attr = element->FirstAttr(); attr; attr = attr->NextAttr()) {
+    *pout_ << ' ' << ns_stack_->FormatQName(attr->Name(), true) << "=\"";
+    PrintQuotedValue(attr->Value());
+    *pout_ << '"';
+  }
+
+  // and the extra xmlns declarations
+  std::vector<std::string>::iterator i(new_ns.begin());
+  while (i < new_ns.end()) {
+    if (*i == STR_EMPTY) {
+      *pout_ << " xmlns=\"" << *(i + 1) << '"';
+    } else {
+      *pout_ << " xmlns:" << *i << "=\"" << *(i + 1) << '"';
+    }
+    i += 2;
+  }
+
+  // now the children
+  const XmlChild* child = element->FirstChild();
+
+  if (child == NULL)
+    *pout_ << "/>";
+  else {
+    *pout_ << '>';
+    while (child) {
+      if (child->IsText()) {
+        if (element->IsCDATA()) {
+          PrintCDATAText(child->AsText()->Text());
+        } else {
+          PrintBodyText(child->AsText()->Text());
+        }
+      } else {
+        PrintElement(child->AsElement());
+      }
+      child = child->NextChild();
+    }
+    *pout_ << "</" << ns_stack_->FormatQName(element->Name(), false) << '>';
+  }
+
+  ns_stack_->PopFrame();
+}
+
+void XmlPrinterImpl::PrintQuotedValue(const std::string& text) {
+  size_t safe = 0;
+  for (;;) {
+    size_t unsafe = text.find_first_of("<>&\"", safe);
+    if (unsafe == std::string::npos)
+      unsafe = text.length();
+    *pout_ << text.substr(safe, unsafe - safe);
+    if (unsafe == text.length())
+      return;
+    switch (text[unsafe]) {
+      case '<': *pout_ << "&lt;"; break;
+      case '>': *pout_ << "&gt;"; break;
+      case '&': *pout_ << "&amp;"; break;
+      case '"': *pout_ << "&quot;"; break;
+    }
+    safe = unsafe + 1;
+    if (safe == text.length())
+      return;
+  }
+}
+
+void XmlPrinterImpl::PrintBodyText(const std::string& text) {
+  size_t safe = 0;
+  for (;;) {
+    size_t unsafe = text.find_first_of("<>&", safe);
+    if (unsafe == std::string::npos)
+      unsafe = text.length();
+    *pout_ << text.substr(safe, unsafe - safe);
+    if (unsafe == text.length())
+      return;
+    switch (text[unsafe]) {
+      case '<': *pout_ << "&lt;"; break;
+      case '>': *pout_ << "&gt;"; break;
+      case '&': *pout_ << "&amp;"; break;
+    }
+    safe = unsafe + 1;
+    if (safe == text.length())
+      return;
+  }
+}
+
+void XmlPrinterImpl::PrintCDATAText(const std::string& text) {
+  *pout_ << "<![CDATA[" << text << "]]>";
+}
+
+}  // namespace buzz
diff --git a/talk/xmllite/xmlprinter.h b/talk/xmllite/xmlprinter.h
new file mode 100755
index 0000000..90cc255
--- /dev/null
+++ b/talk/xmllite/xmlprinter.h
@@ -0,0 +1,49 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_XMLLITE_XMLPRINTER_H_
+#define TALK_XMLLITE_XMLPRINTER_H_
+
+#include <iosfwd>
+#include <string>
+
+namespace buzz {
+
+class XmlElement;
+class XmlnsStack;
+
+class XmlPrinter {
+ public:
+  static void PrintXml(std::ostream* pout, const XmlElement* pelt);
+
+  static void PrintXml(std::ostream* pout, const XmlElement* pelt,
+                       XmlnsStack* ns_stack);
+};
+
+}  // namespace buzz
+
+#endif  // TALK_XMLLITE_XMLPRINTER_H_
diff --git a/talk/xmllite/xmlprinter_unittest.cc b/talk/xmllite/xmlprinter_unittest.cc
new file mode 100755
index 0000000..7100025
--- /dev/null
+++ b/talk/xmllite/xmlprinter_unittest.cc
@@ -0,0 +1,62 @@
+/*
+ * libjingle
+ * Copyright 2004, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/xmllite/xmlprinter.h"
+
+#include <sstream>
+#include <string>
+
+#include "talk/xmllite/qname.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/xmlnsstack.h"
+#include "webrtc/base/common.h"
+#include "webrtc/base/gunit.h"
+
+using buzz::QName;
+using buzz::XmlElement;
+using buzz::XmlnsStack;
+using buzz::XmlPrinter;
+
+TEST(XmlPrinterTest, TestBasicPrinting) {
+  XmlElement elt(QName("google:test", "first"));
+  std::stringstream ss;
+  XmlPrinter::PrintXml(&ss, &elt);
+  EXPECT_EQ("<test:first xmlns:test=\"google:test\"/>", ss.str());
+}
+
+TEST(XmlPrinterTest, TestNamespacedPrinting) {
+  XmlElement elt(QName("google:test", "first"));
+  elt.AddElement(new XmlElement(QName("nested:test", "second")));
+  std::stringstream ss;
+
+  XmlnsStack ns_stack;
+  ns_stack.AddXmlns("gg", "google:test");
+  ns_stack.AddXmlns("", "nested:test");
+
+  XmlPrinter::PrintXml(&ss, &elt, &ns_stack);
+  EXPECT_EQ("<gg:first><second/></gg:first>", ss.str());
+}