summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/common.gypi4
-rw-r--r--build/merge_libs.gyp1
-rw-r--r--libjingle/xmllite/OWNERS22
-rw-r--r--libjingle/xmllite/qname.cc78
-rw-r--r--libjingle/xmllite/qname.h83
-rw-r--r--libjingle/xmllite/qname_unittest.cc114
-rw-r--r--libjingle/xmllite/xmlbuilder.cc130
-rw-r--r--libjingle/xmllite/xmlbuilder.h61
-rw-r--r--libjingle/xmllite/xmlbuilder_unittest.cc177
-rw-r--r--libjingle/xmllite/xmlconstants.cc25
-rw-r--r--libjingle/xmllite/xmlconstants.h30
-rw-r--r--libjingle/xmllite/xmlelement.cc496
-rw-r--r--libjingle/xmllite/xmlelement.h234
-rw-r--r--libjingle/xmllite/xmlelement_unittest.cc258
-rw-r--r--libjingle/xmllite/xmllite.gyp40
-rw-r--r--libjingle/xmllite/xmllite_tests.gyp34
-rw-r--r--libjingle/xmllite/xmlnsstack.cc178
-rw-r--r--libjingle/xmllite/xmlnsstack.h45
-rw-r--r--libjingle/xmllite/xmlnsstack_unittest.cc241
-rw-r--r--libjingle/xmllite/xmlparser.cc261
-rw-r--r--libjingle/xmllite/xmlparser.h103
-rw-r--r--libjingle/xmllite/xmlparser_unittest.cc285
-rw-r--r--libjingle/xmllite/xmlprinter.cc174
-rw-r--r--libjingle/xmllite/xmlprinter.h32
-rw-r--r--libjingle/xmllite/xmlprinter_unittest.cc45
-rw-r--r--webrtc.gyp2
26 files changed, 3153 insertions, 0 deletions
diff --git a/build/common.gypi b/build/common.gypi
index 8376ef8a..6d7d5332 100644
--- a/build/common.gypi
+++ b/build/common.gypi
@@ -49,6 +49,7 @@
'gen_core_neon_offsets_gyp%': '<(gen_core_neon_offsets_gyp)',
'webrtc_vp8_dir%': '<(webrtc_vp8_dir)',
'include_opus%': '<(include_opus)',
+ 'rtc_relative_path%': 1,
'rbe_components_path%': '<(rbe_components_path)',
'external_libraries%': '0',
'json_root%': '<(DEPTH)/third_party/jsoncpp/source/include/',
@@ -178,6 +179,9 @@
'<!@(pkg-config --cflags dbus-glib-1)',
],
}],
+ ['rtc_relative_path==1', {
+ 'defines': ['EXPAT_RELATIVE_PATH',],
+ }],
['enable_video==1', {
'defines': ['WEBRTC_MODULE_UTILITY_VIDEO',],
}],
diff --git a/build/merge_libs.gyp b/build/merge_libs.gyp
index e8c5c99f..d257c991 100644
--- a/build/merge_libs.gyp
+++ b/build/merge_libs.gyp
@@ -12,6 +12,7 @@
'merge_libs_dependencies': [
'../webrtc.gyp:webrtc',
'../sound/sound.gyp:rtc_sound',
+ '../libjingle/xmllite/xmllite.gyp:rtc_xmllite',
],
},
'targets': [
diff --git a/libjingle/xmllite/OWNERS b/libjingle/xmllite/OWNERS
new file mode 100644
index 00000000..47ccde74
--- /dev/null
+++ b/libjingle/xmllite/OWNERS
@@ -0,0 +1,22 @@
+/*
+ * Copyright The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+henrika@webrtc.org
+henrike@webrtc.org
+henrikg@webrtc.org
+hta@webrtc.org
+jiayl@webrtc.org
+juberti@webrtc.org
+mflodman@webrtc.org
+perkj@webrtc.org
+pthatcher@webrtc.org
+sergeyu@chromium.org
+tommi@webrtc.org
+
+per-file BUILD.gn=kjellander@webrtc.org
diff --git a/libjingle/xmllite/qname.cc b/libjingle/xmllite/qname.cc
new file mode 100644
index 00000000..6abde321
--- /dev/null
+++ b/libjingle/xmllite/qname.cc
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/libjingle/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/libjingle/xmllite/qname.h b/libjingle/xmllite/qname.h
new file mode 100644
index 00000000..1e772d3d
--- /dev/null
+++ b/libjingle/xmllite/qname.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_LIBJINGLE_XMLLITE_QNAME_H_
+#define WEBRTC_LIBJINGLE_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 // WEBRTC_LIBJINGLE_XMLLITE_QNAME_H_
diff --git a/libjingle/xmllite/qname_unittest.cc b/libjingle/xmllite/qname_unittest.cc
new file mode 100644
index 00000000..2a129b88
--- /dev/null
+++ b/libjingle/xmllite/qname_unittest.cc
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <string>
+#include "webrtc/libjingle/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/libjingle/xmllite/xmlbuilder.cc b/libjingle/xmllite/xmlbuilder.cc
new file mode 100644
index 00000000..9d982c33
--- /dev/null
+++ b/libjingle/xmllite/xmlbuilder.cc
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/libjingle/xmllite/xmlbuilder.h"
+
+#include <set>
+#include <vector>
+#include "webrtc/libjingle/xmllite/xmlconstants.h"
+#include "webrtc/libjingle/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/libjingle/xmllite/xmlbuilder.h b/libjingle/xmllite/xmlbuilder.h
new file mode 100644
index 00000000..354bf0b7
--- /dev/null
+++ b/libjingle/xmllite/xmlbuilder.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef _xmlbuilder_h_
+#define _xmlbuilder_h_
+
+#include <string>
+#include <vector>
+#include "webrtc/libjingle/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/libjingle/xmllite/xmlbuilder_unittest.cc b/libjingle/xmllite/xmlbuilder_unittest.cc
new file mode 100644
index 00000000..8a5ce08d
--- /dev/null
+++ b/libjingle/xmllite/xmlbuilder_unittest.cc
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <iostream>
+#include <sstream>
+#include <string>
+#include "webrtc/libjingle/xmllite/xmlbuilder.h"
+#include "webrtc/libjingle/xmllite/xmlelement.h"
+#include "webrtc/libjingle/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/libjingle/xmllite/xmlconstants.cc b/libjingle/xmllite/xmlconstants.cc
new file mode 100644
index 00000000..0b9a9ff5
--- /dev/null
+++ b/libjingle/xmllite/xmlconstants.cc
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/libjingle/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/libjingle/xmllite/xmlconstants.h b/libjingle/xmllite/xmlconstants.h
new file mode 100644
index 00000000..0064ee9d
--- /dev/null
+++ b/libjingle/xmllite/xmlconstants.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_LIBJINGLE_XMLLITE_XMLCONSTANTS_H_
+#define WEBRTC_LIBJINGLE_XMLLITE_XMLCONSTANTS_H_
+
+#include "webrtc/libjingle/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 // WEBRTC_LIBJINGLE_XMLLITE_XMLCONSTANTS_H_
diff --git a/libjingle/xmllite/xmlelement.cc b/libjingle/xmllite/xmlelement.cc
new file mode 100644
index 00000000..2d90836e
--- /dev/null
+++ b/libjingle/xmllite/xmlelement.cc
@@ -0,0 +1,496 @@
+/*
+ * Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/libjingle/xmllite/xmlelement.h"
+
+#include <ostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "webrtc/libjingle/xmllite/qname.h"
+#include "webrtc/libjingle/xmllite/xmlbuilder.h"
+#include "webrtc/libjingle/xmllite/xmlconstants.h"
+#include "webrtc/libjingle/xmllite/xmlparser.h"
+#include "webrtc/libjingle/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/libjingle/xmllite/xmlelement.h b/libjingle/xmllite/xmlelement.h
new file mode 100644
index 00000000..70c6f799
--- /dev/null
+++ b/libjingle/xmllite/xmlelement.h
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_LIBJINGLE_XMLLITE_XMLELEMENT_H_
+#define WEBRTC_LIBJINGLE_XMLLITE_XMLELEMENT_H_
+
+#include <iosfwd>
+#include <string>
+
+#include "webrtc/libjingle/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 // WEBRTC_LIBJINGLE_XMLLITE_XMLELEMENT_H_
diff --git a/libjingle/xmllite/xmlelement_unittest.cc b/libjingle/xmllite/xmlelement_unittest.cc
new file mode 100644
index 00000000..df8faedb
--- /dev/null
+++ b/libjingle/xmllite/xmlelement_unittest.cc
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <iostream>
+#include <sstream>
+#include <string>
+#include "webrtc/libjingle/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/libjingle/xmllite/xmllite.gyp b/libjingle/xmllite/xmllite.gyp
new file mode 100644
index 00000000..c4483232
--- /dev/null
+++ b/libjingle/xmllite/xmllite.gyp
@@ -0,0 +1,40 @@
+# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS. All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+
+{
+ 'includes': [ '../../build/common.gypi', ],
+ 'targets': [
+ {
+ 'target_name': 'rtc_xmllite',
+ 'type': 'static_library',
+ 'dependencies': [
+ '<(webrtc_root)/base/base.gyp:webrtc_base',
+ '<(DEPTH)/third_party/expat/expat.gyp:expat',
+ ],
+ 'export_dependent_settings': [
+ '<(DEPTH)/third_party/expat/expat.gyp:expat',
+ ],
+ 'sources': [
+ 'qname.cc',
+ 'qname.h',
+ 'xmlbuilder.cc',
+ 'xmlbuilder.h',
+ 'xmlconstants.cc',
+ 'xmlconstants.h',
+ 'xmlelement.cc',
+ 'xmlelement.h',
+ 'xmlnsstack.cc',
+ 'xmlnsstack.h',
+ 'xmlparser.cc',
+ 'xmlparser.h',
+ 'xmlprinter.cc',
+ 'xmlprinter.h',
+ ],
+ },
+ ],
+}
diff --git a/libjingle/xmllite/xmllite_tests.gyp b/libjingle/xmllite/xmllite_tests.gyp
new file mode 100644
index 00000000..a9173bc9
--- /dev/null
+++ b/libjingle/xmllite/xmllite_tests.gyp
@@ -0,0 +1,34 @@
+# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS. All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+
+{
+ 'includes': [ '../../build/common.gypi', ],
+ 'targets': [
+ {
+ 'target_name': 'rtc_xmllite_unittest',
+ 'type': 'executable',
+ 'dependencies': [
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ '<(webrtc_root)/base/base_tests.gyp:webrtc_base_tests_utils',
+ 'xmllite.gyp:rtc_xmllite',
+ ],
+ 'cflags_cc!': [
+ '-Wnon-virtual-dtor',
+ ],
+ 'sources': [
+ 'qname_unittest.cc',
+ 'xmlbuilder_unittest.cc',
+ 'xmlelement_unittest.cc',
+ 'xmlnsstack_unittest.cc',
+ 'xmlparser_unittest.cc',
+ 'xmlprinter_unittest.cc',
+ ],
+ },
+ ],
+}
+
diff --git a/libjingle/xmllite/xmlnsstack.cc b/libjingle/xmllite/xmlnsstack.cc
new file mode 100644
index 00000000..bac2fc69
--- /dev/null
+++ b/libjingle/xmllite/xmlnsstack.cc
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/libjingle/xmllite/xmlnsstack.h"
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "webrtc/libjingle/xmllite/xmlconstants.h"
+#include "webrtc/libjingle/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/libjingle/xmllite/xmlnsstack.h b/libjingle/xmllite/xmlnsstack.h
new file mode 100644
index 00000000..174f8aef
--- /dev/null
+++ b/libjingle/xmllite/xmlnsstack.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_LIBJINGLE_XMLLITE_XMLNSSTACK_H_
+#define WEBRTC_LIBJINGLE_XMLLITE_XMLNSSTACK_H_
+
+#include <string>
+#include <vector>
+#include "webrtc/libjingle/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 // WEBRTC_LIBJINGLE_XMLLITE_XMLNSSTACK_H_
diff --git a/libjingle/xmllite/xmlnsstack_unittest.cc b/libjingle/xmllite/xmlnsstack_unittest.cc
new file mode 100644
index 00000000..a57a4b07
--- /dev/null
+++ b/libjingle/xmllite/xmlnsstack_unittest.cc
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/libjingle/xmllite/xmlnsstack.h"
+
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include "webrtc/libjingle/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/libjingle/xmllite/xmlparser.cc b/libjingle/xmllite/xmlparser.cc
new file mode 100644
index 00000000..bdb8be7d
--- /dev/null
+++ b/libjingle/xmllite/xmlparser.cc
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/libjingle/xmllite/xmlparser.h"
+
+#include <string>
+#include <vector>
+
+#include "webrtc/libjingle/xmllite/xmlconstants.h"
+#include "webrtc/libjingle/xmllite/xmlelement.h"
+#include "webrtc/libjingle/xmllite/xmlnsstack.h"
+#include "webrtc/libjingle/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/libjingle/xmllite/xmlparser.h b/libjingle/xmllite/xmlparser.h
new file mode 100644
index 00000000..131c585e
--- /dev/null
+++ b/libjingle/xmllite/xmlparser.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_LIBJINGLE_XMLLITE_XMLPARSER_H_
+#define WEBRTC_LIBJINGLE_XMLLITE_XMLPARSER_H_
+
+#include <string>
+
+#include "webrtc/libjingle/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 // WEBRTC_LIBJINGLE_XMLLITE_XMLPARSER_H_
diff --git a/libjingle/xmllite/xmlparser_unittest.cc b/libjingle/xmllite/xmlparser_unittest.cc
new file mode 100644
index 00000000..a73d81e6
--- /dev/null
+++ b/libjingle/xmllite/xmlparser_unittest.cc
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <iostream>
+#include <sstream>
+#include <string>
+#include "webrtc/libjingle/xmllite/qname.h"
+#include "webrtc/libjingle/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/libjingle/xmllite/xmlprinter.cc b/libjingle/xmllite/xmlprinter.cc
new file mode 100644
index 00000000..27d7cc0b
--- /dev/null
+++ b/libjingle/xmllite/xmlprinter.cc
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/libjingle/xmllite/xmlprinter.h"
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "webrtc/libjingle/xmllite/xmlconstants.h"
+#include "webrtc/libjingle/xmllite/xmlelement.h"
+#include "webrtc/libjingle/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/libjingle/xmllite/xmlprinter.h b/libjingle/xmllite/xmlprinter.h
new file mode 100644
index 00000000..40cf195f
--- /dev/null
+++ b/libjingle/xmllite/xmlprinter.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_LIBJINGLE_XMLLITE_XMLPRINTER_H_
+#define WEBRTC_LIBJINGLE_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 // WEBRTC_LIBJINGLE_XMLLITE_XMLPRINTER_H_
diff --git a/libjingle/xmllite/xmlprinter_unittest.cc b/libjingle/xmllite/xmlprinter_unittest.cc
new file mode 100644
index 00000000..b4850b5a
--- /dev/null
+++ b/libjingle/xmllite/xmlprinter_unittest.cc
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/libjingle/xmllite/xmlprinter.h"
+
+#include <sstream>
+#include <string>
+
+#include "webrtc/libjingle/xmllite/qname.h"
+#include "webrtc/libjingle/xmllite/xmlelement.h"
+#include "webrtc/libjingle/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());
+}
diff --git a/webrtc.gyp b/webrtc.gyp
index e5e9cc58..12d38dba 100644
--- a/webrtc.gyp
+++ b/webrtc.gyp
@@ -24,6 +24,7 @@
'common.gyp:*',
'common_audio/common_audio.gyp:*',
'common_video/common_video.gyp:*',
+ 'libjingle/xmllite/xmllite.gyp:*',
'modules/modules.gyp:*',
'system_wrappers/source/system_wrappers.gyp:*',
'video_engine/video_engine.gyp:*',
@@ -44,6 +45,7 @@
'dependencies': [
'base/base_tests.gyp:*',
'common_video/common_video_unittests.gyp:*',
+ 'libjingle/xmllite/xmllite_tests.gyp:*',
'sound/sound_tests.gyp:*',
'system_wrappers/source/system_wrappers_tests.gyp:*',
'test/metrics.gyp:*',