#include "gtest/gtest.h"
#include "gtest/MozGTestBench.h" // For MOZ_GTEST_BENCH
#include <regex>
#include "json/json.h"
#include "json/reader.h"
#include "mozilla/TextUtils.h"
#include "nsString.h"
#include "mozilla/net/MozURL.h"
#include "nsCOMPtr.h"
#include "nsDirectoryServiceDefs.h"
#include "nsNetUtil.h"
#include "nsIFile.h"
#include "nsIURI.h"
#include "nsStreamUtils.h"
#include "mozilla/BasePrincipal.h"
using namespace mozilla;
using namespace mozilla::net;
TEST(TestMozURL, Getters)
{
nsAutoCString href(
"http://user:pass@example.com/path?query#ref");
RefPtr<MozURL> url;
ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
ASSERT_TRUE(url->Scheme().EqualsLiteral(
"http"));
ASSERT_TRUE(url->Spec() == href);
ASSERT_TRUE(url->Username().EqualsLiteral(
"user"));
ASSERT_TRUE(url->Password().EqualsLiteral(
"pass"));
ASSERT_TRUE(url->Host().EqualsLiteral(
"example.com"));
ASSERT_TRUE(url->FilePath().EqualsLiteral(
"/path"));
ASSERT_TRUE(url->Query().EqualsLiteral(
"query"));
ASSERT_TRUE(url->Ref().EqualsLiteral(
"ref"));
url = nullptr;
ASSERT_EQ(MozURL::Init(getter_AddRefs(url),
""_ns), NS_ERROR_MALFORMED_URI);
ASSERT_EQ(url, nullptr);
}
TEST(TestMozURL, MutatorChain)
{
nsAutoCString href(
"http://user:pass@example.com/path?query#ref");
RefPtr<MozURL> url;
ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
nsAutoCString out;
RefPtr<MozURL> url2;
ASSERT_EQ(url->Mutate()
.SetScheme(
"https"_ns)
.SetUsername(
"newuser"_ns)
.SetPassword(
"newpass"_ns)
.SetHostname(
"test"_ns)
.SetFilePath(
"new/file/path"_ns)
.SetQuery(
"bla"_ns)
.SetRef(
"huh"_ns)
.Finalize(getter_AddRefs(url2)),
NS_OK);
ASSERT_TRUE(url2->Spec().EqualsLiteral(
"https://newuser:newpass@test/new/file/path?bla#huh"));
}
TEST(TestMozURL, MutatorFinalizeTwice)
{
nsAutoCString href(
"http://user:pass@example.com/path?query#ref");
RefPtr<MozURL> url;
ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
nsAutoCString out;
RefPtr<MozURL> url2;
MozURL::Mutator mut = url->Mutate();
mut.SetScheme(
"https"_ns);
// Change the scheme to https
ASSERT_EQ(mut.Finalize(getter_AddRefs(url2)), NS_OK);
ASSERT_TRUE(url2->Spec().EqualsLiteral(
"https://user:pass@example.com/path?query#ref"));
// Test that a second call to Finalize will result in an error code
url2 = nullptr;
ASSERT_EQ(mut.Finalize(getter_AddRefs(url2)), NS_ERROR_NOT_AVAILABLE);
ASSERT_EQ(url2, nullptr);
}
TEST(TestMozURL, MutatorErrorStatus)
{
nsAutoCString href(
"http://user:pass@example.com/path?query#ref");
RefPtr<MozURL> url;
ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
nsAutoCString out;
// Test that trying to set the scheme to a bad value will get you an error
MozURL::Mutator mut = url->Mutate();
mut.SetScheme(
"!@#$%^&*("_ns);
ASSERT_EQ(mut.GetStatus(), NS_ERROR_MALFORMED_URI);
// Test that the mutator will not work after one faulty operation
mut.SetScheme(
"test"_ns);
ASSERT_EQ(mut.GetStatus(), NS_ERROR_MALFORMED_URI);
}
TEST(TestMozURL, InitWithBase)
{
nsAutoCString href(
"https://example.net/a/b.html");
RefPtr<MozURL> url;
ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
ASSERT_TRUE(url->Spec().EqualsLiteral(
"https://example.net/a/b.html"));
RefPtr<MozURL> url2;
ASSERT_EQ(MozURL::Init(getter_AddRefs(url2),
"c.png"_ns, url), NS_OK);
ASSERT_TRUE(url2->Spec().EqualsLiteral(
"https://example.net/a/c.png"));
}
TEST(TestMozURL, Path)
{
nsAutoCString href(
"about:blank");
RefPtr<MozURL> url;
ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
ASSERT_TRUE(url->Spec().EqualsLiteral(
"about:blank"));
ASSERT_TRUE(url->Scheme().EqualsLiteral(
"about"));
ASSERT_TRUE(url->FilePath().EqualsLiteral(
"blank"));
}
TEST(TestMozURL, HostPort)
{
nsAutoCString href(
"https://user:pass@example.net:1234/path?query#ref");
RefPtr<MozURL> url;
ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
ASSERT_TRUE(url->HostPort().EqualsLiteral(
"example.net:1234"));
RefPtr<MozURL> url2;
url->Mutate().SetHostPort(
"test:321"_ns).Finalize(getter_AddRefs(url2));
ASSERT_TRUE(url2->HostPort().EqualsLiteral(
"test:321"));
ASSERT_TRUE(
url2->Spec().EqualsLiteral(
"https://user:pass@test:321/path?query#ref"));
href.Assign(
"https://user:pass@example.net:443/path?query#ref");
ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
ASSERT_TRUE(url->HostPort().EqualsLiteral(
"example.net"));
ASSERT_EQ(url->Port(), -1);
}
TEST(TestMozURL, Origin)
{
nsAutoCString href(
"https://user:pass@example.net:1234/path?query#ref");
RefPtr<MozURL> url;
ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
nsAutoCString out;
url->Origin(out);
ASSERT_TRUE(out.EqualsLiteral(
"https://example.net:1234"));
RefPtr<MozURL> url2;
ASSERT_EQ(MozURL::Init(getter_AddRefs(url2),
"file:///tmp/foo"_ns), NS_OK);
url2->Origin(out);
ASSERT_TRUE(out.EqualsLiteral(
"file:///tmp/foo"));
RefPtr<MozURL> url3;
ASSERT_EQ(
MozURL::Init(getter_AddRefs(url3),
nsLiteralCString(
"moz-extension://53711a8f-65ed-e742-9671-1f02e267c0bc/"
"foo/bar.html")),
NS_OK);
url3->Origin(out);
ASSERT_TRUE(out.EqualsLiteral(
"moz-extension://53711a8f-65ed-e742-9671-1f02e267c0bc"));
RefPtr<MozURL> url4;
ASSERT_EQ(MozURL::Init(getter_AddRefs(url4),
"resource://foo/bar.html"_ns),
NS_OK);
url4->Origin(out);
ASSERT_TRUE(out.EqualsLiteral(
"resource://foo"));
RefPtr<MozURL> url5;
ASSERT_EQ(MozURL::Init(getter_AddRefs(url5),
"about:home"_ns), NS_OK);
url5->Origin(out);
ASSERT_TRUE(out.EqualsLiteral(
"about:home"));
}
namespace {
bool OriginMatchesExpectedOrigin(
const nsACString& aOrigin,
const nsACString& aExpectedOrigin) {
if (aExpectedOrigin.Equals(
"null") &&
StringBeginsWith(aOrigin,
"moz-nullprincipal"_ns)) {
return true;
}
return aOrigin == aExpectedOrigin;
}
void CheckOrigin(
const nsACString& aSpec,
const nsACString& aBase,
const nsACString& aOrigin) {
nsCOMPtr<nsIURI> baseUri;
nsresult rv = NS_NewURI(getter_AddRefs(baseUri), aBase);
ASSERT_EQ(rv, NS_OK);
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri), aSpec, nullptr, baseUri);
ASSERT_EQ(rv, NS_OK);
OriginAttributes attrs;
nsCOMPtr<nsIPrincipal> principal =
BasePrincipal::CreateContentPrincipal(uri, attrs);
ASSERT_TRUE(principal);
nsCString origin;
rv = principal->GetOriginNoSuffix(origin);
ASSERT_EQ(rv, NS_OK);
EXPECT_TRUE(OriginMatchesExpectedOrigin(origin, aOrigin));
nsCString baseDomain;
rv = principal->GetBaseDomain(baseDomain);
RefPtr<MozURL> baseUrl;
ASSERT_EQ(MozURL::Init(getter_AddRefs(baseUrl), aBase), NS_OK);
RefPtr<MozURL> url;
ASSERT_EQ(MozURL::Init(getter_AddRefs(url), aSpec, baseUrl), NS_OK);
url->Origin(origin);
EXPECT_TRUE(OriginMatchesExpectedOrigin(origin, aOrigin));
}
}
// namespace
TEST(TestMozURL, UrlTestData)
{
nsCOMPtr<nsIFile> file;
nsresult rv =
NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR, getter_AddRefs(file));
ASSERT_EQ(rv, NS_OK);
rv = file->Append(u
"urltestdata.json"_ns);
ASSERT_EQ(rv, NS_OK);
bool exists;
rv = file->Exists(&exists);
ASSERT_EQ(rv, NS_OK);
ASSERT_TRUE(exists);
nsCOMPtr<nsIInputStream> stream;
rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
ASSERT_EQ(rv, NS_OK);
nsCOMPtr<nsIInputStream> bufferedStream;
rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
stream.forget(), 4096);
ASSERT_EQ(rv, NS_OK);
nsCString data;
rv = NS_ConsumeStream(bufferedStream, UINT32_MAX, data);
ASSERT_EQ(rv, NS_OK);
Json::Value root;
Json::CharReaderBuilder builder;
std::unique_ptr<Json::CharReader>
const reader(builder.newCharReader());
ASSERT_TRUE(
reader->parse(data.BeginReading(), data.EndReading(), &root, nullptr));
ASSERT_TRUE(root.isArray());
for (
auto& item : root) {
if (!item.isObject()) {
continue;
}
const Json::Value& skip = item[
"skip"];
ASSERT_TRUE(skip.isNull() || skip.isBool());
if (skip.isBool() && skip.asBool()) {
continue;
}
const Json::Value& failure = item[
"failure"];
ASSERT_TRUE(failure.isNull() || failure.isBool());
if (failure.isBool() && failure.asBool()) {
continue;
}
const Json::Value& origin = item[
"origin"];
ASSERT_TRUE(origin.isNull() || origin.isString());
if (origin.isNull()) {
continue;
}
const char* originBegin;
const char* originEnd;
origin.getString(&originBegin, &originEnd);
auto baseCString = nsDependentCString(
"about:blank");
const Json::Value& base = item[
"base"];
if (!base.isNull()) {
const char* baseBegin;
const char* baseEnd;
base.getString(&baseBegin, &baseEnd);
baseCString.Assign(nsDependentCSubstring(baseBegin, baseEnd));
}
const Json::Value& input = item[
"input"];
ASSERT_TRUE(input.isString());
const char* inputBegin;
const char* inputEnd;
input.getString(&inputBegin, &inputEnd);
CheckOrigin(nsDependentCString(inputBegin, inputEnd), baseCString,
nsDependentCString(originBegin, originEnd));
}
}