summaryrefslogtreecommitdiffstats
path: root/dom/base/test/gtest
diff options
context:
space:
mode:
Diffstat (limited to 'dom/base/test/gtest')
-rw-r--r--dom/base/test/gtest/TestContentUtils.cpp221
-rw-r--r--dom/base/test/gtest/TestMimeType.cpp1078
-rw-r--r--dom/base/test/gtest/TestParser.cpp52
-rw-r--r--dom/base/test/gtest/TestPlainTextSerializer.cpp340
-rw-r--r--dom/base/test/gtest/TestScheduler.cpp348
-rw-r--r--dom/base/test/gtest/TestXMLSerializerNoBreakLink.cpp69
-rw-r--r--dom/base/test/gtest/TestXPathGenerator.cpp150
-rw-r--r--dom/base/test/gtest/moz.build21
8 files changed, 2279 insertions, 0 deletions
diff --git a/dom/base/test/gtest/TestContentUtils.cpp b/dom/base/test/gtest/TestContentUtils.cpp
new file mode 100644
index 0000000000..2a2d2b4600
--- /dev/null
+++ b/dom/base/test/gtest/TestContentUtils.cpp
@@ -0,0 +1,221 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "jsapi.h"
+#include "js/PropertyAndElement.h" // JS_DefineProperty
+#include "nsContentUtils.h"
+#include "nsNetUtil.h"
+#include "mozilla/CycleCollectedJSContext.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/SimpleGlobalObject.h"
+
+using namespace mozilla::dom;
+
+struct IsURIInListMatch {
+ nsLiteralCString pattern;
+ bool firstMatch, secondMatch;
+};
+
+std::ostream& operator<<(std::ostream& aStream,
+ const nsContentUtils::ParsedRange& aParsedRange) {
+ if (aParsedRange.Start()) {
+ aStream << *aParsedRange.Start();
+ }
+
+ aStream << "-";
+
+ if (aParsedRange.End()) {
+ aStream << *aParsedRange.End();
+ }
+
+ return aStream;
+}
+
+TEST(DOM_Base_ContentUtils, IsURIInList)
+{
+ nsCOMPtr<nsIURI> uri, subURI;
+ nsresult rv = NS_NewURI(getter_AddRefs(uri),
+ "https://example.com/path/favicon.ico#"_ns);
+ ASSERT_TRUE(rv == NS_OK);
+
+ rv = NS_NewURI(getter_AddRefs(subURI),
+ "http://sub.example.com/favicon.ico?"_ns);
+ ASSERT_TRUE(rv == NS_OK);
+
+ static constexpr IsURIInListMatch patterns[] = {
+ {"bar.com,*.example.com,example.com,foo.com"_ns, true, true},
+ {"bar.com,example.com,*.example.com,foo.com"_ns, true, true},
+ {"*.example.com,example.com,foo.com"_ns, true, true},
+ {"example.com,*.example.com,foo.com"_ns, true, true},
+ {"*.example.com,example.com"_ns, true, true},
+ {"example.com,*.example.com"_ns, true, true},
+ {"*.example.com/,example.com/"_ns, true, true},
+ {"example.com/,*.example.com/"_ns, true, true},
+ {"*.example.com/pa,example.com/pa"_ns, false, false},
+ {"example.com/pa,*.example.com/pa"_ns, false, false},
+ {"*.example.com/pa/,example.com/pa/"_ns, false, false},
+ {"example.com/pa/,*.example.com/pa/"_ns, false, false},
+ {"*.example.com/path,example.com/path"_ns, false, false},
+ {"example.com/path,*.example.com/path"_ns, false, false},
+ {"*.example.com/path/,example.com/path/"_ns, true, false},
+ {"example.com/path/,*.example.com/path/"_ns, true, false},
+ {"*.example.com/favicon.ico"_ns, false, true},
+ {"example.com/path/favicon.ico"_ns, true, false},
+ {"*.example.com"_ns, false, true},
+ {"example.com"_ns, true, false},
+ {"foo.com"_ns, false, false},
+ {"*.foo.com"_ns, false, false},
+ };
+
+ for (auto& entry : patterns) {
+ bool result = nsContentUtils::IsURIInList(uri, entry.pattern);
+ ASSERT_EQ(result, entry.firstMatch) << "Matching " << entry.pattern;
+
+ result = nsContentUtils::IsURIInList(subURI, entry.pattern);
+ ASSERT_EQ(result, entry.secondMatch) << "Matching " << entry.pattern;
+ }
+}
+
+TEST(DOM_Base_ContentUtils,
+ StringifyJSON_EmptyValue_UndefinedIsNullStringLiteral)
+{
+ JS::Rooted<JSObject*> globalObject(
+ mozilla::dom::RootingCx(),
+ mozilla::dom::SimpleGlobalObject::Create(
+ mozilla::dom::SimpleGlobalObject::GlobalType::BindingDetail));
+ mozilla::dom::AutoJSAPI jsAPI;
+ ASSERT_TRUE(jsAPI.Init(globalObject));
+ JSContext* cx = jsAPI.cx();
+ nsAutoString serializedValue;
+
+ ASSERT_TRUE(nsContentUtils::StringifyJSON(cx, JS::UndefinedHandleValue,
+ serializedValue,
+ UndefinedIsNullStringLiteral));
+ ASSERT_TRUE(serializedValue.EqualsLiteral("null"));
+}
+
+TEST(DOM_Base_ContentUtils, StringifyJSON_Object_UndefinedIsNullStringLiteral)
+{
+ JS::Rooted<JSObject*> globalObject(
+ mozilla::dom::RootingCx(),
+ mozilla::dom::SimpleGlobalObject::Create(
+ mozilla::dom::SimpleGlobalObject::GlobalType::BindingDetail));
+ mozilla::dom::AutoJSAPI jsAPI;
+ ASSERT_TRUE(jsAPI.Init(globalObject));
+ JSContext* cx = jsAPI.cx();
+ nsAutoString serializedValue;
+
+ JS::Rooted<JSObject*> jsObj(cx, JS_NewPlainObject(cx));
+ JS::Rooted<JSString*> valueStr(cx, JS_NewStringCopyZ(cx, "Hello World!"));
+ ASSERT_TRUE(JS_DefineProperty(cx, jsObj, "key1", valueStr, JSPROP_ENUMERATE));
+ JS::Rooted<JS::Value> jsValue(cx, JS::ObjectValue(*jsObj));
+
+ ASSERT_TRUE(nsContentUtils::StringifyJSON(cx, jsValue, serializedValue,
+ UndefinedIsNullStringLiteral));
+
+ ASSERT_TRUE(serializedValue.EqualsLiteral("{\"key1\":\"Hello World!\"}"));
+}
+
+TEST(DOM_Base_ContentUtils, StringifyJSON_EmptyValue_UndefinedIsVoidString)
+{
+ JS::Rooted<JSObject*> globalObject(
+ mozilla::dom::RootingCx(),
+ mozilla::dom::SimpleGlobalObject::Create(
+ mozilla::dom::SimpleGlobalObject::GlobalType::BindingDetail));
+ mozilla::dom::AutoJSAPI jsAPI;
+ ASSERT_TRUE(jsAPI.Init(globalObject));
+ JSContext* cx = jsAPI.cx();
+ nsAutoString serializedValue;
+
+ ASSERT_TRUE(nsContentUtils::StringifyJSON(
+ cx, JS::UndefinedHandleValue, serializedValue, UndefinedIsVoidString));
+
+ ASSERT_TRUE(serializedValue.IsVoid());
+}
+
+TEST(DOM_Base_ContentUtils, StringifyJSON_Object_UndefinedIsVoidString)
+{
+ JS::Rooted<JSObject*> globalObject(
+ mozilla::dom::RootingCx(),
+ mozilla::dom::SimpleGlobalObject::Create(
+ mozilla::dom::SimpleGlobalObject::GlobalType::BindingDetail));
+ mozilla::dom::AutoJSAPI jsAPI;
+ ASSERT_TRUE(jsAPI.Init(globalObject));
+ JSContext* cx = jsAPI.cx();
+ nsAutoString serializedValue;
+
+ JS::Rooted<JSObject*> jsObj(cx, JS_NewPlainObject(cx));
+ JS::Rooted<JSString*> valueStr(cx, JS_NewStringCopyZ(cx, "Hello World!"));
+ ASSERT_TRUE(JS_DefineProperty(cx, jsObj, "key1", valueStr, JSPROP_ENUMERATE));
+ JS::Rooted<JS::Value> jsValue(cx, JS::ObjectValue(*jsObj));
+
+ ASSERT_TRUE(nsContentUtils::StringifyJSON(cx, jsValue, serializedValue,
+ UndefinedIsVoidString));
+
+ ASSERT_TRUE(serializedValue.EqualsLiteral("{\"key1\":\"Hello World!\"}"));
+}
+
+TEST(DOM_Base_ContentUtils, ParseSingleRangeHeader)
+{
+ // Parsing a simple range should succeed
+ EXPECT_EQ(nsContentUtils::ParseSingleRangeRequest("bytes=0-42"_ns, false),
+ mozilla::Some(nsContentUtils::ParsedRange(mozilla::Some(0),
+ mozilla::Some(42))));
+
+ // Range containing a invalid rangeStart should fail
+ EXPECT_EQ(nsContentUtils::ParseSingleRangeRequest("bytes= t-200"_ns, true),
+ mozilla::Nothing());
+
+ // Range containing whitespace, with allowWhitespace=false should fail.
+ EXPECT_EQ(nsContentUtils::ParseSingleRangeRequest("bytes= 2-200"_ns, false),
+ mozilla::Nothing());
+
+ // Range containing whitespace, with allowWhitespace=true should succeed
+ EXPECT_EQ(
+ nsContentUtils::ParseSingleRangeRequest("bytes \t= 2 - 200"_ns, true),
+ mozilla::Some(
+ nsContentUtils::ParsedRange(mozilla::Some(2), mozilla::Some(200))));
+
+ // Range containing invalid whitespace should fail
+ EXPECT_EQ(
+ nsContentUtils::ParseSingleRangeRequest("bytes \r= 2 - 200"_ns, true),
+ mozilla::Nothing());
+
+ // Range without a rangeStart should succeed
+ EXPECT_EQ(nsContentUtils::ParseSingleRangeRequest("bytes\t=\t-200"_ns, true),
+ mozilla::Some(nsContentUtils::ParsedRange(mozilla::Nothing(),
+ mozilla::Some(200))));
+
+ // Range without a rangeEnd should succeed
+ EXPECT_EQ(nsContentUtils::ParseSingleRangeRequest("bytes=55-"_ns, true),
+ mozilla::Some(nsContentUtils::ParsedRange(mozilla::Some(55),
+ mozilla::Nothing())));
+
+ // Range without a rangeStart or rangeEnd should fail
+ EXPECT_EQ(nsContentUtils::ParseSingleRangeRequest("bytes\t=\t-"_ns, true),
+ mozilla::Nothing());
+
+ // Range with extra characters should fail
+ EXPECT_EQ(nsContentUtils::ParseSingleRangeRequest("bytes=0-42 "_ns, true),
+ mozilla::Nothing());
+
+ // Range with rangeStart > rangeEnd should fail
+ EXPECT_EQ(nsContentUtils::ParseSingleRangeRequest("bytes=42-0 "_ns, true),
+ mozilla::Nothing());
+}
+
+TEST(DOM_Base_ContentUtils, IsAllowedNonCorsRange)
+{
+ EXPECT_EQ(nsContentUtils::IsAllowedNonCorsRange("bytes=-200"_ns), false);
+ EXPECT_EQ(nsContentUtils::IsAllowedNonCorsRange("bytes= 200-"_ns), false);
+ EXPECT_EQ(nsContentUtils::IsAllowedNonCorsRange("bytes=201-200"_ns), false);
+ EXPECT_EQ(nsContentUtils::IsAllowedNonCorsRange("bytes=200-201 "_ns), false);
+ EXPECT_EQ(nsContentUtils::IsAllowedNonCorsRange("bytes=200-"_ns), true);
+ EXPECT_EQ(nsContentUtils::IsAllowedNonCorsRange("bytes=200-201"_ns), true);
+ EXPECT_EQ(nsContentUtils::IsAllowedNonCorsRange("bytes=-200 "_ns), false);
+}
diff --git a/dom/base/test/gtest/TestMimeType.cpp b/dom/base/test/gtest/TestMimeType.cpp
new file mode 100644
index 0000000000..fecb3f8678
--- /dev/null
+++ b/dom/base/test/gtest/TestMimeType.cpp
@@ -0,0 +1,1078 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "MimeType.h"
+#include "nsString.h"
+
+using mozilla::UniquePtr;
+
+TEST(MimeType, EmptyString)
+{
+ const auto in = u""_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_FALSE(parsed)
+ << "Empty string";
+}
+
+TEST(MimeType, JustWhitespace)
+{
+ const auto in = u" \t\r\n "_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_FALSE(parsed)
+ << "Just whitespace";
+}
+
+TEST(MimeType, JustBackslash)
+{
+ const auto in = u"\\"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_FALSE(parsed)
+ << "Just backslash";
+}
+
+TEST(MimeType, JustForwardslash)
+{
+ const auto in = u"/"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_FALSE(parsed)
+ << "Just forward slash";
+}
+
+TEST(MimeType, MissingType1)
+{
+ const auto in = u"/bogus"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_FALSE(parsed)
+ << "Missing type #1";
+}
+
+TEST(MimeType, MissingType2)
+{
+ const auto in = u" \r\n\t/bogus"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_FALSE(parsed)
+ << "Missing type #2";
+}
+
+TEST(MimeType, MissingSubtype1)
+{
+ const auto in = u"bogus"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_FALSE(parsed)
+ << "Missing subtype #1";
+}
+
+TEST(MimeType, MissingSubType2)
+{
+ const auto in = u"bogus/"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_FALSE(parsed)
+ << "Missing subtype #2";
+}
+
+TEST(MimeType, MissingSubType3)
+{
+ const auto in = u"bogus;"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_FALSE(parsed)
+ << "Missing subtype #3";
+}
+
+TEST(MimeType, MissingSubType4)
+{
+ const auto in = u"bogus; \r\n\t"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_FALSE(parsed)
+ << "Missing subtype #3";
+}
+
+TEST(MimeType, ExtraForwardSlash)
+{
+ const auto in = u"bogus/bogus/;"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_FALSE(parsed)
+ << "Extra forward slash";
+}
+
+TEST(MimeType, WhitespaceInType)
+{
+ const auto in = u"t\re\nx\tt /html"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_FALSE(parsed)
+ << "Type with whitespace";
+}
+
+TEST(MimeType, WhitespaceInSubtype)
+{
+ const auto in = u"text/ h\rt\nm\tl"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_FALSE(parsed)
+ << "Subtype with whitespace";
+}
+
+TEST(MimeType, NonAlphanumericMediaType1)
+{
+ const auto in = u"</>"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_FALSE(parsed)
+ << "Non-alphanumeric media type #1";
+}
+
+TEST(MimeType, NonAlphanumericMediaType2)
+{
+ const auto in = u"(/)"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_FALSE(parsed)
+ << "Non-alphanumeric media type #2";
+}
+
+TEST(MimeType, NonAlphanumericMediaType3)
+{
+ const auto in = u"{/}"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_FALSE(parsed)
+ << "Non-alphanumeric media type #3";
+}
+
+TEST(MimeType, NonAlphanumericMediaType4)
+{
+ const auto in = u"\"/\""_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_FALSE(parsed)
+ << "Non-alphanumeric media type #4";
+}
+
+TEST(MimeType, NonAlphanumericMediaType5)
+{
+ const auto in = u"\0/\0"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_FALSE(parsed)
+ << "Non-alphanumeric media type #5";
+}
+
+TEST(MimeType, NonAlphanumericMediaType6)
+{
+ const auto in = u"text/html(;doesnot=matter"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_FALSE(parsed)
+ << "Non-alphanumeric media type #6";
+}
+
+TEST(MimeType, NonLatin1MediaType1)
+{
+ const auto in = u"ÿ/ÿ"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_FALSE(parsed)
+ << "Non-latin1 media type #1";
+}
+
+TEST(MimeType, NonLatin1MediaType2)
+{
+ const auto in = u"\x0100/\x0100"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_FALSE(parsed)
+ << "Non-latin1 media type #2";
+}
+
+TEST(MimeType, MultipleParameters)
+{
+ const auto in = u"text/html;charset=gbk;no=1;charset_=gbk_;yes=2"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.Equals(u"text/html;charset=gbk;no=1;charset_=gbk_;yes=2"_ns))
+ << "Multiple parameters";
+}
+
+TEST(MimeType, DuplicateParameter1)
+{
+ const auto in = u"text/html;charset=gbk;charset=windows-1255"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.Equals(u"text/html;charset=gbk"_ns))
+ << "Duplicate parameter #1";
+}
+
+TEST(MimeType, DuplicateParameter2)
+{
+ const auto in = u"text/html;charset=();charset=GBK"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.Equals(u"text/html;charset=\"()\""_ns))
+ << "Duplicate parameter #2";
+}
+
+TEST(MimeType, CString)
+{
+ const auto in = "text/html;charset=();charset=GBK"_ns;
+ UniquePtr<CMimeType> parsed = CMimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsCString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.Equals("text/html;charset=\"()\""_ns))
+ << "Duplicate parameter #2";
+}
+
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable : 4819)
+#endif
+TEST(MimeType, NonAlphanumericParametersAreQuoted)
+{
+ const auto in = u"text/html;test=\x00FF\\;charset=gbk"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.Equals(u"text/html;test=\"\x00FF\\\\\";charset=gbk"_ns))
+ << "Non-alphanumeric parameters are quoted";
+}
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+TEST(MimeType, ParameterQuotedIfHasLeadingWhitespace1)
+{
+ const auto in = u"text/html;charset= g\\\"bk"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\" g\\\\\\\"bk\""))
+ << "Parameter is quoted if has leading whitespace #1";
+}
+
+TEST(MimeType, ParameterQuotedIfHasLeadingWhitespace2)
+{
+ const auto in = u"text/html;charset= \"g\\bk\""_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\" \\\"g\\\\bk\\\"\""))
+ << "Parameter is quoted if has leading whitespace #2";
+}
+
+TEST(MimeType, ParameterQuotedIfHasInternalWhitespace)
+{
+ const auto in = u"text/html;charset=g \\b\"k"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\"g \\\\b\\\"k\""))
+ << "Parameter is quoted if has internal whitespace";
+}
+
+TEST(MimeType, ImproperlyQuotedParameter1)
+{
+ const auto in = u"x/x;test=\""_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("x/x;test=\"\""))
+ << "Improperly-quoted parameter is handled properly #1";
+}
+
+TEST(MimeType, ImproperlyQuotedParameter2)
+{
+ const auto in = u"x/x;test=\"\\"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("x/x;test=\"\\\\\""))
+ << "Improperly-quoted parameter is handled properly #2";
+}
+
+TEST(MimeType, NonLatin1ParameterIgnored)
+{
+ const auto in = u"x/x;test=\xFFFD;x=x"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("x/x;x=x"))
+ << "Non latin-1 parameters are ignored";
+}
+
+TEST(MimeType, ParameterIgnoredIfWhitespaceInName1)
+{
+ const auto in = u"text/html;charset =gbk;charset=123"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=123"))
+ << "Parameter ignored if whitespace in name #1";
+}
+
+TEST(MimeType, ParameterIgnoredIfWhitespaceInName2)
+{
+ const auto in = u"text/html;cha rset =gbk;charset=123"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=123"))
+ << "Parameter ignored if whitespace in name #2";
+}
+
+TEST(MimeType, WhitespaceTrimmed)
+{
+ const auto in = u"\n\r\t text/plain\n\r\t ;\n\r\t charset=123\n\r\t "_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/plain;charset=123"))
+ << "Whitespace appropriately ignored";
+}
+
+TEST(MimeType, WhitespaceOnlyParameterIgnored)
+{
+ const auto in = u"x/x;x= \r\n\t"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("x/x"))
+ << "Whitespace-only parameter is ignored";
+}
+
+TEST(MimeType, IncompleteParameterIgnored1)
+{
+ const auto in = u"x/x;test"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("x/x"))
+ << "Incomplete parameter is ignored #1";
+}
+
+TEST(MimeType, IncompleteParameterIgnored2)
+{
+ const auto in = u"x/x;test="_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("x/x"))
+ << "Incomplete parameter is ignored #2";
+}
+
+TEST(MimeType, IncompleteParameterIgnored3)
+{
+ const auto in = u"x/x;test= \r\n\t"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("x/x"))
+ << "Incomplete parameter is ignored #3";
+}
+
+TEST(MimeType, IncompleteParameterIgnored4)
+{
+ const auto in = u"text/html;test;charset=gbk"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk"))
+ << "Incomplete parameter is ignored #4";
+}
+
+TEST(MimeType, IncompleteParameterIgnored5)
+{
+ const auto in = u"text/html;test=;charset=gbk"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk"))
+ << "Incomplete parameter is ignored #5";
+}
+
+TEST(MimeType, EmptyParameterIgnored1)
+{
+ const auto in = u"text/html ; ; charset=gbk"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk"))
+ << "Empty parameter ignored #1";
+}
+
+TEST(MimeType, EmptyParameterIgnored2)
+{
+ const auto in = u"text/html;;;;charset=gbk"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk"))
+ << "Empty parameter ignored #2";
+}
+
+TEST(MimeType, InvalidParameterIgnored1)
+{
+ const auto in = u"text/html;';charset=gbk"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk"))
+ << "Invalid parameter ignored #1";
+}
+
+TEST(MimeType, InvalidParameterIgnored2)
+{
+ const auto in = u"text/html;\";charset=gbk;=123; =321"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk"))
+ << "Invalid parameter ignored #2";
+}
+
+TEST(MimeType, InvalidParameterIgnored3)
+{
+ const auto in = u"text/html;charset= \"\u007F;charset=GBK"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=GBK"))
+ << "Invalid parameter ignored #3";
+}
+
+TEST(MimeType, InvalidParameterIgnored4)
+{
+ const auto in = nsLiteralString(
+ u"text/html;charset=\"\u007F;charset=foo\";charset=GBK;charset=");
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=GBK"))
+ << "Invalid parameter ignored #4";
+}
+
+TEST(MimeType, SingleQuotes1)
+{
+ const auto in = u"text/html;charset='gbk'"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset='gbk'"))
+ << "Single quotes handled properly #1";
+}
+
+TEST(MimeType, SingleQuotes2)
+{
+ const auto in = u"text/html;charset='gbk"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset='gbk"))
+ << "Single quotes handled properly #2";
+}
+
+TEST(MimeType, SingleQuotes3)
+{
+ const auto in = u"text/html;charset=gbk'"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk'"))
+ << "Single quotes handled properly #3";
+}
+
+TEST(MimeType, SingleQuotes4)
+{
+ const auto in = u"text/html;charset=';charset=GBK"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset='"))
+ << "Single quotes handled properly #4";
+}
+
+TEST(MimeType, SingleQuotes5)
+{
+ const auto in = u"text/html;charset=''';charset=GBK"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset='''"))
+ << "Single quotes handled properly #5";
+}
+
+TEST(MimeType, DoubleQuotes1)
+{
+ const auto in = u"text/html;charset=\"gbk\""_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk"))
+ << "Double quotes handled properly #1";
+}
+
+TEST(MimeType, DoubleQuotes2)
+{
+ const auto in = u"text/html;charset=\"gbk"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk"))
+ << "Double quotes handled properly #2";
+}
+
+TEST(MimeType, DoubleQuotes3)
+{
+ const auto in = u"text/html;charset=gbk\""_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\"gbk\\\"\""))
+ << "Double quotes handled properly #3";
+}
+
+TEST(MimeType, DoubleQuotes4)
+{
+ const auto in = u"text/html;charset=\" gbk\""_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\" gbk\""))
+ << "Double quotes handled properly #4";
+}
+
+TEST(MimeType, DoubleQuotes5)
+{
+ const auto in = u"text/html;charset=\"gbk \""_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\"gbk \""))
+ << "Double quotes handled properly #5";
+}
+
+TEST(MimeType, DoubleQuotes6)
+{
+ const auto in = u"text/html;charset=\"\\ gbk\""_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\" gbk\""))
+ << "Double quotes handled properly #6";
+}
+
+TEST(MimeType, DoubleQuotes7)
+{
+ const auto in = u"text/html;charset=\"\\g\\b\\k\""_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk"))
+ << "Double quotes handled properly #7";
+}
+
+TEST(MimeType, DoubleQuotes8)
+{
+ const auto in = u"text/html;charset=\"gbk\"x"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk"))
+ << "Double quotes handled properly #8";
+}
+
+TEST(MimeType, DoubleQuotes9)
+{
+ const auto in = u"text/html;charset=\"\";charset=GBK"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\"\""))
+ << "Double quotes handled properly #9";
+}
+
+TEST(MimeType, DoubleQuotes10)
+{
+ const auto in = u"text/html;charset=\";charset=GBK"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\";charset=GBK\""))
+ << "Double quotes handled properly #10";
+}
+
+TEST(MimeType, UnexpectedCodePoints)
+{
+ const auto in = u"text/html;charset={gbk}"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\"{gbk}\""))
+ << "Unexpected code points handled properly";
+}
+
+TEST(MimeType, LongTypesSubtypesAccepted)
+{
+ const auto in = nsLiteralString(
+ u"01234567890123456789012345678901234567890123456789012345678901234567890"
+ u"1"
+ "2345678901234567890123456789012345678901234567890123456789/"
+ "012345678901234567890123456789012345678901234567890123456789012345678901"
+ "2345678901234567890123456789012345678901234567890123456789");
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.Equals(in))
+ << "Long type/subtype accepted";
+}
+
+TEST(MimeType, LongParametersAccepted)
+{
+ const auto in = nsLiteralString(
+ u"text/"
+ "html;"
+ "012345678901234567890123456789012345678901234567890123456789012345678901"
+ "2345678901234567890123456789012345678901234567890123456789=x;charset="
+ "gbk");
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.Equals(in))
+ << "Long parameters accepted";
+}
+
+TEST(MimeType, AllValidCharactersAccepted1)
+{
+ const auto in = nsLiteralString(
+ u"x/x;x=\"\t "
+ u"!\\\"#$%&'()*+,-./"
+ u"0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`"
+ u"abcdefghijklmnopqrstuvwxyz{|}~"
+ u"\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008A"
+ u"\u008B\u008C\u008D\u008E\u008F\u0090\u0091\u0092\u0093\u0094\u0095"
+ u"\u0096\u0097\u0098\u0099\u009A\u009B\u009C\u009D\u009E\u009F\u00A0"
+ u"\u00A1\u00A2\u00A3\u00A4\u00A5\u00A6\u00A7\u00A8\u00A9\u00AA\u00AB"
+ u"\u00AC\u00AD\u00AE\u00AF\u00B0\u00B1\u00B2\u00B3\u00B4\u00B5\u00B6"
+ u"\u00B7\u00B8\u00B9\u00BA\u00BB\u00BC\u00BD\u00BE\u00BF\u00C0\u00C1"
+ u"\u00C2\u00C3\u00C4\u00C5\u00C6\u00C7\u00C8\u00C9\u00CA\u00CB\u00CC"
+ u"\u00CD\u00CE\u00CF\u00D0\u00D1\u00D2\u00D3\u00D4\u00D5\u00D6\u00D7"
+ u"\u00D8\u00D9\u00DA\u00DB\u00DC\u00DD\u00DE\u00DF\u00E0\u00E1\u00E2"
+ u"\u00E3\u00E4\u00E5\u00E6\u00E7\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED"
+ u"\u00EE\u00EF\u00F0\u00F1\u00F2\u00F3\u00F4\u00F5\u00F6\u00F7\u00F8"
+ u"\u00F9\u00FA\u00FB\u00FC\u00FD\u00FE\u00FF\"");
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.Equals(in))
+ << "All valid characters accepted #1";
+}
+
+TEST(MimeType, CaseNormalization1)
+{
+ const auto in = u"TEXT/PLAIN;CHARSET=TEST"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/plain;charset=TEST"))
+ << "Case normalized properly #1";
+}
+
+TEST(MimeType, CaseNormalization2)
+{
+ const auto in = nsLiteralString(
+ u"!#$%&'*+-.^_`|~"
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/"
+ "!#$%&'*+-.^_`|~"
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz;!#$%&'*+-"
+ ".^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=!#$"
+ "%&'*+-.^_`|~"
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral(
+ "!#$%&'*+-.^_`|~"
+ "0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz/"
+ "!#$%&'*+-.^_`|~"
+ "0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz;!#$%&'*+-"
+ ".^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz=!#$"
+ "%&'*+-.^_`|~"
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"))
+ << "Case normalized properly #2";
+}
+
+TEST(MimeType, LegacyCommentSyntax1)
+{
+ const auto in = u"text/html;charset=gbk("_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\"gbk(\""))
+ << "Legacy comment syntax #1";
+}
+
+TEST(MimeType, LegacyCommentSyntax2)
+{
+ const auto in = u"text/html;x=(;charset=gbk"_ns;
+ UniquePtr<MimeType> parsed = MimeType::Parse(in);
+ ASSERT_TRUE(parsed)
+ << "Parsing succeeded";
+ nsAutoString out;
+ parsed->Serialize(out);
+ ASSERT_TRUE(out.EqualsLiteral("text/html;x=\"(\";charset=gbk"))
+ << "Legacy comment syntax #2";
+}
+
+TEST(MimeTypeParsing, contentTypes1)
+{
+ const nsAutoCString val(",text/plain");
+ nsCString contentType;
+ nsCString contentCharset;
+
+ bool parsed = CMimeType::Parse(val, contentType, contentCharset);
+
+ ASSERT_FALSE(parsed);
+ ASSERT_TRUE(contentType.EqualsLiteral(""));
+ ASSERT_TRUE(contentCharset.EqualsLiteral(""));
+}
+
+TEST(MimeTypeParsing, contentTypes2)
+{
+ const nsAutoCString val("text/plain,");
+ nsCString contentType;
+ nsCString contentCharset;
+
+ bool parsed = CMimeType::Parse(val, contentType, contentCharset);
+
+ ASSERT_TRUE(parsed);
+ ASSERT_TRUE(contentType.EqualsLiteral("text/plain"));
+ ASSERT_TRUE(contentCharset.EqualsLiteral(""));
+}
+
+TEST(MimeTypeParsing, contentTypes3)
+{
+ const nsAutoCString val("text/html,text/plain");
+ nsCString contentType;
+ nsCString contentCharset;
+
+ bool parsed = CMimeType::Parse(val, contentType, contentCharset);
+
+ ASSERT_TRUE(parsed);
+ ASSERT_TRUE(contentType.EqualsLiteral("text/plain"));
+ ASSERT_TRUE(contentCharset.EqualsLiteral(""));
+}
+
+TEST(MimeTypeParsing, contentTypes4)
+{
+ const nsAutoCString val("text/plain;charset=gbk,text/html");
+ nsCString contentType;
+ nsCString contentCharset;
+
+ bool parsed = CMimeType::Parse(val, contentType, contentCharset);
+
+ ASSERT_TRUE(parsed);
+ ASSERT_TRUE(contentType.EqualsLiteral("text/html"));
+ ASSERT_TRUE(contentCharset.EqualsLiteral(""));
+}
+
+TEST(MimeTypeParsing, contentTypes5)
+{
+ const nsAutoCString val(
+ "text/plain;charset=gbk,text/html;charset=windows-1254");
+ nsCString contentType;
+ nsCString contentCharset;
+
+ bool parsed = CMimeType::Parse(val, contentType, contentCharset);
+
+ ASSERT_TRUE(parsed);
+ ASSERT_TRUE(contentType.EqualsLiteral("text/html"));
+ ASSERT_TRUE(contentCharset.EqualsLiteral("windows-1254"));
+}
+
+TEST(MimeTypeParsing, contentTypes6)
+{
+ const nsAutoCString val("text/plain;charset=gbk,text/plain");
+ nsCString contentType;
+ nsCString contentCharset;
+
+ bool parsed = CMimeType::Parse(val, contentType, contentCharset);
+
+ ASSERT_TRUE(parsed);
+ ASSERT_TRUE(contentType.EqualsLiteral("text/plain"));
+ ASSERT_TRUE(contentCharset.EqualsLiteral("gbk"));
+}
+
+TEST(MimeTypeParsing, contentTypes7)
+{
+ const nsAutoCString val(
+ "text/plain;charset=gbk,text/plain;charset=windows-1252");
+ nsCString contentType;
+ nsCString contentCharset;
+
+ bool parsed = CMimeType::Parse(val, contentType, contentCharset);
+
+ ASSERT_TRUE(parsed);
+ ASSERT_TRUE(contentType.EqualsLiteral("text/plain"));
+ ASSERT_TRUE(contentCharset.EqualsLiteral("windows-1252"));
+}
+
+TEST(MimeTypeParsing, contentTypes8)
+{
+ const nsAutoCString val("text/html;charset=gbk,text/html;x=\",text/plain");
+ nsCString contentType;
+ nsCString contentCharset;
+
+ bool parsed = CMimeType::Parse(val, contentType, contentCharset);
+
+ ASSERT_TRUE(parsed);
+ ASSERT_TRUE(contentType.EqualsLiteral("text/html"));
+ ASSERT_TRUE(contentCharset.EqualsLiteral("gbk"));
+}
+
+TEST(MimeTypeParsing, contentTypes9)
+{
+ const nsAutoCString val("text/plain;charset=gbk;x=foo,text/plain");
+ nsCString contentType;
+ nsCString contentCharset;
+
+ bool parsed = CMimeType::Parse(val, contentType, contentCharset);
+
+ ASSERT_TRUE(parsed);
+ ASSERT_TRUE(contentType.EqualsLiteral("text/plain"));
+ ASSERT_TRUE(contentCharset.EqualsLiteral("gbk"));
+}
+
+TEST(MimeTypeParsing, contentTypes10)
+{
+ const nsAutoCString val("text/html;charset=gbk,text/plain,text/html");
+ nsCString contentType;
+ nsCString contentCharset;
+
+ bool parsed = CMimeType::Parse(val, contentType, contentCharset);
+
+ ASSERT_TRUE(parsed);
+ ASSERT_TRUE(contentType.EqualsLiteral("text/html"));
+ ASSERT_TRUE(contentCharset.EqualsLiteral(""));
+}
+
+TEST(MimeTypeParsing, contentTypes11)
+{
+ const nsAutoCString val("text/plain,*/*");
+ nsCString contentType;
+ nsCString contentCharset;
+
+ bool parsed = CMimeType::Parse(val, contentType, contentCharset);
+
+ ASSERT_TRUE(parsed);
+ ASSERT_TRUE(contentType.EqualsLiteral("text/plain"));
+ ASSERT_TRUE(contentCharset.EqualsLiteral(""));
+}
+
+TEST(MimeTypeParsing, contentTypes12)
+{
+ const nsAutoCString val("text/html,*/*");
+ nsCString contentType;
+ nsCString contentCharset;
+
+ bool parsed = CMimeType::Parse(val, contentType, contentCharset);
+
+ ASSERT_TRUE(parsed);
+ ASSERT_TRUE(contentType.EqualsLiteral("text/html"));
+ ASSERT_TRUE(contentCharset.EqualsLiteral(""));
+}
+
+TEST(MimeTypeParsing, contentTypes13)
+{
+ const nsAutoCString val("*/*,text/html");
+ nsCString contentType;
+ nsCString contentCharset;
+
+ bool parsed = CMimeType::Parse(val, contentType, contentCharset);
+
+ ASSERT_TRUE(parsed);
+ ASSERT_TRUE(contentType.EqualsLiteral("text/html"));
+ ASSERT_TRUE(contentCharset.EqualsLiteral(""));
+}
+
+TEST(MimeTypeParsing, contentTypes14)
+{
+ const nsAutoCString val("text/plain,*/*;charset=gbk");
+ nsCString contentType;
+ nsCString contentCharset;
+
+ bool parsed = CMimeType::Parse(val, contentType, contentCharset);
+
+ ASSERT_TRUE(parsed);
+ ASSERT_TRUE(contentType.EqualsLiteral("text/plain"));
+ ASSERT_TRUE(contentCharset.EqualsLiteral(""));
+}
+
+TEST(MimeTypeParsing, contentTypes15)
+{
+ const nsAutoCString val("text/html,*/*;charset=gbk");
+ nsCString contentType;
+ nsCString contentCharset;
+
+ bool parsed = CMimeType::Parse(val, contentType, contentCharset);
+
+ ASSERT_TRUE(parsed);
+ ASSERT_TRUE(contentType.EqualsLiteral("text/html"));
+ ASSERT_TRUE(contentCharset.EqualsLiteral(""));
+}
+
+TEST(MimeTypeParsing, contentTypes16)
+{
+ const nsAutoCString val("text/html;x=\",text/plain");
+ nsCString contentType;
+ nsCString contentCharset;
+
+ bool parsed = CMimeType::Parse(val, contentType, contentCharset);
+
+ ASSERT_TRUE(parsed);
+ ASSERT_TRUE(contentType.EqualsLiteral("text/html"));
+ ASSERT_TRUE(contentCharset.EqualsLiteral(""));
+}
+
+TEST(MimeTypeParsing, contentTypes17)
+{
+ const nsAutoCString val("text/html;\",text/plain");
+ nsCString contentType;
+ nsCString contentCharset;
+
+ bool parsed = CMimeType::Parse(val, contentType, contentCharset);
+
+ ASSERT_TRUE(parsed);
+ ASSERT_TRUE(contentType.EqualsLiteral("text/html"));
+ ASSERT_TRUE(contentCharset.EqualsLiteral(""));
+}
+
+TEST(MimeTypeParsing, contentTypes18)
+{
+ const nsAutoCString val("text/html;\",\\\",text/plain");
+ nsCString contentType;
+ nsCString contentCharset;
+
+ bool parsed = CMimeType::Parse(val, contentType, contentCharset);
+
+ ASSERT_TRUE(parsed);
+ ASSERT_TRUE(contentType.EqualsLiteral("text/html"));
+ ASSERT_TRUE(contentCharset.EqualsLiteral(""));
+}
+
+TEST(MimeTypeParsing, contentTypes19)
+{
+ const nsAutoCString val("text/html;\",\\\",text/plain,\";charset=GBK");
+ nsCString contentType;
+ nsCString contentCharset;
+
+ bool parsed = CMimeType::Parse(val, contentType, contentCharset);
+
+ ASSERT_TRUE(parsed);
+ ASSERT_TRUE(contentType.EqualsLiteral("text/html"));
+ ASSERT_TRUE(contentCharset.EqualsLiteral("GBK"));
+}
+
+TEST(MimeTypeParsing, contentTypes20)
+{
+ const nsAutoCString val("text/html;\",\",text/plain");
+ nsCString contentType;
+ nsCString contentCharset;
+
+ bool parsed = CMimeType::Parse(val, contentType, contentCharset);
+
+ ASSERT_TRUE(parsed);
+ ASSERT_TRUE(contentType.EqualsLiteral("text/plain"));
+ ASSERT_TRUE(contentCharset.EqualsLiteral(""));
+}
diff --git a/dom/base/test/gtest/TestParser.cpp b/dom/base/test/gtest/TestParser.cpp
new file mode 100644
index 0000000000..d9240cced7
--- /dev/null
+++ b/dom/base/test/gtest/TestParser.cpp
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "mozilla/dom/DOMParser.h"
+#include "mozilla/dom/Document.h"
+#include "nsIDocumentEncoder.h"
+#include "mozilla/ErrorResult.h"
+
+// This is a test for mozilla::dom::DOMParser::CreateWithoutGlobal() which was
+// implemented for use in Thunderbird's MailNews module.
+
+// int main(int argc, char** argv)
+TEST(TestParser, TestParserMain)
+{
+ bool allTestsPassed = false;
+ constexpr auto htmlInput =
+ u"<html><head>"
+ "<meta http-equiv=\"content-type\" content=\"text/html; charset=\">"
+ "</head><body>Hello <b>Thunderbird!</b></body></html>"_ns;
+
+ do {
+ // Parse the HTML source.
+ mozilla::IgnoredErrorResult rv2;
+ RefPtr<mozilla::dom::DOMParser> parser =
+ mozilla::dom::DOMParser::CreateWithoutGlobal(rv2);
+ if (rv2.Failed()) break;
+ nsCOMPtr<mozilla::dom::Document> document = parser->ParseFromString(
+ htmlInput, mozilla::dom::SupportedType::Text_html, rv2);
+ if (rv2.Failed()) break;
+
+ // Serialize it back to HTML source again.
+ nsCOMPtr<nsIDocumentEncoder> encoder =
+ do_createDocumentEncoder("text/html");
+ if (!encoder) break;
+ nsresult rv =
+ encoder->Init(document, u"text/html"_ns, nsIDocumentEncoder::OutputRaw);
+ if (NS_FAILED(rv)) break;
+ nsString parsed;
+ rv = encoder->EncodeToString(parsed);
+ if (NS_FAILED(rv)) break;
+
+ EXPECT_TRUE(parsed.Equals(htmlInput));
+ allTestsPassed = true;
+ } while (false);
+
+ EXPECT_TRUE(allTestsPassed);
+}
diff --git a/dom/base/test/gtest/TestPlainTextSerializer.cpp b/dom/base/test/gtest/TestPlainTextSerializer.cpp
new file mode 100644
index 0000000000..52ef864aa9
--- /dev/null
+++ b/dom/base/test/gtest/TestPlainTextSerializer.cpp
@@ -0,0 +1,340 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "nsServiceManagerUtils.h"
+#include "nsString.h"
+#include "nsIDocumentEncoder.h"
+#include "nsCRT.h"
+#include "nsIParserUtils.h"
+
+const uint32_t kDefaultWrapColumn = 72;
+
+void ConvertBufToPlainText(nsString& aConBuf, int aFlag, uint32_t aWrapColumn) {
+ nsCOMPtr<nsIParserUtils> utils = do_GetService(NS_PARSERUTILS_CONTRACTID);
+ utils->ConvertToPlainText(aConBuf, aFlag, aWrapColumn, aConBuf);
+}
+
+// Test for ASCII with format=flowed; delsp=yes
+TEST(PlainTextSerializer, ASCIIWithFlowedDelSp)
+{
+ nsString test;
+ nsString result;
+
+ test.AssignLiteral(
+ "<html><body>"
+ "Firefox Firefox Firefox Firefox "
+ "Firefox Firefox Firefox Firefox "
+ "Firefox Firefox Firefox Firefox"
+ "</body></html>");
+
+ ConvertBufToPlainText(test,
+ nsIDocumentEncoder::OutputFormatted |
+ nsIDocumentEncoder::OutputCRLineBreak |
+ nsIDocumentEncoder::OutputLFLineBreak |
+ nsIDocumentEncoder::OutputFormatFlowed |
+ nsIDocumentEncoder::OutputFormatDelSp,
+ kDefaultWrapColumn);
+
+ // create result case
+ result.AssignLiteral(
+ "Firefox Firefox Firefox Firefox "
+ "Firefox Firefox Firefox Firefox "
+ "Firefox \r\nFirefox Firefox Firefox\r\n");
+
+ ASSERT_TRUE(test.Equals(result))
+ << "Wrong HTML to ASCII text serialization with format=flowed; delsp=yes";
+}
+
+TEST(PlainTextSerializer, Bug1864820)
+{
+ nsString test(
+ uR"#(
+<html><body>
+&gt;&nbsp;&nbsp;label=master&amp;label=experimental&amp;product=chrome&amp;product=firefox&amp;product=safari&amp;aligned&amp;view=interop&amp;q=label%3Ainterop-2023-property
+<blockquote>
+&gt;&nbsp;&nbsp;label=master&amp;label=experimental&amp;product=chrome&amp;product=firefox&amp;product=safari&amp;aligned&amp;view=interop&amp;q=label%3Ainterop-2023-property
+</blockquote>
+</body></html>
+)#");
+
+ ConvertBufToPlainText(test,
+ nsIDocumentEncoder::OutputFormatted |
+ nsIDocumentEncoder::OutputPersistNBSP |
+ nsIDocumentEncoder::OutputLFLineBreak |
+ nsIDocumentEncoder::OutputFormatFlowed,
+ kDefaultWrapColumn);
+
+ nsString result(
+ uR"#(
+ >  label=master&label=experimental&product=chrome&product=firefox&product=safari&aligned&view=interop&q=label%3Ainterop-2023-property
+
+ >  label=master&label=experimental&product=chrome&product=firefox&product=safari&aligned&view=interop&q=label%3Ainterop-2023-property
+)#");
+ result.Trim(" \n");
+ test.Trim(" \n");
+ ASSERT_TRUE(test.Equals(result))
+ << "Shouldn't hang with format=flowed: " << NS_ConvertUTF16toUTF8(test).get();
+}
+
+// Test for CJK with format=flowed; delsp=yes
+TEST(PlainTextSerializer, CJKWithFlowedDelSp)
+{
+ nsString test;
+ nsString result;
+
+ test.AssignLiteral("<html><body>");
+ for (uint32_t i = 0; i < 40; i++) {
+ // Insert Kanji (U+5341)
+ test.Append(0x5341);
+ }
+ test.AppendLiteral("</body></html>");
+
+ ConvertBufToPlainText(test,
+ nsIDocumentEncoder::OutputFormatted |
+ nsIDocumentEncoder::OutputCRLineBreak |
+ nsIDocumentEncoder::OutputLFLineBreak |
+ nsIDocumentEncoder::OutputFormatFlowed |
+ nsIDocumentEncoder::OutputFormatDelSp,
+ kDefaultWrapColumn);
+
+ // create result case
+ for (uint32_t i = 0; i < 36; i++) {
+ result.Append(0x5341);
+ }
+ result.AppendLiteral(" \r\n");
+ for (uint32_t i = 0; i < 4; i++) {
+ result.Append(0x5341);
+ }
+ result.AppendLiteral("\r\n");
+
+ ASSERT_TRUE(test.Equals(result))
+ << "Wrong HTML to CJK text serialization with format=flowed; delsp=yes";
+}
+
+// Test for CJK with DisallowLineBreaking
+TEST(PlainTextSerializer, CJKWithDisallowLineBreaking)
+{
+ nsString test;
+ nsString result;
+
+ test.AssignLiteral("<html><body>");
+ for (uint32_t i = 0; i < 400; i++) {
+ // Insert Kanji (U+5341)
+ test.Append(0x5341);
+ }
+ test.AppendLiteral("</body></html>");
+
+ ConvertBufToPlainText(test,
+ nsIDocumentEncoder::OutputFormatted |
+ nsIDocumentEncoder::OutputCRLineBreak |
+ nsIDocumentEncoder::OutputLFLineBreak |
+ nsIDocumentEncoder::OutputFormatFlowed |
+ nsIDocumentEncoder::OutputDisallowLineBreaking,
+ kDefaultWrapColumn);
+
+ // create result case
+ for (uint32_t i = 0; i < 400; i++) {
+ result.Append(0x5341);
+ }
+ result.AppendLiteral("\r\n");
+
+ ASSERT_TRUE(test.Equals(result))
+ << "Wrong HTML to CJK text serialization with OutputDisallowLineBreaking";
+}
+
+// Test for Latin with DisallowLineBreaking
+TEST(PlainTextSerializer, LatinWithDisallowLineBreaking)
+{
+ nsString test;
+ test.AssignLiteral("<html><body>");
+ for (uint32_t i = 0; i < 400; i++) {
+ // Insert á (Latin Small Letter a with Acute) (U+00E1)
+ test.Append(0x00E1);
+ }
+ test.AppendLiteral("</body></html>\r\n");
+
+ ConvertBufToPlainText(test,
+ nsIDocumentEncoder::OutputFormatted |
+ nsIDocumentEncoder::OutputCRLineBreak |
+ nsIDocumentEncoder::OutputLFLineBreak |
+ nsIDocumentEncoder::OutputFormatFlowed |
+ nsIDocumentEncoder::OutputDisallowLineBreaking,
+ kDefaultWrapColumn);
+
+ // Create expect case.
+ nsString expect;
+ for (uint32_t i = 0; i < 400; i++) {
+ expect.Append(0x00E1);
+ }
+ expect.AppendLiteral(" \r\n\r\n");
+
+ ASSERT_TRUE(test.Equals(expect))
+ << "Wrong HTML to Latin text serialization with OutputDisallowLineBreaking";
+}
+
+// Test for ASCII with format=flowed; and quoted lines in preformatted span.
+TEST(PlainTextSerializer, PreformatFlowedQuotes)
+{
+ nsString test;
+ nsString result;
+
+ test.AssignLiteral(
+ "<html><body>"
+ "<span style=\"white-space: pre-wrap;\" _moz_quote=\"true\">"
+ "&gt; Firefox Firefox Firefox Firefox <br>"
+ "&gt; Firefox Firefox Firefox Firefox<br>"
+ "&gt;<br>"
+ "&gt;&gt; Firefox Firefox Firefox Firefox <br>"
+ "&gt;&gt; Firefox Firefox Firefox Firefox<br>"
+ "</span></body></html>");
+
+ ConvertBufToPlainText(test,
+ nsIDocumentEncoder::OutputFormatted |
+ nsIDocumentEncoder::OutputCRLineBreak |
+ nsIDocumentEncoder::OutputLFLineBreak |
+ nsIDocumentEncoder::OutputFormatFlowed,
+ kDefaultWrapColumn);
+
+ // create result case
+ result.AssignLiteral(
+ "> Firefox Firefox Firefox Firefox \r\n"
+ "> Firefox Firefox Firefox Firefox\r\n"
+ ">\r\n"
+ ">> Firefox Firefox Firefox Firefox \r\n"
+ ">> Firefox Firefox Firefox Firefox\r\n");
+
+ ASSERT_TRUE(test.Equals(result))
+ << "Wrong HTML to ASCII text serialization "
+ "with format=flowed; and quoted "
+ "lines";
+}
+
+TEST(PlainTextSerializer, PrettyPrintedHtml)
+{
+ nsString test;
+ test.AppendLiteral("<html>" NS_LINEBREAK "<body>" NS_LINEBREAK
+ " first<br>" NS_LINEBREAK " second<br>" NS_LINEBREAK
+ "</body>" NS_LINEBREAK "</html>");
+
+ ConvertBufToPlainText(test, 0, kDefaultWrapColumn);
+ ASSERT_TRUE(test.EqualsLiteral("first" NS_LINEBREAK "second" NS_LINEBREAK))
+ << "Wrong prettyprinted html to text serialization";
+}
+
+TEST(PlainTextSerializer, PreElement)
+{
+ nsString test;
+ test.AppendLiteral("<html>" NS_LINEBREAK "<body>" NS_LINEBREAK
+ "<pre>" NS_LINEBREAK " first" NS_LINEBREAK
+ " second" NS_LINEBREAK "</pre>" NS_LINEBREAK
+ "</body>" NS_LINEBREAK "</html>");
+
+ ConvertBufToPlainText(test, 0, kDefaultWrapColumn);
+ ASSERT_TRUE(test.EqualsLiteral(" first" NS_LINEBREAK
+ " second" NS_LINEBREAK NS_LINEBREAK))
+ << "Wrong prettyprinted html to text serialization";
+}
+
+TEST(PlainTextSerializer, BlockElement)
+{
+ nsString test;
+ test.AppendLiteral("<html>" NS_LINEBREAK "<body>" NS_LINEBREAK
+ "<div>" NS_LINEBREAK " first" NS_LINEBREAK
+ "</div>" NS_LINEBREAK "<div>" NS_LINEBREAK
+ " second" NS_LINEBREAK "</div>" NS_LINEBREAK
+ "</body>" NS_LINEBREAK "</html>");
+
+ ConvertBufToPlainText(test, 0, kDefaultWrapColumn);
+ ASSERT_TRUE(test.EqualsLiteral("first" NS_LINEBREAK "second" NS_LINEBREAK))
+ << "Wrong prettyprinted html to text serialization";
+}
+
+TEST(PlainTextSerializer, PreWrapElementForThunderbird)
+{
+ // This test examines the magic pre-wrap setup that Thunderbird relies on.
+ nsString test;
+ test.AppendLiteral("<html>" NS_LINEBREAK
+ "<body style=\"white-space: pre-wrap;\">" NS_LINEBREAK
+ "<pre>" NS_LINEBREAK
+ " first line is too long" NS_LINEBREAK
+ " second line is even loooonger " NS_LINEBREAK
+ "</pre>" NS_LINEBREAK "</body>" NS_LINEBREAK "</html>");
+
+ const uint32_t wrapColumn = 10;
+ ConvertBufToPlainText(test, nsIDocumentEncoder::OutputWrap, wrapColumn);
+ // "\n\n first\nline is\ntoo long\n second\nline is\neven\nloooonger\n\n\n"
+ ASSERT_TRUE(test.EqualsLiteral(
+ NS_LINEBREAK NS_LINEBREAK
+ " first" NS_LINEBREAK "line is" NS_LINEBREAK "too long" NS_LINEBREAK
+ " second" NS_LINEBREAK "line is" NS_LINEBREAK "even" NS_LINEBREAK
+ "loooonger" NS_LINEBREAK NS_LINEBREAK NS_LINEBREAK))
+ << "Wrong prettyprinted html to text serialization";
+}
+
+TEST(PlainTextSerializer, Simple)
+{
+ nsString test;
+ test.AppendLiteral(
+ "<html><base>base</base><head><span>span</span></head>"
+ "<body>body</body></html>");
+ ConvertBufToPlainText(test, 0, kDefaultWrapColumn);
+ ASSERT_TRUE(test.EqualsLiteral("basespanbody"))
+ << "Wrong html to text serialization";
+}
+
+TEST(PlainTextSerializer, OneHundredAndOneOL)
+{
+ nsAutoString test;
+ test.AppendLiteral(
+ "<html>"
+ "<body>"
+ "<ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><"
+ "ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><"
+ "ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><"
+ "ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><"
+ "ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><"
+ "ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol></ol></ol></ol></"
+ "ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></"
+ "ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></"
+ "ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></"
+ "ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></"
+ "ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></"
+ "ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></"
+ "ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></"
+ "ol></ol><li>X</li></ol>"
+ "</body>"
+ "</html>");
+
+ ConvertBufToPlainText(test, nsIDocumentEncoder::OutputFormatted,
+ kDefaultWrapColumn);
+
+ nsAutoString expected;
+ expected.AppendLiteral(" 1. X" NS_LINEBREAK);
+ ASSERT_EQ(test, expected);
+}
+
+TEST(PlainTextSerializer, BlockQuoteCite)
+{
+ nsAutoString test;
+ test.AppendLiteral(u"<blockquote type=cite>hello world</blockquote>");
+
+ const uint32_t wrapColumn = 10;
+ ConvertBufToPlainText(test,
+ nsIDocumentEncoder::OutputFormatted |
+ nsIDocumentEncoder::OutputFormatFlowed |
+ nsIDocumentEncoder::OutputCRLineBreak |
+ nsIDocumentEncoder::OutputLFLineBreak,
+ wrapColumn);
+
+ constexpr auto expect = NS_LITERAL_STRING_FROM_CSTRING(
+ "> hello \r\n"
+ "> world\r\n");
+
+ ASSERT_TRUE(test.Equals(expect))
+ << "Wrong blockquote cite to text serialization";
+}
diff --git a/dom/base/test/gtest/TestScheduler.cpp b/dom/base/test/gtest/TestScheduler.cpp
new file mode 100644
index 0000000000..5d04926627
--- /dev/null
+++ b/dom/base/test/gtest/TestScheduler.cpp
@@ -0,0 +1,348 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "mozilla/dom/CCGCScheduler.h"
+#include "mozilla/TimeStamp.h"
+
+// This is a test for mozilla::CCGCScheduler.
+
+using namespace mozilla;
+
+static TimeDuration kOneSecond = TimeDuration::FromSeconds(1);
+static TimeDuration kTenthSecond = TimeDuration::FromSeconds(0.1);
+static TimeDuration kFrameDuration = TimeDuration::FromSeconds(1.0 / 60.0);
+
+static mozilla::TimeStamp sNow = TimeStamp::Now();
+
+static mozilla::TimeStamp AdvanceTime(TimeDuration aDuration) {
+ sNow += aDuration;
+ return sNow;
+}
+
+static TimeStamp Now() { return sNow; }
+
+static uint32_t sSuspected = 0;
+
+static uint32_t SuspectedCCObjects() { return sSuspected; }
+static void SetNumSuspected(uint32_t n) { sSuspected = n; }
+static void SuspectMore(uint32_t n) { sSuspected += n; }
+
+using CCRunnerState = mozilla::CCGCScheduler::CCRunnerState;
+
+class TestGC {
+ protected:
+ CCGCScheduler& mScheduler;
+
+ public:
+ explicit TestGC(CCGCScheduler& aScheduler) : mScheduler(aScheduler) {}
+ void Run(int aNumSlices);
+};
+
+void TestGC::Run(int aNumSlices) {
+ // Make the purple buffer nearly empty so it is itself not an adequate reason
+ // for wanting a CC.
+ static_assert(3 < mozilla::kCCPurpleLimit);
+ SetNumSuspected(3);
+
+ // Running the GC should not influence whether a CC is currently seen as
+ // needed. But the first time we run GC, it will be false; later, we will
+ // have run a GC and set it to true.
+ CCReason neededCCAtStartOfGC =
+ mScheduler.IsCCNeeded(Now(), SuspectedCCObjects());
+
+ mScheduler.NoteGCBegin(JS::GCReason::API);
+
+ for (int slice = 0; slice < aNumSlices; slice++) {
+ EXPECT_TRUE(mScheduler.InIncrementalGC());
+ TimeStamp idleDeadline = Now() + kTenthSecond;
+ js::SliceBudget budget =
+ mScheduler.ComputeInterSliceGCBudget(idleDeadline, Now());
+ TimeDuration budgetDuration =
+ TimeDuration::FromMilliseconds(budget.timeBudget());
+ EXPECT_NEAR(budgetDuration.ToSeconds(), 0.1, 1.e-6);
+ // Pretend the GC took exactly the budget.
+ AdvanceTime(budgetDuration);
+
+ EXPECT_EQ(mScheduler.IsCCNeeded(Now(), SuspectedCCObjects()),
+ neededCCAtStartOfGC);
+
+ // Mutator runs for 1 second.
+ AdvanceTime(kOneSecond);
+ }
+
+ mScheduler.NoteGCEnd();
+ mScheduler.SetNeedsFullGC(false);
+}
+
+class TestCC {
+ protected:
+ CCGCScheduler& mScheduler;
+
+ public:
+ explicit TestCC(CCGCScheduler& aScheduler) : mScheduler(aScheduler) {}
+
+ void Run(int aNumSlices) {
+ Prepare();
+ MaybePokeCC();
+ TimerFires(aNumSlices);
+ EndCycleCollectionCallback();
+ KillCCRunner();
+ }
+
+ virtual void Prepare() = 0;
+ virtual void MaybePokeCC();
+ virtual void TimerFires(int aNumSlices);
+ virtual void RunSlices(int aNumSlices);
+ virtual void RunSlice(TimeStamp aCCStartTime, TimeStamp aPrevSliceEnd,
+ int aSliceNum, int aNumSlices) = 0;
+ virtual void ForgetSkippable();
+ virtual void EndCycleCollectionCallback();
+ virtual void KillCCRunner();
+};
+
+void TestCC::MaybePokeCC() {
+ // nsJSContext::MaybePokeCC
+
+ // In all tests so far, we will be running this just after a GC.
+ CCReason reason = mScheduler.ShouldScheduleCC(Now(), SuspectedCCObjects());
+ EXPECT_EQ(reason, CCReason::GC_FINISHED);
+
+ mScheduler.InitCCRunnerStateMachine(CCRunnerState::ReducePurple, reason);
+ EXPECT_TRUE(mScheduler.IsEarlyForgetSkippable());
+}
+
+void TestCC::TimerFires(int aNumSlices) {
+ // Series of CCRunner timer fires.
+ CCRunnerStep step;
+
+ while (true) {
+ SuspectMore(1000);
+ TimeStamp idleDeadline = Now() + kOneSecond;
+ step =
+ mScheduler.AdvanceCCRunner(idleDeadline, Now(), SuspectedCCObjects());
+ // Should first see a series of ForgetSkippable actions.
+ if (step.mAction != CCRunnerAction::ForgetSkippable ||
+ step.mParam.mRemoveChildless != KeepChildless) {
+ break;
+ }
+ EXPECT_EQ(step.mYield, Yield);
+ ForgetSkippable();
+ }
+
+ while (step.mYield == Continue) {
+ TimeStamp idleDeadline = Now() + kOneSecond;
+ step =
+ mScheduler.AdvanceCCRunner(idleDeadline, Now(), SuspectedCCObjects());
+ }
+ EXPECT_EQ(step.mAction, CCRunnerAction::ForgetSkippable);
+ EXPECT_EQ(step.mParam.mRemoveChildless, RemoveChildless);
+ ForgetSkippable();
+
+ TimeStamp idleDeadline = Now() + kOneSecond;
+ step = mScheduler.AdvanceCCRunner(idleDeadline, Now(), SuspectedCCObjects());
+ EXPECT_EQ(step.mAction, CCRunnerAction::CleanupContentUnbinder);
+ step = mScheduler.AdvanceCCRunner(idleDeadline, Now(), SuspectedCCObjects());
+ EXPECT_EQ(step.mAction, CCRunnerAction::CleanupDeferred);
+
+ mScheduler.NoteCCBegin(CCReason::API, Now(), 0, sSuspected, 0);
+ RunSlices(aNumSlices);
+}
+
+void TestCC::ForgetSkippable() {
+ uint32_t suspectedBefore = sSuspected;
+ // ...ForgetSkippable would happen here...
+ js::SliceBudget budget =
+ mScheduler.ComputeForgetSkippableBudget(Now(), Now() + kTenthSecond);
+ EXPECT_NEAR(budget.timeBudget(), kTenthSecond.ToMilliseconds(), 1);
+ AdvanceTime(kTenthSecond);
+ mScheduler.NoteForgetSkippableComplete(Now(), suspectedBefore,
+ SuspectedCCObjects());
+}
+
+void TestCC::RunSlices(int aNumSlices) {
+ TimeStamp ccStartTime = Now();
+ TimeStamp prevSliceEnd = ccStartTime;
+ for (int ccslice = 0; ccslice < aNumSlices; ccslice++) {
+ RunSlice(ccStartTime, prevSliceEnd, ccslice, aNumSlices);
+ prevSliceEnd = Now();
+ }
+
+ SetNumSuspected(0);
+}
+
+void TestCC::EndCycleCollectionCallback() {
+ // nsJSContext::EndCycleCollectionCallback
+ CycleCollectorResults results;
+ results.mFreedGCed = 10;
+ results.mFreedJSZones = 2;
+ mScheduler.NoteCCEnd(results, Now(), TimeDuration());
+
+ // Because > 0 zones were freed.
+ EXPECT_TRUE(mScheduler.NeedsGCAfterCC());
+}
+
+void TestCC::KillCCRunner() {
+ // nsJSContext::KillCCRunner
+ mScheduler.KillCCRunner();
+}
+
+class TestIdleCC : public TestCC {
+ public:
+ explicit TestIdleCC(CCGCScheduler& aScheduler) : TestCC(aScheduler) {}
+
+ void Prepare() override;
+ void RunSlice(TimeStamp aCCStartTime, TimeStamp aPrevSliceEnd, int aSliceNum,
+ int aNumSlices) override;
+};
+
+void TestIdleCC::Prepare() { EXPECT_TRUE(!mScheduler.InIncrementalGC()); }
+
+void TestIdleCC::RunSlice(TimeStamp aCCStartTime, TimeStamp aPrevSliceEnd,
+ int aSliceNum, int aNumSlices) {
+ CCRunnerStep step;
+ TimeStamp idleDeadline = Now() + kTenthSecond;
+
+ // The scheduler should request a CycleCollect slice.
+ step = mScheduler.AdvanceCCRunner(idleDeadline, Now(), SuspectedCCObjects());
+ EXPECT_EQ(step.mAction, CCRunnerAction::CycleCollect);
+
+ // nsJSContext::RunCycleCollectorSlice
+
+ EXPECT_FALSE(mScheduler.InIncrementalGC());
+ bool preferShorter;
+ js::SliceBudget budget = mScheduler.ComputeCCSliceBudget(
+ idleDeadline, aCCStartTime, aPrevSliceEnd, Now(), &preferShorter);
+ // The scheduler will set the budget to our deadline (0.1sec in the future).
+ EXPECT_NEAR(budget.timeBudget(), kTenthSecond.ToMilliseconds(), 1);
+ EXPECT_FALSE(preferShorter);
+
+ AdvanceTime(kTenthSecond);
+}
+
+class TestNonIdleCC : public TestCC {
+ public:
+ explicit TestNonIdleCC(CCGCScheduler& aScheduler) : TestCC(aScheduler) {}
+
+ void Prepare() override;
+ void RunSlice(TimeStamp aCCStartTime, TimeStamp aPrevSliceEnd, int aSliceNum,
+ int aNumSlices) override;
+};
+
+void TestNonIdleCC::Prepare() {
+ EXPECT_TRUE(!mScheduler.InIncrementalGC());
+
+ // Advance time by an hour to give time for a user event in the past.
+ AdvanceTime(TimeDuration::FromSeconds(3600));
+}
+
+void TestNonIdleCC::RunSlice(TimeStamp aCCStartTime, TimeStamp aPrevSliceEnd,
+ int aSliceNum, int aNumSlices) {
+ CCRunnerStep step;
+ TimeStamp nullDeadline;
+
+ // The scheduler should tell us to run a slice of cycle collection.
+ step = mScheduler.AdvanceCCRunner(nullDeadline, Now(), SuspectedCCObjects());
+ EXPECT_EQ(step.mAction, CCRunnerAction::CycleCollect);
+
+ // nsJSContext::RunCycleCollectorSlice
+
+ EXPECT_FALSE(mScheduler.InIncrementalGC());
+
+ bool preferShorter;
+ js::SliceBudget budget = mScheduler.ComputeCCSliceBudget(
+ nullDeadline, aCCStartTime, aPrevSliceEnd, Now(), &preferShorter);
+ if (aSliceNum == 0) {
+ // First slice of the CC, so always use the baseBudget which is
+ // kICCSliceBudget (3ms) for a non-idle slice.
+ EXPECT_NEAR(budget.timeBudget(), kICCSliceBudget.ToMilliseconds(), 0.1);
+ } else if (aSliceNum == 1) {
+ // Second slice still uses the baseBudget, since not much time has passed
+ // so none of the lengthening mechanisms have kicked in yet.
+ EXPECT_NEAR(budget.timeBudget(), kICCSliceBudget.ToMilliseconds(), 0.1);
+ } else if (aSliceNum == 2) {
+ // We're not overrunning kMaxICCDuration, so we don't go unlimited.
+ EXPECT_FALSE(budget.isUnlimited());
+ // This slice is delayed, slice time should be increased.
+ EXPECT_NEAR(budget.timeBudget(),
+ MainThreadIdlePeriod::GetLongIdlePeriod() / 2, 0.1);
+ } else {
+ // We're not overrunning kMaxICCDuration, so we don't go unlimited.
+ EXPECT_FALSE(budget.isUnlimited());
+
+ // These slices are not delayed, but enough time has passed that the
+ // dominating factor is now the linear ramp up to max slice time at the
+ // halfway point to kMaxICCDuration.
+ EXPECT_TRUE(budget.timeBudget() > kICCSliceBudget.ToMilliseconds());
+ EXPECT_TRUE(budget.timeBudget() <=
+ MainThreadIdlePeriod::GetLongIdlePeriod());
+ }
+ EXPECT_TRUE(preferShorter); // Non-idle prefers shorter slices
+
+ AdvanceTime(TimeDuration::FromMilliseconds(budget.timeBudget()));
+ if (aSliceNum == 1) {
+ // Delay the third slice (only).
+ AdvanceTime(kICCIntersliceDelay * 2);
+ }
+}
+
+// Do a GC then CC then GC.
+static bool BasicScenario(CCGCScheduler& aScheduler, TestGC* aTestGC,
+ TestCC* aTestCC) {
+ // Run a 10-slice incremental GC.
+ aTestGC->Run(10);
+
+ // After a GC, the scheduler should decide to do a full CC regardless of the
+ // number of purple buffer entries.
+ SetNumSuspected(3);
+ EXPECT_EQ(aScheduler.IsCCNeeded(Now(), SuspectedCCObjects()),
+ CCReason::GC_FINISHED);
+
+ // Now we should want to CC.
+ EXPECT_EQ(aScheduler.ShouldScheduleCC(Now(), SuspectedCCObjects()),
+ CCReason::GC_FINISHED);
+
+ // Do a 5-slice CC.
+ aTestCC->Run(5);
+
+ // Not enough suspected objects to deserve a CC.
+ EXPECT_EQ(aScheduler.IsCCNeeded(Now(), SuspectedCCObjects()),
+ CCReason::NO_REASON);
+ EXPECT_EQ(aScheduler.ShouldScheduleCC(Now(), SuspectedCCObjects()),
+ CCReason::NO_REASON);
+ SetNumSuspected(10000);
+
+ // We shouldn't want to CC again yet, it's too soon.
+ EXPECT_EQ(aScheduler.ShouldScheduleCC(Now(), SuspectedCCObjects()),
+ CCReason::NO_REASON);
+ AdvanceTime(mozilla::kCCDelay);
+
+ // *Now* it's time for another CC.
+ EXPECT_EQ(aScheduler.ShouldScheduleCC(Now(), SuspectedCCObjects()),
+ CCReason::MANY_SUSPECTED);
+
+ // Run a 3-slice incremental GC.
+ EXPECT_TRUE(!aScheduler.InIncrementalGC());
+ aTestGC->Run(3);
+
+ return true;
+}
+
+static CCGCScheduler scheduler;
+static TestGC gc(scheduler);
+static TestIdleCC ccIdle(scheduler);
+static TestNonIdleCC ccNonIdle(scheduler);
+
+TEST(TestScheduler, Idle)
+{
+ // Cannot CC until we GC once.
+ EXPECT_EQ(scheduler.ShouldScheduleCC(Now(), SuspectedCCObjects()),
+ CCReason::NO_REASON);
+
+ EXPECT_TRUE(BasicScenario(scheduler, &gc, &ccIdle));
+}
+
+TEST(TestScheduler, NonIdle)
+{ EXPECT_TRUE(BasicScenario(scheduler, &gc, &ccNonIdle)); }
diff --git a/dom/base/test/gtest/TestXMLSerializerNoBreakLink.cpp b/dom/base/test/gtest/TestXMLSerializerNoBreakLink.cpp
new file mode 100644
index 0000000000..a63410303e
--- /dev/null
+++ b/dom/base/test/gtest/TestXMLSerializerNoBreakLink.cpp
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "nsCOMPtr.h"
+#include "nsIDocumentEncoder.h"
+#include "nsString.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/dom/DOMParser.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+// Test that serialising some DOM doesn't destroy links by word-wrapping long
+// href values containing spaces.
+TEST(TestXMLSerializerNoBreakLink, TestXMLSerializerNoBreakLinkMain)
+{
+ // Build up a stupidly-long URL with spaces. Default is to wrap at column
+ // 72, so we want to exceed that.
+ nsString longURL = u"http://www.example.com/link with spaces"_ns;
+ for (int i = 1; i < 125; ++i) {
+ longURL.Append(u' ');
+ longURL.Append(IntToTString<char16_t>(i));
+ }
+ nsString htmlInput =
+ u"<html><head>"
+ "<meta charset=\"utf-8\">"
+ "</head><body>Hello Thunderbird! <a href=\""_ns +
+ longURL + u"\">Link</a></body></html>"_ns;
+
+ // Parse HTML into a Document.
+ nsCOMPtr<Document> document;
+ {
+ IgnoredErrorResult rv;
+ RefPtr<DOMParser> parser = DOMParser::CreateWithoutGlobal(rv);
+ ASSERT_FALSE(rv.Failed());
+ document = parser->ParseFromString(htmlInput, SupportedType::Text_html, rv);
+ ASSERT_FALSE(rv.Failed());
+ }
+
+ // Serialize back in a variety of flavours and check the URL survives the
+ // round trip intact.
+ nsCString contentTypes[] = {"text/xml"_ns, "application/xml"_ns,
+ "application/xhtml+xml"_ns, "image/svg+xml"_ns,
+ "text/html"_ns};
+ for (auto const& contentType : contentTypes) {
+ uint32_t flagsToTest[] = {
+ nsIDocumentEncoder::OutputFormatted, nsIDocumentEncoder::OutputWrap,
+ nsIDocumentEncoder::OutputFormatted | nsIDocumentEncoder::OutputWrap};
+ for (uint32_t flags : flagsToTest) {
+ // Serialize doc back to HTML source again.
+ nsCOMPtr<nsIDocumentEncoder> encoder =
+ do_createDocumentEncoder(contentType.get());
+ ASSERT_TRUE(encoder);
+ nsresult rv =
+ encoder->Init(document, NS_ConvertASCIItoUTF16(contentType), flags);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ nsString parsed;
+ rv = encoder->EncodeToString(parsed);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ // URL is intact?
+ EXPECT_TRUE(parsed.Find(longURL) != kNotFound);
+ }
+ }
+}
diff --git a/dom/base/test/gtest/TestXPathGenerator.cpp b/dom/base/test/gtest/TestXPathGenerator.cpp
new file mode 100644
index 0000000000..c9f4993179
--- /dev/null
+++ b/dom/base/test/gtest/TestXPathGenerator.cpp
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "XPathGenerator.h"
+#include "nsString.h"
+
+TEST(TestXPathGenerator, TestQuoteArgumentWithoutQuote)
+{
+ nsAutoString arg;
+ arg.AssignLiteral(u"testing");
+
+ nsAutoString expectedResult;
+ expectedResult.AssignLiteral(u"\'testing\'");
+
+ nsAutoString result;
+ XPathGenerator::QuoteArgument(arg, result);
+
+ ASSERT_TRUE(expectedResult.Equals(result));
+}
+
+TEST(TestXPathGenerator, TestQuoteArgumentWithSingleQuote)
+{
+ nsAutoString arg;
+ arg.AssignLiteral(u"\'testing\'");
+
+ nsAutoString expectedResult;
+ expectedResult.AssignLiteral(u"\"\'testing\'\"");
+
+ nsAutoString result;
+ XPathGenerator::QuoteArgument(arg, result);
+
+ ASSERT_TRUE(expectedResult.Equals(result));
+}
+
+TEST(TestXPathGenerator, TestQuoteArgumentWithDoubleQuote)
+{
+ nsAutoString arg;
+ arg.AssignLiteral(u"\"testing\"");
+
+ nsAutoString expectedResult;
+ expectedResult.AssignLiteral(u"\'\"testing\"\'");
+
+ nsAutoString result;
+ XPathGenerator::QuoteArgument(arg, result);
+
+ ASSERT_TRUE(expectedResult.Equals(result));
+}
+
+TEST(TestXPathGenerator, TestQuoteArgumentWithSingleAndDoubleQuote)
+{
+ nsAutoString arg;
+ arg.AssignLiteral(u"\'testing\"");
+
+ nsAutoString expectedResult;
+ expectedResult.AssignLiteral(u"concat(\'\',\"\'\",\'testing\"\')");
+
+ nsAutoString result;
+ XPathGenerator::QuoteArgument(arg, result);
+ printf("Result: %s\nExpected: %s\n", NS_ConvertUTF16toUTF8(result).get(),
+ NS_ConvertUTF16toUTF8(expectedResult).get());
+
+ ASSERT_TRUE(expectedResult.Equals(result));
+}
+
+TEST(TestXPathGenerator,
+ TestQuoteArgumentWithDoubleQuoteAndASequenceOfSingleQuote)
+{
+ nsAutoString arg;
+ arg.AssignLiteral(u"\'\'\'\'testing\"");
+
+ nsAutoString expectedResult;
+ expectedResult.AssignLiteral(u"concat(\'\',\"\'\'\'\'\",\'testing\"\')");
+
+ nsAutoString result;
+ XPathGenerator::QuoteArgument(arg, result);
+ printf("Result: %s\nExpected: %s\n", NS_ConvertUTF16toUTF8(result).get(),
+ NS_ConvertUTF16toUTF8(expectedResult).get());
+
+ ASSERT_TRUE(expectedResult.Equals(result));
+}
+
+TEST(TestXPathGenerator,
+ TestQuoteArgumentWithDoubleQuoteAndTwoSequencesOfSingleQuote)
+{
+ nsAutoString arg;
+ arg.AssignLiteral(u"\'\'\'\'testing\'\'\'\'\'\'\"");
+
+ nsAutoString expectedResult;
+ expectedResult.AssignLiteral(
+ u"concat(\'\',\"\'\'\'\'\",\'testing\',\"\'\'\'\'\'\'\",\'\"\')");
+
+ nsAutoString result;
+ XPathGenerator::QuoteArgument(arg, result);
+ printf("Result: %s\nExpected: %s\n", NS_ConvertUTF16toUTF8(result).get(),
+ NS_ConvertUTF16toUTF8(expectedResult).get());
+
+ ASSERT_TRUE(expectedResult.Equals(result));
+}
+
+TEST(TestXPathGenerator,
+ TestQuoteArgumentWithDoubleQuoteAndTwoSequencesOfSingleQuoteInMiddle)
+{
+ nsAutoString arg;
+ arg.AssignLiteral(u"t\'\'\'\'estin\'\'\'\'\'\'\"g");
+
+ nsAutoString expectedResult;
+ expectedResult.AssignLiteral(
+ u"concat(\'t\',\"\'\'\'\'\",\'estin\',\"\'\'\'\'\'\'\",\'\"g\')");
+
+ nsAutoString result;
+ XPathGenerator::QuoteArgument(arg, result);
+ printf("Result: %s\nExpected: %s\n", NS_ConvertUTF16toUTF8(result).get(),
+ NS_ConvertUTF16toUTF8(expectedResult).get());
+
+ ASSERT_TRUE(expectedResult.Equals(result));
+}
+
+TEST(TestXPathGenerator, TestEscapeNameWithNormalCharacters)
+{
+ nsAutoString arg;
+ arg.AssignLiteral(u"testing");
+
+ nsAutoString expectedResult;
+ expectedResult.AssignLiteral(u"testing");
+
+ nsAutoString result;
+ XPathGenerator::EscapeName(arg, result);
+
+ ASSERT_TRUE(expectedResult.Equals(result));
+}
+
+TEST(TestXPathGenerator, TestEscapeNameWithSpecialCharacters)
+{
+ nsAutoString arg;
+ arg.AssignLiteral(u"^testing!");
+
+ nsAutoString expectedResult;
+ expectedResult.AssignLiteral(u"*[local-name()=\'^testing!\']");
+
+ nsAutoString result;
+ XPathGenerator::EscapeName(arg, result);
+ printf("Result: %s\nExpected: %s\n", NS_ConvertUTF16toUTF8(result).get(),
+ NS_ConvertUTF16toUTF8(expectedResult).get());
+
+ ASSERT_TRUE(expectedResult.Equals(result));
+}
diff --git a/dom/base/test/gtest/moz.build b/dom/base/test/gtest/moz.build
new file mode 100644
index 0000000000..9a767eb2ff
--- /dev/null
+++ b/dom/base/test/gtest/moz.build
@@ -0,0 +1,21 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, you can obtain one at http://mozilla.org/MPL/2.0/.
+
+UNIFIED_SOURCES += [
+ "TestContentUtils.cpp",
+ "TestMimeType.cpp",
+ "TestParser.cpp",
+ "TestPlainTextSerializer.cpp",
+ "TestScheduler.cpp",
+ "TestXMLSerializerNoBreakLink.cpp",
+ "TestXPathGenerator.cpp",
+]
+
+LOCAL_INCLUDES += ["/dom/base"]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul-gtest"