diff options
Diffstat (limited to 'dom/base/test/gtest')
-rw-r--r-- | dom/base/test/gtest/TestContentUtils.cpp | 221 | ||||
-rw-r--r-- | dom/base/test/gtest/TestMimeType.cpp | 1078 | ||||
-rw-r--r-- | dom/base/test/gtest/TestParser.cpp | 52 | ||||
-rw-r--r-- | dom/base/test/gtest/TestPlainTextSerializer.cpp | 340 | ||||
-rw-r--r-- | dom/base/test/gtest/TestScheduler.cpp | 348 | ||||
-rw-r--r-- | dom/base/test/gtest/TestXMLSerializerNoBreakLink.cpp | 69 | ||||
-rw-r--r-- | dom/base/test/gtest/TestXPathGenerator.cpp | 150 | ||||
-rw-r--r-- | dom/base/test/gtest/moz.build | 21 |
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> +> label=master&label=experimental&product=chrome&product=firefox&product=safari&aligned&view=interop&q=label%3Ainterop-2023-property +<blockquote> +> label=master&label=experimental&product=chrome&product=firefox&product=safari&aligned&view=interop&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\">" + "> Firefox Firefox Firefox Firefox <br>" + "> Firefox Firefox Firefox Firefox<br>" + "><br>" + ">> Firefox Firefox Firefox Firefox <br>" + ">> 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" |