summaryrefslogtreecommitdiffstats
path: root/netwerk/test/gtest
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /netwerk/test/gtest
parentInitial commit. (diff)
downloadfirefox-upstream/124.0.1.tar.xz
firefox-upstream/124.0.1.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--netwerk/test/gtest/TestBase64Stream.cpp123
-rw-r--r--netwerk/test/gtest/TestBind.cpp187
-rw-r--r--netwerk/test/gtest/TestBufferedInputStream.cpp252
-rw-r--r--netwerk/test/gtest/TestCommon.cpp7
-rw-r--r--netwerk/test/gtest/TestCommon.h45
-rw-r--r--netwerk/test/gtest/TestCookie.cpp1126
-rw-r--r--netwerk/test/gtest/TestDNSPacket.cpp69
-rw-r--r--netwerk/test/gtest/TestHeaders.cpp29
-rw-r--r--netwerk/test/gtest/TestHttpAtom.cpp39
-rw-r--r--netwerk/test/gtest/TestHttpAuthUtils.cpp43
-rw-r--r--netwerk/test/gtest/TestHttpChannel.cpp135
-rw-r--r--netwerk/test/gtest/TestHttpResponseHead.cpp183
-rw-r--r--netwerk/test/gtest/TestInputStreamTransport.cpp204
-rw-r--r--netwerk/test/gtest/TestIsValidIp.cpp178
-rw-r--r--netwerk/test/gtest/TestLinkHeader.cpp356
-rw-r--r--netwerk/test/gtest/TestMIMEInputStream.cpp268
-rw-r--r--netwerk/test/gtest/TestMozURL.cpp392
-rw-r--r--netwerk/test/gtest/TestNamedPipeService.cpp281
-rw-r--r--netwerk/test/gtest/TestNetworkLinkIdHashingDarwin.cpp93
-rw-r--r--netwerk/test/gtest/TestNetworkLinkIdHashingWindows.cpp88
-rw-r--r--netwerk/test/gtest/TestPACMan.cpp246
-rw-r--r--netwerk/test/gtest/TestProtocolProxyService.cpp164
-rw-r--r--netwerk/test/gtest/TestReadStreamToString.cpp190
-rw-r--r--netwerk/test/gtest/TestSSLTokensCache.cpp154
-rw-r--r--netwerk/test/gtest/TestServerTimingHeader.cpp238
-rw-r--r--netwerk/test/gtest/TestSocketTransportService.cpp164
-rw-r--r--netwerk/test/gtest/TestStandardURL.cpp441
-rw-r--r--netwerk/test/gtest/TestUDPSocket.cpp410
-rw-r--r--netwerk/test/gtest/TestURIMutator.cpp163
-rw-r--r--netwerk/test/gtest/moz.build80
-rw-r--r--netwerk/test/gtest/urltestdata-orig.json6148
-rw-r--r--netwerk/test/gtest/urltestdata.json9050
32 files changed, 21546 insertions, 0 deletions
diff --git a/netwerk/test/gtest/TestBase64Stream.cpp b/netwerk/test/gtest/TestBase64Stream.cpp
new file mode 100644
index 0000000000..47ef9e7bc6
--- /dev/null
+++ b/netwerk/test/gtest/TestBase64Stream.cpp
@@ -0,0 +1,123 @@
+/* -*- 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/Base64.h"
+#include "mozilla/gtest/MozAssertions.h"
+#include "nsCOMPtr.h"
+#include "nsIInputStream.h"
+#include "nsStringStream.h"
+
+namespace mozilla {
+namespace net {
+
+// An input stream whose ReadSegments method calls aWriter with writes of size
+// aStep from the provided aInput in order to test edge-cases related to small
+// buffers.
+class TestStream final : public nsIInputStream {
+ public:
+ NS_DECL_ISUPPORTS;
+
+ TestStream(const nsACString& aInput, uint32_t aStep)
+ : mInput(aInput), mStep(aStep) {}
+
+ NS_IMETHOD Close() override { MOZ_CRASH("This should not be called"); }
+
+ NS_IMETHOD Available(uint64_t* aLength) override {
+ *aLength = mInput.Length() - mPos;
+ return NS_OK;
+ }
+
+ NS_IMETHOD StreamStatus() override { return NS_OK; }
+
+ NS_IMETHOD Read(char* aBuffer, uint32_t aCount,
+ uint32_t* aReadCount) override {
+ MOZ_CRASH("This should not be called");
+ }
+
+ NS_IMETHOD ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
+ uint32_t aCount, uint32_t* aResult) override {
+ *aResult = 0;
+
+ if (mPos == mInput.Length()) {
+ return NS_OK;
+ }
+
+ while (aCount > 0) {
+ uint32_t amt = std::min(mStep, (uint32_t)(mInput.Length() - mPos));
+
+ uint32_t read = 0;
+ nsresult rv =
+ aWriter(this, aClosure, mInput.get() + mPos, *aResult, amt, &read);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ *aResult += read;
+ aCount -= read;
+ mPos += read;
+ }
+
+ return NS_OK;
+ }
+
+ NS_IMETHOD IsNonBlocking(bool* aNonBlocking) override {
+ *aNonBlocking = true;
+ return NS_OK;
+ }
+
+ private:
+ ~TestStream() = default;
+
+ nsCString mInput;
+ const uint32_t mStep;
+ uint32_t mPos = 0;
+};
+
+NS_IMPL_ISUPPORTS(TestStream, nsIInputStream)
+
+// Test the base64 encoder with writer buffer sizes between 1 byte and the
+// entire length of "Hello World!" in order to exercise various edge cases.
+TEST(TestBase64Stream, Run)
+{
+ nsCString input;
+ input.AssignLiteral("Hello World!");
+
+ for (uint32_t step = 1; step <= input.Length(); ++step) {
+ RefPtr<TestStream> ts = new TestStream(input, step);
+
+ nsAutoString encodedData;
+ nsresult rv = Base64EncodeInputStream(ts, encodedData, input.Length());
+ ASSERT_NS_SUCCEEDED(rv);
+
+ EXPECT_TRUE(encodedData.EqualsLiteral("SGVsbG8gV29ybGQh"));
+ }
+}
+
+TEST(TestBase64Stream, VaryingCount)
+{
+ nsCString input;
+ input.AssignLiteral("Hello World!");
+
+ std::pair<size_t, nsCString> tests[] = {
+ {0, "SGVsbG8gV29ybGQh"_ns}, {1, "SA=="_ns},
+ {5, "SGVsbG8="_ns}, {11, "SGVsbG8gV29ybGQ="_ns},
+ {12, "SGVsbG8gV29ybGQh"_ns}, {13, "SGVsbG8gV29ybGQh"_ns},
+ };
+
+ for (auto& [count, expected] : tests) {
+ nsCOMPtr<nsIInputStream> is;
+ nsresult rv = NS_NewCStringInputStream(getter_AddRefs(is), input);
+ ASSERT_NS_SUCCEEDED(rv);
+
+ nsAutoCString encodedData;
+ rv = Base64EncodeInputStream(is, encodedData, count);
+ ASSERT_NS_SUCCEEDED(rv);
+ EXPECT_EQ(encodedData, expected) << "count: " << count;
+ }
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/test/gtest/TestBind.cpp b/netwerk/test/gtest/TestBind.cpp
new file mode 100644
index 0000000000..371a09fdab
--- /dev/null
+++ b/netwerk/test/gtest/TestBind.cpp
@@ -0,0 +1,187 @@
+/* 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 "TestCommon.h"
+#include "gtest/gtest.h"
+#include "mozilla/gtest/MozAssertions.h"
+#include "nsISocketTransportService.h"
+#include "nsISocketTransport.h"
+#include "nsIServerSocket.h"
+#include "nsIAsyncInputStream.h"
+#include "mozilla/net/DNS.h"
+#include "prerror.h"
+#include "../../base/nsSocketTransportService2.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+
+using namespace mozilla::net;
+using namespace mozilla;
+
+class ServerListener : public nsIServerSocketListener {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISERVERSOCKETLISTENER
+
+ explicit ServerListener(WaitForCondition* waiter);
+
+ // Port that is got from server side will be store here.
+ uint32_t mClientPort;
+ bool mFailed;
+ RefPtr<WaitForCondition> mWaiter;
+
+ private:
+ virtual ~ServerListener();
+};
+
+NS_IMPL_ISUPPORTS(ServerListener, nsIServerSocketListener)
+
+ServerListener::ServerListener(WaitForCondition* waiter)
+ : mClientPort(-1), mFailed(false), mWaiter(waiter) {}
+
+ServerListener::~ServerListener() = default;
+
+NS_IMETHODIMP
+ServerListener::OnSocketAccepted(nsIServerSocket* aServ,
+ nsISocketTransport* aTransport) {
+ // Run on STS thread.
+ NetAddr peerAddr;
+ nsresult rv = aTransport->GetPeerAddr(&peerAddr);
+ if (NS_FAILED(rv)) {
+ mFailed = true;
+ mWaiter->Notify();
+ return NS_OK;
+ }
+ mClientPort = PR_ntohs(peerAddr.inet.port);
+ mWaiter->Notify();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ServerListener::OnStopListening(nsIServerSocket* aServ, nsresult aStatus) {
+ return NS_OK;
+}
+
+class ClientInputCallback : public nsIInputStreamCallback {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIINPUTSTREAMCALLBACK
+
+ explicit ClientInputCallback(WaitForCondition* waiter);
+
+ bool mFailed;
+ RefPtr<WaitForCondition> mWaiter;
+
+ private:
+ virtual ~ClientInputCallback();
+};
+
+NS_IMPL_ISUPPORTS(ClientInputCallback, nsIInputStreamCallback)
+
+ClientInputCallback::ClientInputCallback(WaitForCondition* waiter)
+ : mFailed(false), mWaiter(waiter) {}
+
+ClientInputCallback::~ClientInputCallback() = default;
+
+NS_IMETHODIMP
+ClientInputCallback::OnInputStreamReady(nsIAsyncInputStream* aStream) {
+ // Server doesn't send. That means if we are here, we probably have run into
+ // an error.
+ uint64_t avail;
+ nsresult rv = aStream->Available(&avail);
+ if (NS_FAILED(rv)) {
+ mFailed = true;
+ }
+ mWaiter->Notify();
+ return NS_OK;
+}
+
+TEST(TestBind, MainTest)
+{
+ //
+ // Server side.
+ //
+ nsCOMPtr<nsIServerSocket> server =
+ do_CreateInstance("@mozilla.org/network/server-socket;1");
+ ASSERT_TRUE(server);
+
+ nsresult rv = server->Init(-1, true, -1);
+ ASSERT_NS_SUCCEEDED(rv);
+
+ int32_t serverPort;
+ rv = server->GetPort(&serverPort);
+ ASSERT_NS_SUCCEEDED(rv);
+
+ RefPtr<WaitForCondition> waiter = new WaitForCondition();
+
+ // Listening.
+ RefPtr<ServerListener> serverListener = new ServerListener(waiter);
+ rv = server->AsyncListen(serverListener);
+ ASSERT_NS_SUCCEEDED(rv);
+
+ //
+ // Client side
+ //
+ uint32_t bindingPort = 20000;
+ nsCOMPtr<nsISocketTransportService> service =
+ do_GetService("@mozilla.org/network/socket-transport-service;1", &rv);
+ ASSERT_NS_SUCCEEDED(rv);
+
+ nsCOMPtr<nsIInputStream> inputStream;
+ RefPtr<ClientInputCallback> clientCallback;
+
+ auto* sts = gSocketTransportService;
+ ASSERT_TRUE(sts);
+ for (int32_t tried = 0; tried < 100; tried++) {
+ NS_DispatchAndSpinEventLoopUntilComplete(
+ "test"_ns, sts, NS_NewRunnableFunction("test", [&]() {
+ nsCOMPtr<nsISocketTransport> client;
+ rv = service->CreateTransport(nsTArray<nsCString>(), "127.0.0.1"_ns,
+ serverPort, nullptr, nullptr,
+ getter_AddRefs(client));
+ ASSERT_NS_SUCCEEDED(rv);
+
+ // Bind to a port. It's possible that we are binding to a port
+ // that is currently in use. If we failed to bind, we try next
+ // port.
+ NetAddr bindingAddr;
+ bindingAddr.inet.family = AF_INET;
+ bindingAddr.inet.ip = 0;
+ bindingAddr.inet.port = PR_htons(bindingPort);
+ rv = client->Bind(&bindingAddr);
+ ASSERT_NS_SUCCEEDED(rv);
+
+ // Open IO streams, to make client SocketTransport connect to
+ // server.
+ clientCallback = new ClientInputCallback(waiter);
+ rv = client->OpenInputStream(nsITransport::OPEN_UNBUFFERED, 0, 0,
+ getter_AddRefs(inputStream));
+ ASSERT_NS_SUCCEEDED(rv);
+
+ nsCOMPtr<nsIAsyncInputStream> asyncInputStream =
+ do_QueryInterface(inputStream);
+ rv = asyncInputStream->AsyncWait(clientCallback, 0, 0, nullptr);
+ }));
+
+ // Wait for server's response or callback of input stream.
+ waiter->Wait(1);
+ if (clientCallback->mFailed) {
+ // if client received error, we likely have bound a port that is
+ // in use. we can try another port.
+ bindingPort++;
+ } else {
+ // We are unlocked by server side, leave the loop and check
+ // result.
+ break;
+ }
+ }
+
+ ASSERT_FALSE(serverListener->mFailed);
+ ASSERT_EQ(serverListener->mClientPort, bindingPort);
+
+ inputStream->Close();
+ waiter->Wait(1);
+ ASSERT_TRUE(clientCallback->mFailed);
+
+ server->Close();
+}
diff --git a/netwerk/test/gtest/TestBufferedInputStream.cpp b/netwerk/test/gtest/TestBufferedInputStream.cpp
new file mode 100644
index 0000000000..7230fe7e5b
--- /dev/null
+++ b/netwerk/test/gtest/TestBufferedInputStream.cpp
@@ -0,0 +1,252 @@
+#include "gtest/gtest.h"
+
+#include "mozilla/SpinEventLoopUntil.h"
+#include "nsBufferedStreams.h"
+#include "nsIThread.h"
+#include "nsNetUtil.h"
+#include "nsStreamUtils.h"
+#include "nsThreadUtils.h"
+#include "Helpers.h"
+
+// Helper function for creating a testing::AsyncStringStream
+already_AddRefed<nsBufferedInputStream> CreateStream(uint32_t aSize,
+ nsCString& aBuffer) {
+ aBuffer.SetLength(aSize);
+ for (uint32_t i = 0; i < aSize; ++i) {
+ aBuffer.BeginWriting()[i] = i % 10;
+ }
+
+ nsCOMPtr<nsIInputStream> stream = new testing::AsyncStringStream(aBuffer);
+
+ RefPtr<nsBufferedInputStream> bis = new nsBufferedInputStream();
+ bis->Init(stream, aSize);
+ return bis.forget();
+}
+
+// Simple reading.
+TEST(TestBufferedInputStream, SimpleRead)
+{
+ const size_t kBufSize = 10;
+
+ nsCString buf;
+ RefPtr<nsBufferedInputStream> bis = CreateStream(kBufSize, buf);
+
+ uint64_t length;
+ ASSERT_EQ(NS_OK, bis->Available(&length));
+ ASSERT_EQ((uint64_t)kBufSize, length);
+
+ char buf2[kBufSize];
+ uint32_t count;
+ ASSERT_EQ(NS_OK, bis->Read(buf2, sizeof(buf2), &count));
+ ASSERT_EQ(count, buf.Length());
+ ASSERT_TRUE(nsCString(buf.get(), kBufSize).Equals(nsCString(buf2, count)));
+}
+
+// Simple segment reading.
+TEST(TestBufferedInputStream, SimpleReadSegments)
+{
+ const size_t kBufSize = 10;
+
+ nsCString buf;
+ RefPtr<nsBufferedInputStream> bis = CreateStream(kBufSize, buf);
+
+ char buf2[kBufSize];
+ uint32_t count;
+ ASSERT_EQ(NS_OK, bis->ReadSegments(NS_CopySegmentToBuffer, buf2, sizeof(buf2),
+ &count));
+ ASSERT_EQ(count, buf.Length());
+ ASSERT_TRUE(nsCString(buf.get(), kBufSize).Equals(nsCString(buf2, count)));
+}
+
+// AsyncWait - sync
+TEST(TestBufferedInputStream, AsyncWait_sync)
+{
+ const size_t kBufSize = 10;
+
+ nsCString buf;
+ RefPtr<nsBufferedInputStream> bis = CreateStream(kBufSize, buf);
+
+ RefPtr<testing::InputStreamCallback> cb = new testing::InputStreamCallback();
+
+ ASSERT_EQ(NS_OK, bis->AsyncWait(cb, 0, 0, nullptr));
+
+ // Immediatelly called
+ ASSERT_TRUE(cb->Called());
+}
+
+// AsyncWait - async
+TEST(TestBufferedInputStream, AsyncWait_async)
+{
+ const size_t kBufSize = 10;
+
+ nsCString buf;
+ RefPtr<nsBufferedInputStream> bis = CreateStream(kBufSize, buf);
+
+ RefPtr<testing::InputStreamCallback> cb = new testing::InputStreamCallback();
+ nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
+
+ ASSERT_EQ(NS_OK, bis->AsyncWait(cb, 0, 0, thread));
+
+ ASSERT_FALSE(cb->Called());
+
+ // Eventually it is called.
+ MOZ_ALWAYS_TRUE(mozilla::SpinEventLoopUntil(
+ "TEST(TestBufferedInputStream, AsyncWait_async)"_ns,
+ [&]() { return cb->Called(); }));
+ ASSERT_TRUE(cb->Called());
+}
+
+// AsyncWait - sync - closureOnly
+TEST(TestBufferedInputStream, AsyncWait_sync_closureOnly)
+{
+ const size_t kBufSize = 10;
+
+ nsCString buf;
+ RefPtr<nsBufferedInputStream> bis = CreateStream(kBufSize, buf);
+
+ RefPtr<testing::InputStreamCallback> cb = new testing::InputStreamCallback();
+
+ ASSERT_EQ(NS_OK, bis->AsyncWait(cb, nsIAsyncInputStream::WAIT_CLOSURE_ONLY, 0,
+ nullptr));
+ ASSERT_FALSE(cb->Called());
+
+ bis->CloseWithStatus(NS_ERROR_FAILURE);
+
+ // Immediatelly called
+ ASSERT_TRUE(cb->Called());
+}
+
+// AsyncWait - async
+TEST(TestBufferedInputStream, AsyncWait_async_closureOnly)
+{
+ const size_t kBufSize = 10;
+
+ nsCString buf;
+ RefPtr<nsBufferedInputStream> bis = CreateStream(kBufSize, buf);
+
+ RefPtr<testing::InputStreamCallback> cb = new testing::InputStreamCallback();
+ nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
+
+ ASSERT_EQ(NS_OK, bis->AsyncWait(cb, nsIAsyncInputStream::WAIT_CLOSURE_ONLY, 0,
+ thread));
+
+ ASSERT_FALSE(cb->Called());
+ bis->CloseWithStatus(NS_ERROR_FAILURE);
+ ASSERT_FALSE(cb->Called());
+
+ // Eventually it is called.
+ MOZ_ALWAYS_TRUE(mozilla::SpinEventLoopUntil(
+ "TEST(TestBufferedInputStream, AsyncWait_async_closureOnly)"_ns,
+ [&]() { return cb->Called(); }));
+ ASSERT_TRUE(cb->Called());
+}
+
+TEST(TestBufferedInputStream, AsyncWait_after_close)
+{
+ const size_t kBufSize = 10;
+
+ nsCString buf;
+ RefPtr<nsBufferedInputStream> bis = CreateStream(kBufSize, buf);
+
+ nsCOMPtr<nsIThread> eventTarget = do_GetCurrentThread();
+
+ auto cb = mozilla::MakeRefPtr<testing::InputStreamCallback>();
+ ASSERT_EQ(NS_OK, bis->AsyncWait(cb, 0, 0, eventTarget));
+ MOZ_ALWAYS_TRUE(mozilla::SpinEventLoopUntil(
+ "TEST(TestBufferedInputStream, AsyncWait_after_close) 1"_ns,
+ [&]() { return cb->Called(); }));
+ ASSERT_TRUE(cb->Called());
+
+ ASSERT_EQ(NS_OK, bis->Close());
+
+ cb = mozilla::MakeRefPtr<testing::InputStreamCallback>();
+ ASSERT_EQ(NS_OK, bis->AsyncWait(cb, 0, 0, eventTarget));
+ MOZ_ALWAYS_TRUE(mozilla::SpinEventLoopUntil(
+ "TEST(TestBufferedInputStream, AsyncWait_after_close) 2"_ns,
+ [&]() { return cb->Called(); }));
+ ASSERT_TRUE(cb->Called());
+}
+
+TEST(TestBufferedInputStream, AsyncLengthWait_after_close)
+{
+ nsCString buf{"The Quick Brown Fox Jumps over the Lazy Dog"};
+ const size_t kBufSize = 44;
+
+ RefPtr<nsBufferedInputStream> bis = CreateStream(kBufSize, buf);
+
+ nsCOMPtr<nsIThread> eventTarget = do_GetCurrentThread();
+
+ auto cb = mozilla::MakeRefPtr<testing::LengthCallback>();
+ ASSERT_EQ(NS_OK, bis->AsyncLengthWait(cb, eventTarget));
+ MOZ_ALWAYS_TRUE(mozilla::SpinEventLoopUntil(
+ "TEST(TestBufferedInputStream, AsyncLengthWait_after_close) 1"_ns,
+ [&]() { return cb->Called(); }));
+ ASSERT_TRUE(cb->Called());
+
+ uint64_t length;
+ ASSERT_EQ(NS_OK, bis->Available(&length));
+ ASSERT_EQ((uint64_t)kBufSize, length);
+
+ cb = mozilla::MakeRefPtr<testing::LengthCallback>();
+ ASSERT_EQ(NS_OK, bis->AsyncLengthWait(cb, eventTarget));
+ MOZ_ALWAYS_TRUE(mozilla::SpinEventLoopUntil(
+ "TEST(TestBufferedInputStream, AsyncLengthWait_after_close) 2"_ns,
+ [&]() { return cb->Called(); }));
+ ASSERT_TRUE(cb->Called());
+}
+
+// This stream returns a few bytes on the first read, and error on the second.
+class BrokenInputStream : public nsIInputStream {
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIINPUTSTREAM
+ private:
+ virtual ~BrokenInputStream() = default;
+ bool mFirst = true;
+};
+
+NS_IMPL_ISUPPORTS(BrokenInputStream, nsIInputStream)
+
+NS_IMETHODIMP BrokenInputStream::Close(void) { return NS_OK; }
+
+NS_IMETHODIMP BrokenInputStream::Available(uint64_t* _retval) {
+ *_retval = 100;
+ return NS_OK;
+}
+
+NS_IMETHODIMP BrokenInputStream::StreamStatus(void) { return NS_OK; }
+
+NS_IMETHODIMP BrokenInputStream::Read(char* aBuf, uint32_t aCount,
+ uint32_t* _retval) {
+ if (mFirst) {
+ aBuf[0] = 'h';
+ aBuf[1] = 'e';
+ aBuf[2] = 'l';
+ aBuf[3] = 0;
+ *_retval = 4;
+ mFirst = false;
+ return NS_OK;
+ }
+ return NS_ERROR_CORRUPTED_CONTENT;
+}
+
+NS_IMETHODIMP BrokenInputStream::ReadSegments(nsWriteSegmentFun aWriter,
+ void* aClosure, uint32_t aCount,
+ uint32_t* _retval) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP BrokenInputStream::IsNonBlocking(bool* _retval) {
+ *_retval = false;
+ return NS_OK;
+}
+
+// Check that the error from BrokenInputStream::Read is propagated
+// through NS_ReadInputStreamToString
+TEST(TestBufferedInputStream, BrokenInputStreamToBuffer)
+{
+ nsAutoCString out;
+ RefPtr<BrokenInputStream> stream = new BrokenInputStream();
+
+ nsresult rv = NS_ReadInputStreamToString(stream, out, -1);
+ ASSERT_EQ(rv, NS_ERROR_CORRUPTED_CONTENT);
+}
diff --git a/netwerk/test/gtest/TestCommon.cpp b/netwerk/test/gtest/TestCommon.cpp
new file mode 100644
index 0000000000..37c08fbed8
--- /dev/null
+++ b/netwerk/test/gtest/TestCommon.cpp
@@ -0,0 +1,7 @@
+/* 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 "TestCommon.h"
+
+NS_IMPL_ISUPPORTS(WaitForCondition, nsIRunnable)
diff --git a/netwerk/test/gtest/TestCommon.h b/netwerk/test/gtest/TestCommon.h
new file mode 100644
index 0000000000..0d2fd74e5b
--- /dev/null
+++ b/netwerk/test/gtest/TestCommon.h
@@ -0,0 +1,45 @@
+/* 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/. */
+
+#ifndef TestCommon_h__
+#define TestCommon_h__
+
+#include <stdlib.h>
+#include "nsThreadUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/SpinEventLoopUntil.h"
+
+//-----------------------------------------------------------------------------
+
+class WaitForCondition final : public nsIRunnable {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ void Wait(int pending) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mPending == 0);
+
+ mPending = pending;
+ mozilla::SpinEventLoopUntil("TestCommon.h:WaitForCondition::Wait"_ns,
+ [&]() { return !mPending; });
+ NS_ProcessPendingEvents(nullptr);
+ }
+
+ void Notify() { NS_DispatchToMainThread(this); }
+
+ private:
+ virtual ~WaitForCondition() = default;
+
+ NS_IMETHOD Run() override {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mPending);
+
+ --mPending;
+ return NS_OK;
+ }
+
+ uint32_t mPending = 0;
+};
+
+#endif
diff --git a/netwerk/test/gtest/TestCookie.cpp b/netwerk/test/gtest/TestCookie.cpp
new file mode 100644
index 0000000000..4812ee47f1
--- /dev/null
+++ b/netwerk/test/gtest/TestCookie.cpp
@@ -0,0 +1,1126 @@
+/* -*- 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 "TestCommon.h"
+#include "gtest/gtest.h"
+#include "nsContentUtils.h"
+#include "nsICookieService.h"
+#include "nsICookieManager.h"
+#include "nsICookie.h"
+#include <stdio.h>
+#include "plstr.h"
+#include "nsNetUtil.h"
+#include "nsIChannel.h"
+#include "nsIPrincipal.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsServiceManagerUtils.h"
+#include "nsNetCID.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefService.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/gtest/MozAssertions.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Unused.h"
+#include "mozilla/net/CookieJarSettings.h"
+#include "Cookie.h"
+#include "nsIURI.h"
+
+using namespace mozilla;
+using namespace mozilla::net;
+
+static NS_DEFINE_CID(kCookieServiceCID, NS_COOKIESERVICE_CID);
+static NS_DEFINE_CID(kPrefServiceCID, NS_PREFSERVICE_CID);
+
+// various pref strings
+static const char kCookiesPermissions[] = "network.cookie.cookieBehavior";
+static const char kPrefCookieQuotaPerHost[] = "network.cookie.quotaPerHost";
+static const char kCookiesMaxPerHost[] = "network.cookie.maxPerHost";
+
+#define OFFSET_ONE_WEEK int64_t(604800) * PR_USEC_PER_SEC
+#define OFFSET_ONE_DAY int64_t(86400) * PR_USEC_PER_SEC
+
+// Set server time or expiry time
+void SetTime(PRTime offsetTime, nsAutoCString& serverString,
+ nsAutoCString& cookieString, bool expiry) {
+ char timeStringPreset[40];
+ PRTime CurrentTime = PR_Now();
+ PRTime SetCookieTime = CurrentTime + offsetTime;
+ PRTime SetExpiryTime;
+ if (expiry) {
+ SetExpiryTime = SetCookieTime - OFFSET_ONE_DAY;
+ } else {
+ SetExpiryTime = SetCookieTime + OFFSET_ONE_DAY;
+ }
+
+ // Set server time string
+ PRExplodedTime explodedTime;
+ PR_ExplodeTime(SetCookieTime, PR_GMTParameters, &explodedTime);
+ PR_FormatTimeUSEnglish(timeStringPreset, 40, "%c GMT", &explodedTime);
+ serverString.Assign(timeStringPreset);
+
+ // Set cookie string
+ PR_ExplodeTime(SetExpiryTime, PR_GMTParameters, &explodedTime);
+ PR_FormatTimeUSEnglish(timeStringPreset, 40, "%c GMT", &explodedTime);
+ cookieString.ReplaceLiteral(
+ 0, strlen("test=expiry; expires=") + strlen(timeStringPreset) + 1,
+ "test=expiry; expires=");
+ cookieString.Append(timeStringPreset);
+}
+
+void SetACookieInternal(nsICookieService* aCookieService, const char* aSpec,
+ const char* aCookieString, bool aAllowed) {
+ nsCOMPtr<nsIURI> uri;
+ NS_NewURI(getter_AddRefs(uri), aSpec);
+
+ // We create a dummy channel using the aSpec to simulate same-siteness
+ nsresult rv0;
+ nsCOMPtr<nsIScriptSecurityManager> ssm =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv0);
+ ASSERT_NS_SUCCEEDED(rv0);
+ nsCOMPtr<nsIPrincipal> specPrincipal;
+ nsCString tmpString(aSpec);
+ ssm->CreateContentPrincipalFromOrigin(tmpString,
+ getter_AddRefs(specPrincipal));
+
+ nsCOMPtr<nsIChannel> dummyChannel;
+ NS_NewChannel(getter_AddRefs(dummyChannel), uri, specPrincipal,
+ nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
+ nsIContentPolicy::TYPE_OTHER);
+
+ nsCOMPtr<nsICookieJarSettings> cookieJarSettings =
+ aAllowed
+ ? CookieJarSettings::Create(CookieJarSettings::eRegular,
+ /* shouldResistFingerprinting */ false)
+ : CookieJarSettings::GetBlockingAll(
+ /* shouldResistFingerprinting */ false);
+ MOZ_ASSERT(cookieJarSettings);
+
+ nsCOMPtr<nsILoadInfo> loadInfo = dummyChannel->LoadInfo();
+ loadInfo->SetCookieJarSettings(cookieJarSettings);
+
+ nsresult rv = aCookieService->SetCookieStringFromHttp(
+ uri, nsDependentCString(aCookieString), dummyChannel);
+ EXPECT_NS_SUCCEEDED(rv);
+}
+
+void SetACookieJarBlocked(nsICookieService* aCookieService, const char* aSpec,
+ const char* aCookieString) {
+ SetACookieInternal(aCookieService, aSpec, aCookieString, false);
+}
+
+void SetACookie(nsICookieService* aCookieService, const char* aSpec,
+ const char* aCookieString) {
+ SetACookieInternal(aCookieService, aSpec, aCookieString, true);
+}
+
+// The cookie string is returned via aCookie.
+void GetACookie(nsICookieService* aCookieService, const char* aSpec,
+ nsACString& aCookie) {
+ nsCOMPtr<nsIURI> uri;
+ NS_NewURI(getter_AddRefs(uri), aSpec);
+
+ nsCOMPtr<nsIIOService> service = do_GetIOService();
+
+ nsCOMPtr<nsIChannel> channel;
+ Unused << service->NewChannelFromURI(
+ uri, nullptr, nsContentUtils::GetSystemPrincipal(),
+ nsContentUtils::GetSystemPrincipal(), 0, nsIContentPolicy::TYPE_DOCUMENT,
+ getter_AddRefs(channel));
+
+ Unused << aCookieService->GetCookieStringFromHttp(uri, channel, aCookie);
+}
+
+// The cookie string is returned via aCookie.
+void GetACookieNoHttp(nsICookieService* aCookieService, const char* aSpec,
+ nsACString& aCookie) {
+ nsCOMPtr<nsIURI> uri;
+ NS_NewURI(getter_AddRefs(uri), aSpec);
+
+ RefPtr<BasePrincipal> principal =
+ BasePrincipal::CreateContentPrincipal(uri, OriginAttributes());
+ MOZ_ASSERT(principal);
+
+ nsCOMPtr<mozilla::dom::Document> document;
+ nsresult rv = NS_NewDOMDocument(getter_AddRefs(document),
+ u""_ns, // aNamespaceURI
+ u""_ns, // aQualifiedName
+ nullptr, // aDoctype
+ uri, uri, principal,
+ false, // aLoadedAsData
+ nullptr, // aEventObject
+ DocumentFlavorHTML);
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+
+ Unused << aCookieService->GetCookieStringFromDocument(document, aCookie);
+}
+
+// some #defines for comparison rules
+#define MUST_BE_NULL 0
+#define MUST_EQUAL 1
+#define MUST_CONTAIN 2
+#define MUST_NOT_CONTAIN 3
+#define MUST_NOT_EQUAL 4
+
+// a simple helper function to improve readability:
+// takes one of the #defined rules above, and performs the appropriate test.
+// true means the test passed; false means the test failed.
+static inline bool CheckResult(const char* aLhs, uint32_t aRule,
+ const char* aRhs = nullptr) {
+ switch (aRule) {
+ case MUST_BE_NULL:
+ return !aLhs || !*aLhs;
+
+ case MUST_EQUAL:
+ return !PL_strcmp(aLhs, aRhs);
+
+ case MUST_NOT_EQUAL:
+ return PL_strcmp(aLhs, aRhs);
+
+ case MUST_CONTAIN:
+ return strstr(aLhs, aRhs) != nullptr;
+
+ case MUST_NOT_CONTAIN:
+ return strstr(aLhs, aRhs) == nullptr;
+
+ default:
+ return false; // failure
+ }
+}
+
+void InitPrefs(nsIPrefBranch* aPrefBranch) {
+ // init some relevant prefs, so the tests don't go awry.
+ // we use the most restrictive set of prefs we can;
+ // however, we don't test third party blocking here.
+ aPrefBranch->SetIntPref(kCookiesPermissions, 0); // accept all
+ // Set quotaPerHost to maxPerHost - 1, so there is only one cookie
+ // will be evicted everytime.
+ aPrefBranch->SetIntPref(kPrefCookieQuotaPerHost, 49);
+ // Set the base domain limit to 50 so we have a known value.
+ aPrefBranch->SetIntPref(kCookiesMaxPerHost, 50);
+
+ // SameSite=None by default. We have other tests for lax-by-default.
+ // XXX: Bug 1617611 - Fix all the tests broken by "cookies SameSite=Lax by
+ // default"
+ Preferences::SetBool("network.cookie.sameSite.laxByDefault", false);
+ Preferences::SetBool("network.cookieJarSettings.unblocked_for_testing", true);
+ Preferences::SetBool("dom.securecontext.allowlist_onions", false);
+ Preferences::SetBool("network.cookie.sameSite.schemeful", false);
+}
+
+TEST(TestCookie, TestCookieMain)
+{
+ nsresult rv0;
+
+ nsCOMPtr<nsICookieService> cookieService =
+ do_GetService(kCookieServiceCID, &rv0);
+ ASSERT_NS_SUCCEEDED(rv0);
+
+ nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(kPrefServiceCID, &rv0);
+ ASSERT_NS_SUCCEEDED(rv0);
+
+ InitPrefs(prefBranch);
+
+ nsCString cookie;
+
+ /* The basic idea behind these tests is the following:
+ *
+ * we set() some cookie, then try to get() it in various ways. we have
+ * several possible tests we perform on the cookie string returned from
+ * get():
+ *
+ * a) check whether the returned string is null (i.e. we got no cookies
+ * back). this is used e.g. to ensure a given cookie was deleted
+ * correctly, or to ensure a certain cookie wasn't returned to a given
+ * host.
+ * b) check whether the returned string exactly matches a given string.
+ * this is used where we want to make sure our cookie service adheres to
+ * some strict spec (e.g. ordering of multiple cookies), or where we
+ * just know exactly what the returned string should be.
+ * c) check whether the returned string contains/does not contain a given
+ * string. this is used where we don't know/don't care about the
+ * ordering of multiple cookies - we just want to make sure the cookie
+ * string contains them all, in some order.
+ *
+ * NOTE: this testsuite is not yet comprehensive or complete, and is
+ * somewhat contrived - still under development, and needs improving!
+ */
+
+ // test some basic variations of the domain & path
+ SetACookie(cookieService, "http://www.basic.com", "test=basic");
+ GetACookie(cookieService, "http://www.basic.com", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=basic"));
+ GetACookie(cookieService, "http://www.basic.com/testPath/testfile.txt",
+ cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=basic"));
+ GetACookie(cookieService, "http://www.basic.com./", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+ GetACookie(cookieService, "http://www.basic.com.", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+ GetACookie(cookieService, "http://www.basic.com./testPath/testfile.txt",
+ cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+ GetACookie(cookieService, "http://www.basic2.com/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+ SetACookie(cookieService, "http://www.basic.com", "test=basic; max-age=-1");
+ GetACookie(cookieService, "http://www.basic.com/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ // *** domain tests
+
+ // test some variations of the domain & path, for different domains of
+ // a domain cookie
+ SetACookie(cookieService, "http://www.domain.com",
+ "test=domain; domain=domain.com");
+ GetACookie(cookieService, "http://domain.com", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=domain"));
+ GetACookie(cookieService, "http://domain.com.", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+ GetACookie(cookieService, "http://www.domain.com", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=domain"));
+ GetACookie(cookieService, "http://foo.domain.com", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=domain"));
+ SetACookie(cookieService, "http://www.domain.com",
+ "test=domain; domain=domain.com; max-age=-1");
+ GetACookie(cookieService, "http://domain.com", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ SetACookie(cookieService, "http://www.domain.com",
+ "test=domain; domain=.domain.com");
+ GetACookie(cookieService, "http://domain.com", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=domain"));
+ GetACookie(cookieService, "http://www.domain.com", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=domain"));
+ GetACookie(cookieService, "http://bah.domain.com", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=domain"));
+ SetACookie(cookieService, "http://www.domain.com",
+ "test=domain; domain=.domain.com; max-age=-1");
+ GetACookie(cookieService, "http://domain.com", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ SetACookie(cookieService, "http://www.domain.com",
+ "test=domain; domain=.foo.domain.com");
+ GetACookie(cookieService, "http://foo.domain.com", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ SetACookie(cookieService, "http://www.domain.com",
+ "test=domain; domain=moose.com");
+ GetACookie(cookieService, "http://foo.domain.com", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ SetACookie(cookieService, "http://www.domain.com",
+ "test=domain; domain=domain.com.");
+ GetACookie(cookieService, "http://foo.domain.com", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ SetACookie(cookieService, "http://www.domain.com",
+ "test=domain; domain=..domain.com");
+ GetACookie(cookieService, "http://foo.domain.com", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ SetACookie(cookieService, "http://www.domain.com",
+ "test=domain; domain=..domain.com.");
+ GetACookie(cookieService, "http://foo.domain.com", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ SetACookie(cookieService, "http://path.net/path/file",
+ R"(test=taco; path="/bogus")");
+ GetACookie(cookieService, "http://path.net/path/file", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=taco"));
+ SetACookie(cookieService, "http://path.net/path/file",
+ "test=taco; max-age=-1");
+ GetACookie(cookieService, "http://path.net/path/file", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ // *** path tests
+
+ // test some variations of the domain & path, for different paths of
+ // a path cookie
+ SetACookie(cookieService, "http://path.net/path/file",
+ "test=path; path=/path");
+ GetACookie(cookieService, "http://path.net/path", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=path"));
+ GetACookie(cookieService, "http://path.net/path/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=path"));
+ GetACookie(cookieService, "http://path.net/path/hithere.foo", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=path"));
+ GetACookie(cookieService, "http://path.net/path?hithere/foo", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=path"));
+ GetACookie(cookieService, "http://path.net/path2", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+ GetACookie(cookieService, "http://path.net/path2/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+ SetACookie(cookieService, "http://path.net/path/file",
+ "test=path; path=/path; max-age=-1");
+ GetACookie(cookieService, "http://path.net/path/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ SetACookie(cookieService, "http://path.net/path/file",
+ "test=path; path=/path/");
+ GetACookie(cookieService, "http://path.net/path", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+ GetACookie(cookieService, "http://path.net/path/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=path"));
+ SetACookie(cookieService, "http://path.net/path/file",
+ "test=path; path=/path/; max-age=-1");
+ GetACookie(cookieService, "http://path.net/path/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ // note that a site can set a cookie for a path it's not on.
+ // this is an intentional deviation from spec (see comments in
+ // CookieService::CheckPath()), so we test this functionality too
+ SetACookie(cookieService, "http://path.net/path/file",
+ "test=path; path=/foo/");
+ GetACookie(cookieService, "http://path.net/path", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+ GetACookie(cookieService, "http://path.net/foo", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+ SetACookie(cookieService, "http://path.net/path/file",
+ "test=path; path=/foo/; max-age=-1");
+ GetACookie(cookieService, "http://path.net/foo/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ // bug 373228: make sure cookies with paths longer than 1024 bytes,
+ // and cookies with paths or names containing tabs, are rejected.
+ // the following cookie has a path > 1024 bytes explicitly specified in the
+ // cookie
+ SetACookie(
+ cookieService, "http://path.net/",
+ "test=path; "
+ "path=/"
+ "123456789012345678901234567890123456789012345678901234567890123456789012"
+ "345678901234567890123456789012345678901234567890123456789012345678901234"
+ "567890123456789012345678901234567890123456789012345678901234567890123456"
+ "789012345678901234567890123456789012345678901234567890123456789012345678"
+ "901234567890123456789012345678901234567890123456789012345678901234567890"
+ "123456789012345678901234567890123456789012345678901234567890123456789012"
+ "345678901234567890123456789012345678901234567890123456789012345678901234"
+ "567890123456789012345678901234567890123456789012345678901234567890123456"
+ "789012345678901234567890123456789012345678901234567890123456789012345678"
+ "901234567890123456789012345678901234567890123456789012345678901234567890"
+ "123456789012345678901234567890123456789012345678901234567890123456789012"
+ "345678901234567890123456789012345678901234567890123456789012345678901234"
+ "567890123456789012345678901234567890123456789012345678901234567890123456"
+ "789012345678901234567890123456789012345678901234567890123456789012345678"
+ "9012345678901234567890/");
+ GetACookie(
+ cookieService,
+ "http://path.net/"
+ "123456789012345678901234567890123456789012345678901234567890123456789012"
+ "345678901234567890123456789012345678901234567890123456789012345678901234"
+ "567890123456789012345678901234567890123456789012345678901234567890123456"
+ "789012345678901234567890123456789012345678901234567890123456789012345678"
+ "901234567890123456789012345678901234567890123456789012345678901234567890"
+ "123456789012345678901234567890123456789012345678901234567890123456789012"
+ "345678901234567890123456789012345678901234567890123456789012345678901234"
+ "567890123456789012345678901234567890123456789012345678901234567890123456"
+ "789012345678901234567890123456789012345678901234567890123456789012345678"
+ "901234567890123456789012345678901234567890123456789012345678901234567890"
+ "123456789012345678901234567890123456789012345678901234567890123456789012"
+ "345678901234567890123456789012345678901234567890123456789012345678901234"
+ "567890123456789012345678901234567890123456789012345678901234567890123456"
+ "789012345678901234567890123456789012345678901234567890123456789012345678"
+ "9012345678901234567890",
+ cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+ // the following cookie has a path > 1024 bytes implicitly specified by the
+ // uri path
+ SetACookie(
+ cookieService,
+ "http://path.net/"
+ "123456789012345678901234567890123456789012345678901234567890123456789012"
+ "345678901234567890123456789012345678901234567890123456789012345678901234"
+ "567890123456789012345678901234567890123456789012345678901234567890123456"
+ "789012345678901234567890123456789012345678901234567890123456789012345678"
+ "901234567890123456789012345678901234567890123456789012345678901234567890"
+ "123456789012345678901234567890123456789012345678901234567890123456789012"
+ "345678901234567890123456789012345678901234567890123456789012345678901234"
+ "567890123456789012345678901234567890123456789012345678901234567890123456"
+ "789012345678901234567890123456789012345678901234567890123456789012345678"
+ "901234567890123456789012345678901234567890123456789012345678901234567890"
+ "123456789012345678901234567890123456789012345678901234567890123456789012"
+ "345678901234567890123456789012345678901234567890123456789012345678901234"
+ "567890123456789012345678901234567890123456789012345678901234567890123456"
+ "789012345678901234567890123456789012345678901234567890123456789012345678"
+ "9012345678901234567890/",
+ "test=path");
+ GetACookie(
+ cookieService,
+ "http://path.net/"
+ "123456789012345678901234567890123456789012345678901234567890123456789012"
+ "345678901234567890123456789012345678901234567890123456789012345678901234"
+ "567890123456789012345678901234567890123456789012345678901234567890123456"
+ "789012345678901234567890123456789012345678901234567890123456789012345678"
+ "901234567890123456789012345678901234567890123456789012345678901234567890"
+ "123456789012345678901234567890123456789012345678901234567890123456789012"
+ "345678901234567890123456789012345678901234567890123456789012345678901234"
+ "567890123456789012345678901234567890123456789012345678901234567890123456"
+ "789012345678901234567890123456789012345678901234567890123456789012345678"
+ "901234567890123456789012345678901234567890123456789012345678901234567890"
+ "123456789012345678901234567890123456789012345678901234567890123456789012"
+ "345678901234567890123456789012345678901234567890123456789012345678901234"
+ "567890123456789012345678901234567890123456789012345678901234567890123456"
+ "789012345678901234567890123456789012345678901234567890123456789012345678"
+ "9012345678901234567890/",
+ cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+ // the following cookie includes a tab in the path
+ SetACookie(cookieService, "http://path.net/", "test=path; path=/foo\tbar/");
+ GetACookie(cookieService, "http://path.net/foo\tbar/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+ // the following cookie includes a tab in the name
+ SetACookie(cookieService, "http://path.net/", "test\ttabs=tab");
+ GetACookie(cookieService, "http://path.net/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+ // the following cookie includes a tab in the value - allowed
+ SetACookie(cookieService, "http://path.net/", "test=tab\ttest");
+ GetACookie(cookieService, "http://path.net/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=tab\ttest"));
+ SetACookie(cookieService, "http://path.net/", "test=tab\ttest; max-age=-1");
+ GetACookie(cookieService, "http://path.net/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ // *** expiry & deletion tests
+ // XXX add server time str parsing tests here
+
+ // test some variations of the expiry time,
+ // and test deletion of previously set cookies
+ SetACookie(cookieService, "http://expireme.org/", "test=expiry; max-age=-1");
+ GetACookie(cookieService, "http://expireme.org/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+ SetACookie(cookieService, "http://expireme.org/", "test=expiry; max-age=0");
+ GetACookie(cookieService, "http://expireme.org/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+ SetACookie(cookieService, "http://expireme.org/", "test=expiry; expires=bad");
+ GetACookie(cookieService, "http://expireme.org/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=expiry"));
+ SetACookie(cookieService, "http://expireme.org/",
+ "test=expiry; expires=Thu, 10 Apr 1980 16:33:12 GMT");
+ GetACookie(cookieService, "http://expireme.org/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+ SetACookie(cookieService, "http://expireme.org/",
+ R"(test=expiry; expires="Thu, 10 Apr 1980 16:33:12 GMT)");
+ GetACookie(cookieService, "http://expireme.org/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+ SetACookie(cookieService, "http://expireme.org/",
+ R"(test=expiry; expires="Thu, 10 Apr 1980 16:33:12 GMT")");
+ GetACookie(cookieService, "http://expireme.org/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ SetACookie(cookieService, "http://expireme.org/", "test=expiry; max-age=60");
+ GetACookie(cookieService, "http://expireme.org/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=expiry"));
+ SetACookie(cookieService, "http://expireme.org/", "test=expiry; max-age=-20");
+ GetACookie(cookieService, "http://expireme.org/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+ SetACookie(cookieService, "http://expireme.org/", "test=expiry; max-age=60");
+ GetACookie(cookieService, "http://expireme.org/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=expiry"));
+ SetACookie(cookieService, "http://expireme.org/",
+ "test=expiry; expires=Thu, 10 Apr 1980 16:33:12 GMT");
+ GetACookie(cookieService, "http://expireme.org/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+ SetACookie(cookieService, "http://expireme.org/", "test=expiry; max-age=60");
+ SetACookie(cookieService, "http://expireme.org/",
+ "newtest=expiry; max-age=60");
+ GetACookie(cookieService, "http://expireme.org/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_CONTAIN, "test=expiry"));
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_CONTAIN, "newtest=expiry"));
+ SetACookie(cookieService, "http://expireme.org/",
+ "test=differentvalue; max-age=0");
+ GetACookie(cookieService, "http://expireme.org/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "newtest=expiry"));
+ SetACookie(cookieService, "http://expireme.org/",
+ "newtest=evendifferentvalue; max-age=0");
+ GetACookie(cookieService, "http://expireme.org/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ SetACookie(cookieService, "http://foo.expireme.org/",
+ "test=expiry; domain=.expireme.org; max-age=60");
+ GetACookie(cookieService, "http://expireme.org/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=expiry"));
+ SetACookie(cookieService, "http://bar.expireme.org/",
+ "test=differentvalue; domain=.expireme.org; max-age=0");
+ GetACookie(cookieService, "http://expireme.org/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ nsAutoCString ServerTime;
+ nsAutoCString CookieString;
+
+ // *** multiple cookie tests
+
+ // test the setting of multiple cookies, and test the order of precedence
+ // (a later cookie overwriting an earlier one, in the same header string)
+ SetACookie(cookieService, "http://multiple.cookies/",
+ "test=multiple; domain=.multiple.cookies \n test=different \n "
+ "test=same; domain=.multiple.cookies \n newtest=ciao \n "
+ "newtest=foo; max-age=-6 \n newtest=reincarnated");
+ GetACookie(cookieService, "http://multiple.cookies/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_NOT_CONTAIN, "test=multiple"));
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_CONTAIN, "test=different"));
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_CONTAIN, "test=same"));
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_NOT_CONTAIN, "newtest=ciao"));
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_NOT_CONTAIN, "newtest=foo"));
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_CONTAIN, "newtest=reincarnated"));
+ SetACookie(cookieService, "http://multiple.cookies/",
+ "test=expiry; domain=.multiple.cookies; max-age=0");
+ GetACookie(cookieService, "http://multiple.cookies/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_NOT_CONTAIN, "test=same"));
+ SetACookie(cookieService, "http://multiple.cookies/",
+ "\n test=different; max-age=0 \n");
+ GetACookie(cookieService, "http://multiple.cookies/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_NOT_CONTAIN, "test=different"));
+ SetACookie(cookieService, "http://multiple.cookies/",
+ "newtest=dead; max-age=0");
+ GetACookie(cookieService, "http://multiple.cookies/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ // *** parser tests
+
+ // test the cookie header parser, under various circumstances.
+ SetACookie(cookieService, "http://parser.test/",
+ "test=parser; domain=.parser.test; ;; ;=; ,,, ===,abc,=; "
+ "abracadabra! max-age=20;=;;");
+ GetACookie(cookieService, "http://parser.test/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=parser"));
+ SetACookie(cookieService, "http://parser.test/",
+ "test=parser; domain=.parser.test; max-age=0");
+ GetACookie(cookieService, "http://parser.test/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+ SetACookie(cookieService, "http://parser.test/",
+ "test=\"fubar! = foo;bar\\\";\" parser; domain=.parser.test; "
+ "max-age=6\nfive; max-age=2.63,");
+ GetACookie(cookieService, "http://parser.test/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_CONTAIN, R"(test="fubar! = foo)"));
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_CONTAIN, "five"));
+ SetACookie(cookieService, "http://parser.test/",
+ "test=kill; domain=.parser.test; max-age=0 \n five; max-age=0");
+ GetACookie(cookieService, "http://parser.test/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ // test the handling of VALUE-only cookies (see bug 169091),
+ // i.e. "six" should assume an empty NAME, which allows other VALUE-only
+ // cookies to overwrite it
+ SetACookie(cookieService, "http://parser.test/", "six");
+ GetACookie(cookieService, "http://parser.test/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "six"));
+ SetACookie(cookieService, "http://parser.test/", "seven");
+ GetACookie(cookieService, "http://parser.test/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "seven"));
+ SetACookie(cookieService, "http://parser.test/", " =eight");
+ GetACookie(cookieService, "http://parser.test/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "eight"));
+ SetACookie(cookieService, "http://parser.test/", "test=six");
+ GetACookie(cookieService, "http://parser.test/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_CONTAIN, "test=six"));
+
+ // *** path ordering tests
+
+ // test that cookies are returned in path order - longest to shortest.
+ // if the header doesn't specify a path, it's taken from the host URI.
+ SetACookie(cookieService, "http://multi.path.tests/",
+ "test1=path; path=/one/two/three");
+ SetACookie(cookieService, "http://multi.path.tests/",
+ "test2=path; path=/one \n test3=path; path=/one/two/three/four \n "
+ "test4=path; path=/one/two \n test5=path; path=/one/two/");
+ SetACookie(cookieService, "http://multi.path.tests/one/two/three/four/five/",
+ "test6=path");
+ SetACookie(cookieService,
+ "http://multi.path.tests/one/two/three/four/five/six/",
+ "test7=path; path=");
+ SetACookie(cookieService, "http://multi.path.tests/", "test8=path; path=/");
+ GetACookie(cookieService,
+ "http://multi.path.tests/one/two/three/four/five/six/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL,
+ "test7=path; test6=path; test3=path; test1=path; "
+ "test5=path; test4=path; test2=path; test8=path"));
+
+ // *** Cookie prefix tests
+
+ // prefixed cookies can't be set from insecure HTTP
+ SetACookie(cookieService, "http://prefixed.test/", "__Secure-test1=test");
+ SetACookie(cookieService, "http://prefixed.test/",
+ "__Secure-test2=test; secure");
+ SetACookie(cookieService, "http://prefixed.test/", "__Host-test1=test");
+ SetACookie(cookieService, "http://prefixed.test/",
+ "__Host-test2=test; secure");
+ GetACookie(cookieService, "http://prefixed.test/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ // prefixed cookies won't be set without the secure flag
+ SetACookie(cookieService, "https://prefixed.test/", "__Secure-test=test");
+ SetACookie(cookieService, "https://prefixed.test/", "__Host-test=test");
+ GetACookie(cookieService, "https://prefixed.test/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ // prefixed cookies can be set when done correctly
+ SetACookie(cookieService, "https://prefixed.test/",
+ "__Secure-test=test; secure");
+ SetACookie(cookieService, "https://prefixed.test/",
+ "__Host-test=test; secure");
+ GetACookie(cookieService, "https://prefixed.test/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_CONTAIN, "__Secure-test=test"));
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_CONTAIN, "__Host-test=test"));
+
+ // but when set must not be returned to the host insecurely
+ GetACookie(cookieService, "http://prefixed.test/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ // Host-prefixed cookies cannot specify a domain
+ SetACookie(cookieService, "https://host.prefixed.test/",
+ "__Host-a=test; secure; domain=prefixed.test");
+ SetACookie(cookieService, "https://host.prefixed.test/",
+ "__Host-b=test; secure; domain=.prefixed.test");
+ SetACookie(cookieService, "https://host.prefixed.test/",
+ "__Host-c=test; secure; domain=host.prefixed.test");
+ SetACookie(cookieService, "https://host.prefixed.test/",
+ "__Host-d=test; secure; domain=.host.prefixed.test");
+ GetACookie(cookieService, "https://host.prefixed.test/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ // Host-prefixed cookies can only have a path of "/"
+ SetACookie(cookieService, "https://host.prefixed.test/some/path",
+ "__Host-e=test; secure");
+ SetACookie(cookieService, "https://host.prefixed.test/some/path",
+ "__Host-f=test; secure; path=/");
+ SetACookie(cookieService, "https://host.prefixed.test/some/path",
+ "__Host-g=test; secure; path=/some");
+ GetACookie(cookieService, "https://host.prefixed.test/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "__Host-f=test"));
+
+ // *** leave-secure-alone tests
+
+ // testing items 0 & 1 for 3.1 of spec Deprecate modification of ’secure’
+ // cookies from non-secure origins
+ SetACookie(cookieService, "http://www.security.test/",
+ "test=non-security; secure");
+ GetACookieNoHttp(cookieService, "https://www.security.test/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+ SetACookie(cookieService, "https://www.security.test/path/",
+ "test=security; secure; path=/path/");
+ GetACookieNoHttp(cookieService, "https://www.security.test/path/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=security"));
+ // testing items 2 & 3 & 4 for 3.2 of spec Deprecate modification of ’secure’
+ // cookies from non-secure origins
+ // Secure site can modify cookie value
+ SetACookie(cookieService, "https://www.security.test/path/",
+ "test=security2; secure; path=/path/");
+ GetACookieNoHttp(cookieService, "https://www.security.test/path/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=security2"));
+ // If new cookie contains same name, same host and partially matching path
+ // with an existing security cookie on non-security site, it can't modify an
+ // existing security cookie.
+ SetACookie(cookieService, "http://www.security.test/path/foo/",
+ "test=non-security; path=/path/foo");
+ GetACookieNoHttp(cookieService, "https://www.security.test/path/foo/",
+ cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=security2"));
+ // Non-secure cookie can set by same name, same host and non-matching path.
+ SetACookie(cookieService, "http://www.security.test/bar/",
+ "test=non-security; path=/bar");
+ GetACookieNoHttp(cookieService, "http://www.security.test/bar/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=non-security"));
+ // Modify value and downgrade secure level.
+ SetACookie(
+ cookieService, "https://www.security.test/",
+ "test_modify_cookie=security-cookie; secure; domain=.security.test");
+ GetACookieNoHttp(cookieService, "https://www.security.test/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL,
+ "test_modify_cookie=security-cookie"));
+ SetACookie(cookieService, "https://www.security.test/",
+ "test_modify_cookie=non-security-cookie; domain=.security.test");
+ GetACookieNoHttp(cookieService, "https://www.security.test/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL,
+ "test_modify_cookie=non-security-cookie"));
+
+ // Test the non-security cookie can set when domain or path not same to secure
+ // cookie of same name.
+ SetACookie(cookieService, "https://www.security.test/", "test=security3");
+ GetACookieNoHttp(cookieService, "http://www.security.test/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_CONTAIN, "test=security3"));
+ SetACookie(cookieService, "http://www.security.test/",
+ "test=non-security2; domain=security.test");
+ GetACookieNoHttp(cookieService, "http://www.security.test/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_CONTAIN, "test=non-security2"));
+
+ // *** nsICookieManager interface tests
+ nsCOMPtr<nsICookieManager> cookieMgr =
+ do_GetService(NS_COOKIEMANAGER_CONTRACTID, &rv0);
+ ASSERT_NS_SUCCEEDED(rv0);
+
+ const nsCOMPtr<nsICookieManager>& cookieMgr2 = cookieMgr;
+ ASSERT_TRUE(cookieMgr2);
+
+ mozilla::OriginAttributes attrs;
+
+ // first, ensure a clean slate
+ EXPECT_NS_SUCCEEDED(cookieMgr->RemoveAll());
+ // add some cookies
+ EXPECT_TRUE(NS_SUCCEEDED(cookieMgr2->AddNative("cookiemgr.test"_ns, // domain
+ "/foo"_ns, // path
+ "test1"_ns, // name
+ "yes"_ns, // value
+ false, // is secure
+ false, // is httponly
+ true, // is session
+ INT64_MAX, // expiry time
+ &attrs, // originAttributes
+ nsICookie::SAMESITE_NONE,
+ nsICookie::SCHEME_HTTPS)));
+ EXPECT_TRUE(NS_SUCCEEDED(cookieMgr2->AddNative(
+ "cookiemgr.test"_ns, // domain
+ "/foo"_ns, // path
+ "test2"_ns, // name
+ "yes"_ns, // value
+ false, // is secure
+ true, // is httponly
+ true, // is session
+ PR_Now() / PR_USEC_PER_SEC + 2, // expiry time
+ &attrs, // originAttributes
+ nsICookie::SAMESITE_NONE, nsICookie::SCHEME_HTTPS)));
+ EXPECT_TRUE(NS_SUCCEEDED(cookieMgr2->AddNative("new.domain"_ns, // domain
+ "/rabbit"_ns, // path
+ "test3"_ns, // name
+ "yes"_ns, // value
+ false, // is secure
+ false, // is httponly
+ true, // is session
+ INT64_MAX, // expiry time
+ &attrs, // originAttributes
+ nsICookie::SAMESITE_NONE,
+ nsICookie::SCHEME_HTTPS)));
+ // confirm using enumerator
+ nsTArray<RefPtr<nsICookie>> cookies;
+ EXPECT_NS_SUCCEEDED(cookieMgr->GetCookies(cookies));
+ nsCOMPtr<nsICookie> expiredCookie, newDomainCookie;
+ for (const auto& cookie : cookies) {
+ nsAutoCString name;
+ cookie->GetName(name);
+ if (name.EqualsLiteral("test2")) {
+ expiredCookie = cookie;
+ } else if (name.EqualsLiteral("test3")) {
+ newDomainCookie = cookie;
+ }
+ }
+ EXPECT_EQ(cookies.Length(), 3ul);
+ // check the httpOnly attribute of the second cookie is honored
+ GetACookie(cookieService, "http://cookiemgr.test/foo/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_CONTAIN, "test2=yes"));
+ GetACookieNoHttp(cookieService, "http://cookiemgr.test/foo/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_NOT_CONTAIN, "test2=yes"));
+ // check CountCookiesFromHost()
+ uint32_t hostCookies = 0;
+ EXPECT_TRUE(NS_SUCCEEDED(
+ cookieMgr2->CountCookiesFromHost("cookiemgr.test"_ns, &hostCookies)));
+ EXPECT_EQ(hostCookies, 2u);
+ // check CookieExistsNative() using the third cookie
+ bool found;
+ EXPECT_TRUE(NS_SUCCEEDED(cookieMgr2->CookieExistsNative(
+ "new.domain"_ns, "/rabbit"_ns, "test3"_ns, &attrs, &found)));
+ EXPECT_TRUE(found);
+
+ // sleep four seconds, to make sure the second cookie has expired
+ PR_Sleep(4 * PR_TicksPerSecond());
+ // check that both CountCookiesFromHost() and CookieExistsNative() count the
+ // expired cookie
+ EXPECT_TRUE(NS_SUCCEEDED(
+ cookieMgr2->CountCookiesFromHost("cookiemgr.test"_ns, &hostCookies)));
+ EXPECT_EQ(hostCookies, 2u);
+ EXPECT_TRUE(NS_SUCCEEDED(cookieMgr2->CookieExistsNative(
+ "cookiemgr.test"_ns, "/foo"_ns, "test2"_ns, &attrs, &found)));
+ EXPECT_TRUE(found);
+ // double-check RemoveAll() using the enumerator
+ EXPECT_NS_SUCCEEDED(cookieMgr->RemoveAll());
+ cookies.SetLength(0);
+ EXPECT_TRUE(NS_SUCCEEDED(cookieMgr->GetCookies(cookies)) &&
+ cookies.IsEmpty());
+
+ // *** eviction and creation ordering tests
+
+ // test that cookies are
+ // a) returned by order of creation time (oldest first, newest last)
+ // b) evicted by order of lastAccessed time, if the limit on cookies per host
+ // (50) is reached
+ nsAutoCString name;
+ nsAutoCString expected;
+ for (int32_t i = 0; i < 60; ++i) {
+ name = "test"_ns;
+ name.AppendInt(i);
+ name += "=creation"_ns;
+ SetACookie(cookieService, "http://creation.ordering.tests/", name.get());
+
+ if (i >= 10) {
+ expected += name;
+ if (i < 59) expected += "; "_ns;
+ }
+ }
+ GetACookie(cookieService, "http://creation.ordering.tests/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, expected.get()));
+
+ cookieMgr->RemoveAll();
+
+ for (int32_t i = 0; i < 60; ++i) {
+ name = "test"_ns;
+ name.AppendInt(i);
+ name += "=delete_non_security"_ns;
+
+ // Create 50 cookies that include the secure flag.
+ if (i < 50) {
+ name += "; secure"_ns;
+ SetACookie(cookieService, "https://creation.ordering.tests/", name.get());
+ } else {
+ // non-security cookies will be removed beside the latest cookie that be
+ // created.
+ SetACookie(cookieService, "http://creation.ordering.tests/", name.get());
+ }
+ }
+ GetACookie(cookieService, "http://creation.ordering.tests/", cookie);
+
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ // *** SameSite attribute - parsing and cookie storage tests
+ // Clear the cookies
+ EXPECT_NS_SUCCEEDED(cookieMgr->RemoveAll());
+
+ // None of these cookies will be set because using
+ // CookieJarSettings::GetBlockingAll().
+ SetACookieJarBlocked(cookieService, "http://samesite.test", "unset=yes");
+ SetACookieJarBlocked(cookieService, "http://samesite.test",
+ "unspecified=yes; samesite");
+ SetACookieJarBlocked(cookieService, "http://samesite.test",
+ "empty=yes; samesite=");
+ SetACookieJarBlocked(cookieService, "http://samesite.test",
+ "bogus=yes; samesite=bogus");
+ SetACookieJarBlocked(cookieService, "http://samesite.test",
+ "strict=yes; samesite=strict");
+ SetACookieJarBlocked(cookieService, "http://samesite.test",
+ "lax=yes; samesite=lax");
+
+ cookies.SetLength(0);
+ EXPECT_NS_SUCCEEDED(cookieMgr->GetCookies(cookies));
+
+ EXPECT_TRUE(cookies.IsEmpty());
+
+ // Set cookies with various incantations of the samesite attribute:
+ // No same site attribute present
+ SetACookie(cookieService, "http://samesite.test", "unset=yes");
+ // samesite attribute present but with no value
+ SetACookie(cookieService, "http://samesite.test",
+ "unspecified=yes; samesite");
+ // samesite attribute present but with an empty value
+ SetACookie(cookieService, "http://samesite.test", "empty=yes; samesite=");
+ // samesite attribute present but with an invalid value
+ SetACookie(cookieService, "http://samesite.test",
+ "bogus=yes; samesite=bogus");
+ // samesite=strict
+ SetACookie(cookieService, "http://samesite.test",
+ "strict=yes; samesite=strict");
+ // samesite=lax
+ SetACookie(cookieService, "http://samesite.test", "lax=yes; samesite=lax");
+
+ cookies.SetLength(0);
+ EXPECT_NS_SUCCEEDED(cookieMgr->GetCookies(cookies));
+
+ // check the cookies for the required samesite value
+ for (const auto& cookie : cookies) {
+ nsAutoCString name;
+ cookie->GetName(name);
+ int32_t sameSiteAttr;
+ cookie->GetSameSite(&sameSiteAttr);
+ if (name.EqualsLiteral("unset")) {
+ EXPECT_TRUE(sameSiteAttr == nsICookie::SAMESITE_NONE);
+ } else if (name.EqualsLiteral("unspecified")) {
+ EXPECT_TRUE(sameSiteAttr == nsICookie::SAMESITE_NONE);
+ } else if (name.EqualsLiteral("empty")) {
+ EXPECT_TRUE(sameSiteAttr == nsICookie::SAMESITE_NONE);
+ } else if (name.EqualsLiteral("bogus")) {
+ EXPECT_TRUE(sameSiteAttr == nsICookie::SAMESITE_NONE);
+ } else if (name.EqualsLiteral("strict")) {
+ EXPECT_TRUE(sameSiteAttr == nsICookie::SAMESITE_STRICT);
+ } else if (name.EqualsLiteral("lax")) {
+ EXPECT_TRUE(sameSiteAttr == nsICookie::SAMESITE_LAX);
+ }
+ }
+
+ EXPECT_TRUE(cookies.Length() == 6);
+
+ // *** SameSite attribute
+ // Clear the cookies
+ EXPECT_NS_SUCCEEDED(cookieMgr->RemoveAll());
+
+ // please note that the flag aForeign is always set to true using this test
+ // setup because no nsIChannel is passed to SetCookieString(). therefore we
+ // can only test that no cookies are sent for cross origin requests using
+ // same-site cookies.
+ SetACookie(cookieService, "http://www.samesite.com",
+ "test=sameSiteStrictVal; samesite=strict");
+ GetACookie(cookieService, "http://www.notsamesite.com", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ SetACookie(cookieService, "http://www.samesite.test",
+ "test=sameSiteLaxVal; samesite=lax");
+ GetACookie(cookieService, "http://www.notsamesite.com", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ static const char* secureURIs[] = {
+ "http://localhost", "http://localhost:1234", "http://127.0.0.1",
+ "http://127.0.0.2", "http://127.1.0.1", "http://[::1]",
+ // TODO bug 1220810 "http://xyzzy.localhost"
+ };
+
+ uint32_t numSecureURIs = sizeof(secureURIs) / sizeof(const char*);
+ for (uint32_t i = 0; i < numSecureURIs; ++i) {
+ SetACookie(cookieService, secureURIs[i], "test=basic; secure");
+ GetACookie(cookieService, secureURIs[i], cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=basic"));
+ SetACookie(cookieService, secureURIs[i], "test=basic1");
+ GetACookie(cookieService, secureURIs[i], cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=basic1"));
+ }
+
+ // XXX the following are placeholders: add these tests please!
+ // *** "noncompliant cookie" tests
+ // *** IP address tests
+ // *** speed tests
+}
+
+TEST(TestCookie, SameSiteLax)
+{
+ Preferences::SetBool("network.cookie.sameSite.laxByDefault", true);
+
+ nsresult rv;
+
+ nsCOMPtr<nsICookieService> cookieService =
+ do_GetService(kCookieServiceCID, &rv);
+ ASSERT_NS_SUCCEEDED(rv);
+
+ nsCOMPtr<nsICookieManager> cookieMgr =
+ do_GetService(NS_COOKIEMANAGER_CONTRACTID, &rv);
+ ASSERT_NS_SUCCEEDED(rv);
+
+ EXPECT_NS_SUCCEEDED(cookieMgr->RemoveAll());
+
+ SetACookie(cookieService, "http://samesite.test", "unset=yes");
+
+ nsTArray<RefPtr<nsICookie>> cookies;
+ EXPECT_NS_SUCCEEDED(cookieMgr->GetCookies(cookies));
+ EXPECT_EQ(cookies.Length(), (uint64_t)1);
+
+ Cookie* cookie = static_cast<Cookie*>(cookies[0].get());
+ EXPECT_EQ(cookie->RawSameSite(), nsICookie::SAMESITE_NONE);
+ EXPECT_EQ(cookie->SameSite(), nsICookie::SAMESITE_LAX);
+
+ Preferences::SetCString("network.cookie.sameSite.laxByDefault.disabledHosts",
+ "foo.com,samesite.test,bar.net");
+
+ EXPECT_NS_SUCCEEDED(cookieMgr->RemoveAll());
+
+ cookies.SetLength(0);
+ EXPECT_NS_SUCCEEDED(cookieMgr->GetCookies(cookies));
+ EXPECT_EQ(cookies.Length(), (uint64_t)0);
+
+ SetACookie(cookieService, "http://samesite.test", "unset=yes");
+
+ cookies.SetLength(0);
+ EXPECT_NS_SUCCEEDED(cookieMgr->GetCookies(cookies));
+ EXPECT_EQ(cookies.Length(), (uint64_t)1);
+
+ cookie = static_cast<Cookie*>(cookies[0].get());
+ EXPECT_EQ(cookie->RawSameSite(), nsICookie::SAMESITE_NONE);
+ EXPECT_EQ(cookie->SameSite(), nsICookie::SAMESITE_LAX);
+}
+
+TEST(TestCookie, OnionSite)
+{
+ Preferences::SetBool("dom.securecontext.allowlist_onions", true);
+ Preferences::SetBool("network.cookie.sameSite.laxByDefault", false);
+
+ nsresult rv;
+ nsCString cookie;
+
+ nsCOMPtr<nsICookieService> cookieService =
+ do_GetService(kCookieServiceCID, &rv);
+ ASSERT_NS_SUCCEEDED(rv);
+
+ // .onion secure cookie tests
+ SetACookie(cookieService, "http://123456789abcdef.onion/",
+ "test=onion-security; secure");
+ GetACookieNoHttp(cookieService, "https://123456789abcdef.onion/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=onion-security"));
+ SetACookie(cookieService, "http://123456789abcdef.onion/",
+ "test=onion-security2; secure");
+ GetACookieNoHttp(cookieService, "http://123456789abcdef.onion/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=onion-security2"));
+ SetACookie(cookieService, "https://123456789abcdef.onion/",
+ "test=onion-security3; secure");
+ GetACookieNoHttp(cookieService, "http://123456789abcdef.onion/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=onion-security3"));
+ SetACookie(cookieService, "http://123456789abcdef.onion/",
+ "test=onion-security4");
+ GetACookieNoHttp(cookieService, "http://123456789abcdef.onion/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "test=onion-security4"));
+}
+
+TEST(TestCookie, HiddenPrefix)
+{
+ nsresult rv;
+ nsCString cookie;
+
+ nsCOMPtr<nsICookieService> cookieService =
+ do_GetService(kCookieServiceCID, &rv);
+ ASSERT_NS_SUCCEEDED(rv);
+
+ SetACookie(cookieService, "http://hiddenprefix.test/", "=__Host-test=a");
+ GetACookie(cookieService, "http://hiddenprefix.test/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ SetACookie(cookieService, "http://hiddenprefix.test/", "=__Secure-test=a");
+ GetACookie(cookieService, "http://hiddenprefix.test/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ SetACookie(cookieService, "http://hiddenprefix.test/", "=__Host-check");
+ GetACookie(cookieService, "http://hiddenprefix.test/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ SetACookie(cookieService, "http://hiddenprefix.test/", "=__Secure-check");
+ GetACookie(cookieService, "http://hiddenprefix.test/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+}
+
+TEST(TestCookie, BlockUnicode)
+{
+ Preferences::SetBool("network.cookie.blockUnicode", true);
+
+ nsresult rv;
+ nsCString cookie;
+
+ nsCOMPtr<nsICookieService> cookieService =
+ do_GetService(kCookieServiceCID, &rv);
+ ASSERT_NS_SUCCEEDED(rv);
+
+ SetACookie(cookieService, "http://unicode.com/", "name=🍪");
+ GetACookie(cookieService, "http://unicode.com/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ SetACookie(cookieService, "http://unicode.com/", "🍪=value");
+ GetACookie(cookieService, "http://unicode.com/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+ Preferences::SetBool("network.cookie.blockUnicode", false);
+
+ SetACookie(cookieService, "http://unicode.com/", "name=🍪");
+ GetACookie(cookieService, "http://unicode.com/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "name=🍪"));
+
+ nsCOMPtr<nsICookieManager> cookieMgr =
+ do_GetService(NS_COOKIEMANAGER_CONTRACTID);
+ EXPECT_NS_SUCCEEDED(cookieMgr->RemoveAll());
+
+ SetACookie(cookieService, "http://unicode.com/", "🍪=value");
+ GetACookie(cookieService, "http://unicode.com/", cookie);
+ EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL, "🍪=value"));
+
+ EXPECT_NS_SUCCEEDED(cookieMgr->RemoveAll());
+ Preferences::ClearUser("network.cookie.blockUnicode");
+}
diff --git a/netwerk/test/gtest/TestDNSPacket.cpp b/netwerk/test/gtest/TestDNSPacket.cpp
new file mode 100644
index 0000000000..49530b80e0
--- /dev/null
+++ b/netwerk/test/gtest/TestDNSPacket.cpp
@@ -0,0 +1,69 @@
+#include "gtest/gtest.h"
+
+#include "mozilla/net/DNSPacket.h"
+#include "mozilla/Preferences.h"
+
+using namespace mozilla;
+using namespace mozilla::net;
+
+void AssertDnsPadding(uint32_t PaddingLength, unsigned int WithPadding,
+ unsigned int WithoutPadding, bool DisableEcn,
+ const nsCString& host) {
+ DNSPacket encoder;
+ nsCString buf;
+
+ ASSERT_EQ(Preferences::SetUint("network.trr.padding.length", PaddingLength),
+ NS_OK);
+
+ ASSERT_EQ(Preferences::SetBool("network.trr.padding", true), NS_OK);
+ ASSERT_EQ(encoder.EncodeRequest(buf, host, 1, DisableEcn), NS_OK);
+ ASSERT_EQ(buf.Length(), WithPadding);
+
+ ASSERT_EQ(Preferences::SetBool("network.trr.padding", false), NS_OK);
+ ASSERT_EQ(encoder.EncodeRequest(buf, host, 1, DisableEcn), NS_OK);
+ ASSERT_EQ(buf.Length(), WithoutPadding);
+}
+
+TEST(TestDNSPacket, PaddingLenEcn)
+{
+ AssertDnsPadding(16, 48, 41, true, "a.de"_ns);
+ AssertDnsPadding(16, 48, 42, true, "ab.de"_ns);
+ AssertDnsPadding(16, 48, 43, true, "abc.de"_ns);
+ AssertDnsPadding(16, 48, 44, true, "abcd.de"_ns);
+ AssertDnsPadding(16, 64, 45, true, "abcde.de"_ns);
+ AssertDnsPadding(16, 64, 46, true, "abcdef.de"_ns);
+ AssertDnsPadding(16, 64, 47, true, "abcdefg.de"_ns);
+ AssertDnsPadding(16, 64, 48, true, "abcdefgh.de"_ns);
+}
+
+TEST(TestDNSPacket, PaddingLenDisableEcn)
+{
+ AssertDnsPadding(16, 48, 22, false, "a.de"_ns);
+ AssertDnsPadding(16, 48, 23, false, "ab.de"_ns);
+ AssertDnsPadding(16, 48, 24, false, "abc.de"_ns);
+ AssertDnsPadding(16, 48, 25, false, "abcd.de"_ns);
+ AssertDnsPadding(16, 48, 26, false, "abcde.de"_ns);
+ AssertDnsPadding(16, 48, 27, false, "abcdef.de"_ns);
+ AssertDnsPadding(16, 48, 32, false, "abcdefghijk.de"_ns);
+ AssertDnsPadding(16, 48, 33, false, "abcdefghijkl.de"_ns);
+ AssertDnsPadding(16, 64, 34, false, "abcdefghijklm.de"_ns);
+ AssertDnsPadding(16, 64, 35, false, "abcdefghijklmn.de"_ns);
+}
+
+TEST(TestDNSPacket, PaddingLengths)
+{
+ AssertDnsPadding(0, 45, 41, true, "a.de"_ns);
+ AssertDnsPadding(1, 45, 41, true, "a.de"_ns);
+ AssertDnsPadding(2, 46, 41, true, "a.de"_ns);
+ AssertDnsPadding(3, 45, 41, true, "a.de"_ns);
+ AssertDnsPadding(4, 48, 41, true, "a.de"_ns);
+ AssertDnsPadding(16, 48, 41, true, "a.de"_ns);
+ AssertDnsPadding(32, 64, 41, true, "a.de"_ns);
+ AssertDnsPadding(42, 84, 41, true, "a.de"_ns);
+ AssertDnsPadding(52, 52, 41, true, "a.de"_ns);
+ AssertDnsPadding(80, 80, 41, true, "a.de"_ns);
+ AssertDnsPadding(128, 128, 41, true, "a.de"_ns);
+ AssertDnsPadding(256, 256, 41, true, "a.de"_ns);
+ AssertDnsPadding(1024, 1024, 41, true, "a.de"_ns);
+ AssertDnsPadding(1025, 1024, 41, true, "a.de"_ns);
+}
diff --git a/netwerk/test/gtest/TestHeaders.cpp b/netwerk/test/gtest/TestHeaders.cpp
new file mode 100644
index 0000000000..0da6b06c70
--- /dev/null
+++ b/netwerk/test/gtest/TestHeaders.cpp
@@ -0,0 +1,29 @@
+#include "gtest/gtest.h"
+
+#include "nsHttpHeaderArray.h"
+
+TEST(TestHeaders, DuplicateHSTS)
+{
+ // When the Strict-Transport-Security header is sent multiple times, its
+ // effective value is the value of the first item. It is not merged as other
+ // headers are.
+ mozilla::net::nsHttpHeaderArray headers;
+ nsresult rv = headers.SetHeaderFromNet(
+ mozilla::net::nsHttp::Strict_Transport_Security,
+ "Strict_Transport_Security"_ns, "max-age=360"_ns, true);
+ ASSERT_EQ(rv, NS_OK);
+
+ nsAutoCString h;
+ rv = headers.GetHeader(mozilla::net::nsHttp::Strict_Transport_Security, h);
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(h.get(), "max-age=360");
+
+ rv = headers.SetHeaderFromNet(mozilla::net::nsHttp::Strict_Transport_Security,
+ "Strict_Transport_Security"_ns,
+ "max-age=720"_ns, true);
+ ASSERT_EQ(rv, NS_OK);
+
+ rv = headers.GetHeader(mozilla::net::nsHttp::Strict_Transport_Security, h);
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(h.get(), "max-age=360");
+}
diff --git a/netwerk/test/gtest/TestHttpAtom.cpp b/netwerk/test/gtest/TestHttpAtom.cpp
new file mode 100644
index 0000000000..917210a2c4
--- /dev/null
+++ b/netwerk/test/gtest/TestHttpAtom.cpp
@@ -0,0 +1,39 @@
+#include "gtest/gtest.h"
+
+#include "nsHttp.h"
+
+TEST(TestHttpAtom, AtomComparison)
+{
+ mozilla::net::nsHttpAtom atom(mozilla::net::nsHttp::Host);
+ mozilla::net::nsHttpAtom same_atom(mozilla::net::nsHttp::Host);
+ mozilla::net::nsHttpAtom different_atom(mozilla::net::nsHttp::Accept);
+
+ ASSERT_EQ(atom, atom);
+ ASSERT_EQ(atom, mozilla::net::nsHttp::Host);
+ ASSERT_EQ(mozilla::net::nsHttp::Host, atom);
+ ASSERT_EQ(atom, same_atom);
+ ASSERT_EQ(atom.get(), same_atom.get());
+ ASSERT_EQ(atom.get(), mozilla::net::nsHttp::Host.get());
+
+ ASSERT_NE(atom, different_atom);
+ ASSERT_NE(atom.get(), different_atom.get());
+}
+
+TEST(TestHttpAtom, LiteralComparison)
+{
+ ASSERT_EQ(mozilla::net::nsHttp::Host, mozilla::net::nsHttp::Host);
+ ASSERT_NE(mozilla::net::nsHttp::Host, mozilla::net::nsHttp::Accept);
+
+ ASSERT_EQ(mozilla::net::nsHttp::Host.get(), mozilla::net::nsHttp::Host.get());
+ ASSERT_NE(mozilla::net::nsHttp::Host.get(),
+ mozilla::net::nsHttp::Accept.get());
+}
+
+TEST(TestHttpAtom, Validity)
+{
+ mozilla::net::nsHttpAtom atom(mozilla::net::nsHttp::Host);
+ ASSERT_TRUE(atom);
+
+ mozilla::net::nsHttpAtom atom_empty;
+ ASSERT_FALSE(atom_empty);
+}
diff --git a/netwerk/test/gtest/TestHttpAuthUtils.cpp b/netwerk/test/gtest/TestHttpAuthUtils.cpp
new file mode 100644
index 0000000000..78fb40d4d0
--- /dev/null
+++ b/netwerk/test/gtest/TestHttpAuthUtils.cpp
@@ -0,0 +1,43 @@
+#include "gtest/gtest.h"
+
+#include "mozilla/net/HttpAuthUtils.h"
+#include "mozilla/Preferences.h"
+#include "nsNetUtil.h"
+
+namespace mozilla {
+namespace net {
+
+#define TEST_PREF "network.http_test.auth_utils"
+
+TEST(TestHttpAuthUtils, Bug1351301)
+{
+ nsCOMPtr<nsIURI> url;
+ nsAutoCString spec;
+
+ ASSERT_EQ(Preferences::SetCString(TEST_PREF, "bar.com"), NS_OK);
+ spec = "http://bar.com";
+ ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
+ ASSERT_EQ(auth::URIMatchesPrefPattern(url, TEST_PREF), true);
+
+ spec = "http://foo.bar.com";
+ ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
+ ASSERT_EQ(auth::URIMatchesPrefPattern(url, TEST_PREF), true);
+
+ spec = "http://foobar.com";
+ ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
+ ASSERT_EQ(auth::URIMatchesPrefPattern(url, TEST_PREF), false);
+
+ ASSERT_EQ(Preferences::SetCString(TEST_PREF, ".bar.com"), NS_OK);
+ spec = "http://foo.bar.com";
+ ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
+ ASSERT_EQ(auth::URIMatchesPrefPattern(url, TEST_PREF), true);
+
+ spec = "http://bar.com";
+ ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
+ ASSERT_EQ(auth::URIMatchesPrefPattern(url, TEST_PREF), false);
+
+ ASSERT_EQ(Preferences::ClearUser(TEST_PREF), NS_OK);
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/test/gtest/TestHttpChannel.cpp b/netwerk/test/gtest/TestHttpChannel.cpp
new file mode 100644
index 0000000000..10ef744bb4
--- /dev/null
+++ b/netwerk/test/gtest/TestHttpChannel.cpp
@@ -0,0 +1,135 @@
+#include "gtest/gtest.h"
+
+#include "nsCOMPtr.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/PreloadHashKey.h"
+#include "mozilla/SpinEventLoopUntil.h"
+#include "nsNetUtil.h"
+#include "nsIChannel.h"
+#include "nsIStreamListener.h"
+#include "nsThreadUtils.h"
+#include "nsStringStream.h"
+#include "nsIPrivateBrowsingChannel.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsContentUtils.h"
+
+using namespace mozilla;
+
+class FakeListener : public nsIStreamListener, public nsIInterfaceRequestor {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+ NS_DECL_NSIINTERFACEREQUESTOR
+
+ enum { Never, OnStart, OnData, OnStop } mCancelIn = Never;
+
+ nsresult mOnStartResult = NS_OK;
+ nsresult mOnDataResult = NS_OK;
+ nsresult mOnStopResult = NS_OK;
+
+ bool mOnStart = false;
+ nsCString mOnData;
+ Maybe<nsresult> mOnStop;
+
+ private:
+ virtual ~FakeListener() = default;
+};
+
+NS_IMPL_ISUPPORTS(FakeListener, nsIStreamListener, nsIRequestObserver,
+ nsIInterfaceRequestor)
+
+NS_IMETHODIMP
+FakeListener::GetInterface(const nsIID& aIID, void** aResult) {
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = nullptr;
+ return NS_NOINTERFACE;
+}
+
+NS_IMETHODIMP FakeListener::OnStartRequest(nsIRequest* request) {
+ EXPECT_FALSE(mOnStart);
+ mOnStart = true;
+
+ if (mCancelIn == OnStart) {
+ request->Cancel(NS_ERROR_ABORT);
+ }
+
+ return mOnStartResult;
+}
+
+NS_IMETHODIMP FakeListener::OnDataAvailable(nsIRequest* request,
+ nsIInputStream* input,
+ uint64_t offset, uint32_t count) {
+ nsAutoCString data;
+ data.SetLength(count);
+
+ uint32_t read;
+ input->Read(data.BeginWriting(), count, &read);
+ mOnData += data;
+
+ if (mCancelIn == OnData) {
+ request->Cancel(NS_ERROR_ABORT);
+ }
+
+ return mOnDataResult;
+}
+
+NS_IMETHODIMP FakeListener::OnStopRequest(nsIRequest* request,
+ nsresult status) {
+ EXPECT_FALSE(mOnStop);
+ mOnStop.emplace(status);
+
+ if (mCancelIn == OnStop) {
+ request->Cancel(NS_ERROR_ABORT);
+ }
+
+ return mOnStopResult;
+}
+
+// Test that nsHttpChannel::AsyncOpen properly picks up changes to
+// loadInfo.mPrivateBrowsingId that occur after the channel was created.
+TEST(TestHttpChannel, PBAsyncOpen)
+{
+ nsCOMPtr<nsIURI> uri;
+ NS_NewURI(getter_AddRefs(uri), "http://localhost/"_ns);
+
+ nsCOMPtr<nsIChannel> channel;
+ nsresult rv = NS_NewChannel(
+ getter_AddRefs(channel), uri, nsContentUtils::GetSystemPrincipal(),
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ nsIContentPolicy::TYPE_OTHER);
+ ASSERT_EQ(rv, NS_OK);
+
+ RefPtr<FakeListener> listener = new FakeListener();
+ rv = channel->SetNotificationCallbacks(listener);
+ ASSERT_EQ(rv, NS_OK);
+
+ nsCOMPtr<nsIPrivateBrowsingChannel> pbchannel = do_QueryInterface(channel);
+ ASSERT_TRUE(pbchannel);
+
+ bool isPrivate = false;
+ rv = pbchannel->GetIsChannelPrivate(&isPrivate);
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(isPrivate, false);
+
+ nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
+ OriginAttributes attrs;
+ attrs.mPrivateBrowsingId = 1;
+ rv = loadInfo->SetOriginAttributes(attrs);
+ ASSERT_EQ(rv, NS_OK);
+
+ rv = pbchannel->GetIsChannelPrivate(&isPrivate);
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(isPrivate, false);
+
+ rv = channel->AsyncOpen(listener);
+ ASSERT_EQ(rv, NS_OK);
+
+ rv = pbchannel->GetIsChannelPrivate(&isPrivate);
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(isPrivate, true);
+
+ MOZ_ALWAYS_TRUE(mozilla::SpinEventLoopUntil(
+ "TEST(TestHttpChannel, PBAsyncOpen)"_ns,
+ [&]() -> bool { return listener->mOnStop.isSome(); }));
+}
diff --git a/netwerk/test/gtest/TestHttpResponseHead.cpp b/netwerk/test/gtest/TestHttpResponseHead.cpp
new file mode 100644
index 0000000000..643489d496
--- /dev/null
+++ b/netwerk/test/gtest/TestHttpResponseHead.cpp
@@ -0,0 +1,183 @@
+#include "gtest/gtest.h"
+
+#include "chrome/common/ipc_message.h"
+#include "mozilla/net/PHttpChannelParams.h"
+#include "mozilla/Unused.h"
+#include "nsHttp.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefService.h"
+#include "nsURLHelper.h"
+
+namespace mozilla {
+namespace net {
+
+void AssertRoundTrips(const nsHttpResponseHead& aHead) {
+ {
+ // Assert it round-trips via IPC.
+ UniquePtr<IPC::Message> msg(new IPC::Message(MSG_ROUTING_NONE, 0));
+ IPC::MessageWriter writer(*msg);
+ IPC::ParamTraits<nsHttpResponseHead>::Write(&writer, aHead);
+
+ nsHttpResponseHead deserializedHead;
+ IPC::MessageReader reader(*msg);
+ bool res = IPC::ParamTraits<mozilla::net::nsHttpResponseHead>::Read(
+ &reader, &deserializedHead);
+ ASSERT_TRUE(res);
+ ASSERT_EQ(aHead, deserializedHead);
+ }
+
+ {
+ // Assert it round-trips through copy-ctor.
+ nsHttpResponseHead copied(aHead);
+ ASSERT_EQ(aHead, copied);
+ }
+
+ {
+ // Assert it round-trips through operator=
+ nsHttpResponseHead copied;
+ copied = aHead;
+ // It is important that the below statement cannot be
+ // ASSERT_EQ(aHead, copied) to avoid potential lock-order inversion problem.
+ // See Bug 1829445 for more details
+ ASSERT_EQ(copied, aHead);
+ }
+}
+
+TEST(TestHttpResponseHead, Bug1636930)
+{
+ nsHttpResponseHead head;
+
+ Unused << head.ParseStatusLine("HTTP/1.1 200 OK"_ns);
+ Unused << head.ParseHeaderLine("content-type: text/plain"_ns);
+ Unused << head.ParseHeaderLine("etag: Just testing"_ns);
+ Unused << head.ParseHeaderLine("cache-control: max-age=99999"_ns);
+ Unused << head.ParseHeaderLine("accept-ranges: bytes"_ns);
+ Unused << head.ParseHeaderLine("content-length: 1408"_ns);
+ Unused << head.ParseHeaderLine("connection: close"_ns);
+ Unused << head.ParseHeaderLine("server: httpd.js"_ns);
+ Unused << head.ParseHeaderLine("date: Tue, 12 May 2020 09:24:23 GMT"_ns);
+
+ AssertRoundTrips(head);
+}
+
+TEST(TestHttpResponseHead, bug1649807)
+{
+ nsHttpResponseHead head;
+
+ Unused << head.ParseStatusLine("HTTP/1.1 200 OK"_ns);
+ Unused << head.ParseHeaderLine("content-type: text/plain"_ns);
+ Unused << head.ParseHeaderLine("etag: Just testing"_ns);
+ Unused << head.ParseHeaderLine("cache-control: age=99999"_ns);
+ Unused << head.ParseHeaderLine("accept-ranges: bytes"_ns);
+ Unused << head.ParseHeaderLine("content-length: 1408"_ns);
+ Unused << head.ParseHeaderLine("connection: close"_ns);
+ Unused << head.ParseHeaderLine("server: httpd.js"_ns);
+ Unused << head.ParseHeaderLine("pragma: no-cache"_ns);
+ Unused << head.ParseHeaderLine("date: Tue, 12 May 2020 09:24:23 GMT"_ns);
+
+ ASSERT_FALSE(head.NoCache())
+ << "Cache-Control wins over Pragma: no-cache";
+ AssertRoundTrips(head);
+}
+
+TEST(TestHttpResponseHead, bug1660200)
+{
+ nsHttpResponseHead head;
+
+ Unused << head.ParseStatusLine("HTTP/1.1 200 OK"_ns);
+ Unused << head.ParseHeaderLine("content-type: text/plain"_ns);
+ Unused << head.ParseHeaderLine("etag: Just testing"_ns);
+ Unused << head.ParseHeaderLine("cache-control: no-cache"_ns);
+ Unused << head.ParseHeaderLine("accept-ranges: bytes"_ns);
+ Unused << head.ParseHeaderLine("content-length: 1408"_ns);
+ Unused << head.ParseHeaderLine("connection: close"_ns);
+ Unused << head.ParseHeaderLine("server: httpd.js"_ns);
+ Unused << head.ParseHeaderLine("date: Tue, 12 May 2020 09:24:23 GMT"_ns);
+
+ AssertRoundTrips(head);
+}
+
+TEST(TestHttpResponseHead, bug1687903)
+{
+ nsHttpResponseHead head;
+
+ bool usingStrictParsing = false;
+ nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+ if (prefs) {
+ prefs->GetBoolPref("network.http.strict_response_status_line_parsing",
+ &usingStrictParsing);
+ }
+
+ nsresult expectation =
+ usingStrictParsing ? NS_ERROR_PARSING_HTTP_STATUS_LINE : NS_OK;
+
+ ASSERT_EQ(expectation, head.ParseStatusLine("HTTP/1.1 "_ns));
+ ASSERT_EQ(expectation, head.ParseStatusLine("HTTP/1.1 BLAH"_ns));
+ ASSERT_EQ(expectation, head.ParseStatusLine("HTTP/1.1 1000 BOO"_ns));
+ ASSERT_EQ(expectation, head.ParseStatusLine("HTTP/1.1 0200 BOO"_ns));
+ ASSERT_EQ(expectation, head.ParseStatusLine("HTTP/1.1 60200 200"_ns));
+ ASSERT_EQ(expectation, head.ParseStatusLine("HTTP/1.1 131072 HIOK"_ns));
+ ASSERT_EQ(expectation, head.ParseStatusLine("HTTP/1.1 -200 OK"_ns));
+ ASSERT_EQ(expectation, head.ParseStatusLine("HTTP/1.1 0x9 OK"_ns));
+ ASSERT_EQ(expectation, head.ParseStatusLine("HTTP/1.1 C8 OK"_ns));
+}
+
+TEST(TestHttpResponseHead, atoms)
+{
+ // Test that the resolving the content-type atom returns the initial static
+ ASSERT_EQ(nsHttp::Content_Type, nsHttp::ResolveAtom("content-type"_ns));
+ // Check that they're case insensitive
+ ASSERT_EQ(nsHttp::ResolveAtom("Content-Type"_ns),
+ nsHttp::ResolveAtom("content-type"_ns));
+ // This string literal should be the backing of the atom when resolved first
+ auto header1 = "CustomHeaderXXX1"_ns;
+ auto atom1 = nsHttp::ResolveAtom(header1);
+ auto header2 = "customheaderxxx1"_ns;
+ auto atom2 = nsHttp::ResolveAtom(header2);
+ ASSERT_EQ(atom1, atom2);
+ ASSERT_EQ(atom1.get(), atom2.get());
+ // Check that we get the expected pointer back.
+ ASSERT_EQ(atom2.get(), header1.BeginReading());
+}
+
+TEST(ContentTypeParsing, CommentHandling1)
+{
+ bool dummy;
+ const nsAutoCString val("text/html;charset=gbk(");
+ nsCString contentType;
+ nsCString contentCharset;
+
+ net_ParseContentType(val, contentType, contentCharset, &dummy);
+
+ ASSERT_TRUE(contentType.EqualsLiteral("text/html"));
+ ASSERT_TRUE(contentCharset.EqualsLiteral("gbk("));
+}
+
+TEST(ContentTypeParsing, CommentHandling2)
+{
+ bool dummy;
+ const nsAutoCString val("text/html;x=(;charset=gbk");
+ nsCString contentType;
+ nsCString contentCharset;
+
+ net_ParseContentType(val, contentType, contentCharset, &dummy);
+
+ ASSERT_TRUE(contentType.EqualsLiteral("text/html"));
+ ASSERT_TRUE(contentCharset.EqualsLiteral("gbk"));
+}
+
+TEST(ContentTypeParsing, CommentHandling3)
+{
+ bool dummy;
+ const nsAutoCString val("text/html;test=test;(;charset=gbk");
+ nsCString contentType;
+ nsCString contentCharset;
+
+ net_ParseContentType(val, contentType, contentCharset, &dummy);
+
+ ASSERT_TRUE(contentType.EqualsLiteral("text/html"));
+ ASSERT_TRUE(contentCharset.EqualsLiteral("gbk"));
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/test/gtest/TestInputStreamTransport.cpp b/netwerk/test/gtest/TestInputStreamTransport.cpp
new file mode 100644
index 0000000000..43df0e193a
--- /dev/null
+++ b/netwerk/test/gtest/TestInputStreamTransport.cpp
@@ -0,0 +1,204 @@
+#include "gtest/gtest.h"
+
+#include "nsIStreamTransportService.h"
+#include "nsStreamUtils.h"
+#include "nsThreadUtils.h"
+#include "Helpers.h"
+#include "nsNetCID.h"
+#include "nsServiceManagerUtils.h"
+#include "nsITransport.h"
+#include "nsNetUtil.h"
+
+static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
+
+void CreateStream(already_AddRefed<nsIInputStream> aSource,
+ nsIAsyncInputStream** aStream) {
+ nsCOMPtr<nsIInputStream> source = std::move(aSource);
+
+ nsresult rv;
+ nsCOMPtr<nsIStreamTransportService> sts =
+ do_GetService(kStreamTransportServiceCID, &rv);
+ ASSERT_EQ(NS_OK, rv);
+
+ nsCOMPtr<nsITransport> transport;
+ rv = sts->CreateInputTransport(source, true, getter_AddRefs(transport));
+ ASSERT_EQ(NS_OK, rv);
+
+ nsCOMPtr<nsIInputStream> wrapper;
+ rv = transport->OpenInputStream(0, 0, 0, getter_AddRefs(wrapper));
+ ASSERT_EQ(NS_OK, rv);
+
+ nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(wrapper);
+ MOZ_ASSERT(asyncStream);
+
+ asyncStream.forget(aStream);
+}
+
+class BlockingSyncStream final : public nsIInputStream {
+ nsCOMPtr<nsIInputStream> mStream;
+
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ explicit BlockingSyncStream(const nsACString& aBuffer) {
+ NS_NewCStringInputStream(getter_AddRefs(mStream), aBuffer);
+ }
+
+ NS_IMETHOD
+ Available(uint64_t* aLength) override { return mStream->Available(aLength); }
+
+ NS_IMETHOD
+ StreamStatus() override { return mStream->StreamStatus(); }
+
+ NS_IMETHOD
+ Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount) override {
+ return mStream->Read(aBuffer, aCount, aReadCount);
+ }
+
+ NS_IMETHOD
+ ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, uint32_t aCount,
+ uint32_t* aResult) override {
+ return mStream->ReadSegments(aWriter, aClosure, aCount, aResult);
+ }
+
+ NS_IMETHOD
+ Close() override { return mStream->Close(); }
+
+ NS_IMETHOD
+ IsNonBlocking(bool* aNonBlocking) override {
+ *aNonBlocking = false;
+ return NS_OK;
+ }
+
+ private:
+ ~BlockingSyncStream() = default;
+};
+
+NS_IMPL_ISUPPORTS(BlockingSyncStream, nsIInputStream)
+
+// Testing a simple blocking stream.
+TEST(TestInputStreamTransport, BlockingNotAsync)
+{
+ RefPtr<BlockingSyncStream> stream = new BlockingSyncStream("Hello world"_ns);
+
+ nsCOMPtr<nsIAsyncInputStream> ais;
+ CreateStream(stream.forget(), getter_AddRefs(ais));
+ ASSERT_TRUE(!!ais);
+
+ nsAutoCString data;
+ nsresult rv = NS_ReadInputStreamToString(ais, data, -1);
+ ASSERT_EQ(NS_OK, rv);
+
+ ASSERT_TRUE(data.EqualsLiteral("Hello world"));
+}
+
+class BlockingAsyncStream final : public nsIAsyncInputStream {
+ nsCOMPtr<nsIInputStream> mStream;
+ bool mPending;
+
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ explicit BlockingAsyncStream(const nsACString& aBuffer) : mPending(false) {
+ NS_NewCStringInputStream(getter_AddRefs(mStream), aBuffer);
+ }
+
+ NS_IMETHOD
+ Available(uint64_t* aLength) override {
+ mStream->Available(aLength);
+
+ // 1 char at the time, just to test the asyncWait+Read loop a bit more.
+ if (*aLength > 0) {
+ *aLength = 1;
+ }
+
+ return NS_OK;
+ }
+
+ NS_IMETHOD
+ StreamStatus() override { return mStream->StreamStatus(); }
+
+ NS_IMETHOD
+ Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount) override {
+ mPending = !mPending;
+ if (mPending) {
+ return NS_BASE_STREAM_WOULD_BLOCK;
+ }
+
+ // 1 char at the time, just to test the asyncWait+Read loop a bit more.
+ aCount = 1;
+
+ return mStream->Read(aBuffer, aCount, aReadCount);
+ }
+
+ NS_IMETHOD
+ ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, uint32_t aCount,
+ uint32_t* aResult) override {
+ mPending = !mPending;
+ if (mPending) {
+ return NS_BASE_STREAM_WOULD_BLOCK;
+ }
+
+ // 1 char at the time, just to test the asyncWait+Read loop a bit more.
+ aCount = 1;
+
+ return mStream->ReadSegments(aWriter, aClosure, aCount, aResult);
+ }
+
+ NS_IMETHOD
+ Close() override { return mStream->Close(); }
+
+ NS_IMETHOD
+ IsNonBlocking(bool* aNonBlocking) override {
+ *aNonBlocking = false;
+ return NS_OK;
+ }
+
+ NS_IMETHOD
+ CloseWithStatus(nsresult aStatus) override { return Close(); }
+
+ NS_IMETHOD
+ AsyncWait(nsIInputStreamCallback* aCallback, uint32_t aFlags,
+ uint32_t aRequestedCount, nsIEventTarget* aEventTarget) override {
+ if (!aCallback) {
+ return NS_OK;
+ }
+
+ RefPtr<BlockingAsyncStream> self = this;
+ nsCOMPtr<nsIInputStreamCallback> callback = aCallback;
+
+ nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+ "gtest-asyncwait",
+ [self, callback]() { callback->OnInputStreamReady(self); });
+
+ if (aEventTarget) {
+ aEventTarget->Dispatch(r.forget());
+ } else {
+ r->Run();
+ }
+
+ return NS_OK;
+ }
+
+ private:
+ ~BlockingAsyncStream() = default;
+};
+
+NS_IMPL_ISUPPORTS(BlockingAsyncStream, nsIInputStream, nsIAsyncInputStream)
+
+// Testing an async blocking stream.
+TEST(TestInputStreamTransport, BlockingAsync)
+{
+ RefPtr<BlockingAsyncStream> stream =
+ new BlockingAsyncStream("Hello world"_ns);
+
+ nsCOMPtr<nsIAsyncInputStream> ais;
+ CreateStream(stream.forget(), getter_AddRefs(ais));
+ ASSERT_TRUE(!!ais);
+
+ nsAutoCString data;
+ nsresult rv = NS_ReadInputStreamToString(ais, data, -1);
+ ASSERT_EQ(NS_OK, rv);
+
+ ASSERT_TRUE(data.EqualsLiteral("Hello world"));
+}
diff --git a/netwerk/test/gtest/TestIsValidIp.cpp b/netwerk/test/gtest/TestIsValidIp.cpp
new file mode 100644
index 0000000000..dfee8c5c0f
--- /dev/null
+++ b/netwerk/test/gtest/TestIsValidIp.cpp
@@ -0,0 +1,178 @@
+#include "gtest/MozGTestBench.h" // For MOZ_GTEST_BENCH
+#include "gtest/gtest.h"
+
+#include "nsURLHelper.h"
+
+TEST(TestIsValidIp, IPV4Localhost)
+{
+ constexpr auto ip = "127.0.0.1"_ns;
+ ASSERT_EQ(true, net_IsValidIPv4Addr(ip));
+}
+
+TEST(TestIsValidIp, IPV4Only0)
+{
+ constexpr auto ip = "0.0.0.0"_ns;
+ ASSERT_EQ(true, net_IsValidIPv4Addr(ip));
+}
+
+TEST(TestIsValidIp, IPV4Max)
+{
+ constexpr auto ip = "255.255.255.255"_ns;
+ ASSERT_EQ(true, net_IsValidIPv4Addr(ip));
+}
+
+TEST(TestIsValidIp, IPV4LeadingZero)
+{
+ constexpr auto ip = "055.225.255.255"_ns;
+ ASSERT_EQ(false, net_IsValidIPv4Addr(ip));
+
+ constexpr auto ip2 = "255.055.255.255"_ns;
+ ASSERT_EQ(false, net_IsValidIPv4Addr(ip2));
+
+ constexpr auto ip3 = "255.255.055.255"_ns;
+ ASSERT_EQ(false, net_IsValidIPv4Addr(ip3));
+
+ constexpr auto ip4 = "255.255.255.055"_ns;
+ ASSERT_EQ(false, net_IsValidIPv4Addr(ip4));
+}
+
+TEST(TestIsValidIp, IPV4StartWithADot)
+{
+ constexpr auto ip = ".192.168.120.197"_ns;
+ ASSERT_EQ(false, net_IsValidIPv4Addr(ip));
+}
+
+TEST(TestIsValidIp, IPV4StartWith4Digits)
+{
+ constexpr auto ip = "1927.168.120.197"_ns;
+ ASSERT_EQ(false, net_IsValidIPv4Addr(ip));
+}
+
+TEST(TestIsValidIp, IPV4OutOfRange)
+{
+ constexpr auto invalid1 = "421.168.120.124"_ns;
+ constexpr auto invalid2 = "192.997.120.124"_ns;
+ constexpr auto invalid3 = "192.168.300.124"_ns;
+ constexpr auto invalid4 = "192.168.120.256"_ns;
+
+ ASSERT_EQ(false, net_IsValidIPv4Addr(invalid1));
+ ASSERT_EQ(false, net_IsValidIPv4Addr(invalid2));
+ ASSERT_EQ(false, net_IsValidIPv4Addr(invalid3));
+ ASSERT_EQ(false, net_IsValidIPv4Addr(invalid4));
+}
+
+TEST(TestIsValidIp, IPV4EmptyDigits)
+{
+ constexpr auto invalid1 = "..0.0.0"_ns;
+ constexpr auto invalid2 = "127..0.0"_ns;
+ constexpr auto invalid3 = "127.0..0"_ns;
+ constexpr auto invalid4 = "127.0.0."_ns;
+
+ ASSERT_EQ(false, net_IsValidIPv4Addr(invalid1));
+ ASSERT_EQ(false, net_IsValidIPv4Addr(invalid2));
+ ASSERT_EQ(false, net_IsValidIPv4Addr(invalid3));
+ ASSERT_EQ(false, net_IsValidIPv4Addr(invalid4));
+}
+
+TEST(TestIsValidIp, IPV4NonNumeric)
+{
+ constexpr auto invalid1 = "127.0.0.f"_ns;
+ constexpr auto invalid2 = "127.0.0.!"_ns;
+ constexpr auto invalid3 = "127#0.0.1"_ns;
+
+ ASSERT_EQ(false, net_IsValidIPv4Addr(invalid1));
+ ASSERT_EQ(false, net_IsValidIPv4Addr(invalid2));
+ ASSERT_EQ(false, net_IsValidIPv4Addr(invalid3));
+}
+
+TEST(TestIsValidIp, IPV4TooManyDigits)
+{
+ constexpr auto ip = "127.0.0.1.2"_ns;
+ ASSERT_EQ(false, net_IsValidIPv4Addr(ip));
+}
+
+TEST(TestIsValidIp, IPV4TooFewDigits)
+{
+ constexpr auto ip = "127.0.1"_ns;
+ ASSERT_EQ(false, net_IsValidIPv4Addr(ip));
+}
+
+TEST(TestIsValidIp, IPV6WithIPV4Inside)
+{
+ constexpr auto ipv6 = "0123:4567:89ab:cdef:0123:4567:127.0.0.1"_ns;
+ ASSERT_EQ(true, net_IsValidIPv6Addr(ipv6));
+}
+
+TEST(TestIsValidIp, IPv6FullForm)
+{
+ constexpr auto ipv6 = "0123:4567:89ab:cdef:0123:4567:890a:bcde"_ns;
+ ASSERT_EQ(true, net_IsValidIPv6Addr(ipv6));
+}
+
+TEST(TestIsValidIp, IPv6TrimLeading0)
+{
+ constexpr auto ipv6 = "123:4567:0:0:123:4567:890a:bcde"_ns;
+ ASSERT_EQ(true, net_IsValidIPv6Addr(ipv6));
+}
+
+TEST(TestIsValidIp, IPv6Collapsed)
+{
+ constexpr auto ipv6 = "FF01::101"_ns;
+ ASSERT_EQ(true, net_IsValidIPv6Addr(ipv6));
+}
+
+TEST(TestIsValidIp, IPV6WithIPV4InsideCollapsed)
+{
+ constexpr auto ipv6 = "::FFFF:129.144.52.38"_ns;
+ ASSERT_EQ(true, net_IsValidIPv6Addr(ipv6));
+}
+
+TEST(TestIsValidIp, IPV6Localhost)
+{
+ constexpr auto ipv6 = "::1"_ns;
+ ASSERT_EQ(true, net_IsValidIPv6Addr(ipv6));
+}
+
+TEST(TestIsValidIp, IPV6LinkLocalPrefix)
+{
+ constexpr auto ipv6 = "fe80::"_ns;
+ ASSERT_EQ(true, net_IsValidIPv6Addr(ipv6));
+}
+
+TEST(TestIsValidIp, IPV6GlobalUnicastPrefix)
+{
+ constexpr auto ipv6 = "2001::"_ns;
+ ASSERT_EQ(true, net_IsValidIPv6Addr(ipv6));
+}
+
+TEST(TestIsValidIp, IPV6Unspecified)
+{
+ constexpr auto ipv6 = "::"_ns;
+ ASSERT_EQ(true, net_IsValidIPv6Addr(ipv6));
+}
+
+TEST(TestIsValidIp, IPV6InvalidIPV4Inside)
+{
+ constexpr auto ipv6 = "0123:4567:89ab:cdef:0123:4567:127.0."_ns;
+ ASSERT_EQ(false, net_IsValidIPv6Addr(ipv6));
+}
+
+TEST(TestIsValidIp, IPV6InvalidCharacters)
+{
+ constexpr auto ipv6 = "012g:4567:89ab:cdef:0123:4567:127.0.0.1"_ns;
+ ASSERT_EQ(false, net_IsValidIPv6Addr(ipv6));
+
+ constexpr auto ipv6pound = "0123:456#:89ab:cdef:0123:4567:127.0.0.1"_ns;
+ ASSERT_EQ(false, net_IsValidIPv6Addr(ipv6pound));
+}
+
+TEST(TestIsValidIp, IPV6TooManyCharacters)
+{
+ constexpr auto ipv6 = "0123:45671:89ab:cdef:0123:4567:127.0.0.1"_ns;
+ ASSERT_EQ(false, net_IsValidIPv6Addr(ipv6));
+}
+TEST(TestIsValidIp, IPV6DoubleDoubleDots)
+{
+ constexpr auto ipv6 = "0123::4567:890a::bcde:0123:4567"_ns;
+ ASSERT_EQ(false, net_IsValidIPv6Addr(ipv6));
+}
diff --git a/netwerk/test/gtest/TestLinkHeader.cpp b/netwerk/test/gtest/TestLinkHeader.cpp
new file mode 100644
index 0000000000..4da7002f87
--- /dev/null
+++ b/netwerk/test/gtest/TestLinkHeader.cpp
@@ -0,0 +1,356 @@
+/* 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 <ostream>
+
+#include "gtest/gtest-param-test.h"
+#include "gtest/gtest.h"
+
+#include "mozilla/gtest/MozAssertions.h"
+#include "nsNetUtil.h"
+
+using namespace mozilla::net;
+
+LinkHeader LinkHeaderSetAll(nsAString const& v) {
+ LinkHeader l;
+ l.mHref = v;
+ l.mRel = v;
+ l.mTitle = v;
+ l.mIntegrity = v;
+ l.mSrcset = v;
+ l.mSizes = v;
+ l.mType = v;
+ l.mMedia = v;
+ l.mAnchor = v;
+ l.mCrossOrigin = v;
+ l.mReferrerPolicy = v;
+ l.mAs = v;
+ l.mFetchPriority = v;
+ return l;
+}
+
+LinkHeader LinkHeaderSetTitle(nsAString const& v) {
+ LinkHeader l;
+ l.mHref = v;
+ l.mRel = v;
+ l.mTitle = v;
+ return l;
+}
+
+LinkHeader LinkHeaderSetMinimum(nsAString const& v) {
+ LinkHeader l;
+ l.mHref = v;
+ l.mRel = v;
+ return l;
+}
+
+void PrintTo(const nsTArray<LinkHeader>& aLinkHeaders, std::ostream* aOs) {
+ bool first = true;
+ for (const auto& header : aLinkHeaders) {
+ if (!first) {
+ *aOs << ", ";
+ }
+ first = false;
+
+ *aOs << "(mHref=" << header.mHref << ", "
+ << "mRel=" << header.mRel << ", "
+ << "mTitle=" << header.mTitle << ", "
+ << "mNonce=" << header.mNonce << ", "
+ << "mIntegrity=" << header.mIntegrity << ", "
+ << "mSrcset=" << header.mSrcset << ", "
+ << "mSizes=" << header.mSizes << ", "
+ << "mType=" << header.mType << ", "
+ << "mMedia=" << header.mMedia << ", "
+ << "mAnchor=" << header.mAnchor << ", "
+ << "mCrossOrigin=" << header.mCrossOrigin << ", "
+ << "mReferrerPolicy=" << header.mReferrerPolicy << ", "
+ << "mAs=" << header.mAs << ", "
+ << "mFetchPriority=" << header.mFetchPriority << ")";
+ }
+}
+
+TEST(TestLinkHeader, MultipleLinkHeaders)
+{
+ nsString link =
+ u"<a>; rel=a; title=a; integrity=a; imagesrcset=a; imagesizes=a; type=a; media=a; anchor=a; crossorigin=a; referrerpolicy=a; as=a; fetchpriority=a,"_ns
+ u"<b>; rel=b; title=b; integrity=b; imagesrcset=b; imagesizes=b; type=b; media=b; anchor=b; crossorigin=b; referrerpolicy=b; as=b; fetchpriority=b,"_ns
+ u"<c>; rel=c"_ns;
+
+ nsTArray<LinkHeader> linkHeaders = ParseLinkHeader(link);
+
+ nsTArray<LinkHeader> expected;
+ expected.AppendElement(LinkHeaderSetAll(u"a"_ns));
+ expected.AppendElement(LinkHeaderSetAll(u"b"_ns));
+ expected.AppendElement(LinkHeaderSetMinimum(u"c"_ns));
+
+ ASSERT_EQ(linkHeaders, expected);
+}
+
+// title* has to be tested separately
+TEST(TestLinkHeader, MultipleLinkHeadersTitleStar)
+{
+ nsString link =
+ u"<d>; rel=d; title*=UTF-8'de'd,"_ns
+ u"<e>; rel=e; title*=UTF-8'de'e; title=g,"_ns
+ u"<f>; rel=f"_ns;
+
+ nsTArray<LinkHeader> linkHeaders = ParseLinkHeader(link);
+
+ nsTArray<LinkHeader> expected;
+ expected.AppendElement(LinkHeaderSetTitle(u"d"_ns));
+ expected.AppendElement(LinkHeaderSetTitle(u"e"_ns));
+ expected.AppendElement(LinkHeaderSetMinimum(u"f"_ns));
+
+ ASSERT_EQ(linkHeaders, expected);
+}
+
+struct SimpleParseTestData {
+ nsString link;
+ bool valid;
+ nsString url;
+ nsString rel;
+ nsString as;
+ nsString fetchpriority;
+
+ friend void PrintTo(const SimpleParseTestData& aData, std::ostream* aOs) {
+ *aOs << "link=" << aData.link << ", valid=" << aData.valid
+ << ", url=" << aData.url << ", rel=" << aData.rel
+ << ", as=" << aData.as << ", fetchpriority=" << aData.fetchpriority
+ << ")";
+ }
+};
+
+class SimpleParseTest : public ::testing::TestWithParam<SimpleParseTestData> {};
+
+TEST_P(SimpleParseTest, Simple) {
+ const SimpleParseTestData test = GetParam();
+
+ nsTArray<LinkHeader> linkHeaders = ParseLinkHeader(test.link);
+
+ EXPECT_EQ(test.valid, !linkHeaders.IsEmpty());
+ if (test.valid) {
+ ASSERT_EQ(linkHeaders.Length(), (nsTArray<LinkHeader>::size_type)1);
+ EXPECT_EQ(test.url, linkHeaders[0].mHref);
+ EXPECT_EQ(test.rel, linkHeaders[0].mRel);
+ EXPECT_EQ(test.as, linkHeaders[0].mAs);
+ EXPECT_EQ(test.fetchpriority, linkHeaders[0].mFetchPriority);
+ }
+}
+
+// Some test data copied and adapted from
+// https://source.chromium.org/chromium/chromium/src/+/main:components/link_header_util/link_header_util_unittest.cc
+// the different behavior of the parser is commented above each test case.
+const SimpleParseTestData simple_parse_tests[] = {
+ {u"<s.css>; rel=stylesheet; fetchpriority=\"auto\""_ns, true, u"s.css"_ns,
+ u"stylesheet"_ns, u""_ns, u"auto"_ns},
+ {u"<s.css>; rel=stylesheet; fetchpriority=\"low\""_ns, true, u"s.css"_ns,
+ u"stylesheet"_ns, u""_ns, u"low"_ns},
+ {u"<s.css>; rel=stylesheet; fetchpriority=\"high\""_ns, true, u"s.css"_ns,
+ u"stylesheet"_ns, u""_ns, u"high"_ns},
+ {u"<s.css>; rel=stylesheet; fetchpriority=\"foo\""_ns, true, u"s.css"_ns,
+ u"stylesheet"_ns, u""_ns, u"foo"_ns},
+ {u"<s.css>; rel=stylesheet; fetchpriority=\"\""_ns, true, u"s.css"_ns,
+ u"stylesheet"_ns, u""_ns, u""_ns},
+ {u"<s.css>; rel=stylesheet; fetchpriority=fooWithoutDoubleQuotes"_ns, true,
+ u"s.css"_ns, u"stylesheet"_ns, u""_ns, u"fooWithoutDoubleQuotes"_ns},
+ {u"</images/cat.jpg>; rel=prefetch"_ns, true, u"/images/cat.jpg"_ns,
+ u"prefetch"_ns, u""_ns},
+ {u"</images/cat.jpg>;rel=prefetch"_ns, true, u"/images/cat.jpg"_ns,
+ u"prefetch"_ns, u""_ns},
+ {u"</images/cat.jpg> ;rel=prefetch"_ns, true, u"/images/cat.jpg"_ns,
+ u"prefetch"_ns, u""_ns},
+ {u"</images/cat.jpg> ; rel=prefetch"_ns, true, u"/images/cat.jpg"_ns,
+ u"prefetch"_ns, u""_ns},
+ {u"< /images/cat.jpg> ; rel=prefetch"_ns, true, u"/images/cat.jpg"_ns,
+ u"prefetch"_ns, u""_ns},
+ {u"</images/cat.jpg > ; rel=prefetch"_ns, true, u"/images/cat.jpg"_ns,
+ u"prefetch"_ns, u""_ns},
+ // TODO(1744051): don't ignore spaces in href
+ // {u"</images/cat.jpg wutwut> ; rel=prefetch"_ns, true,
+ // u"/images/cat.jpg wutwut"_ns, u"prefetch"_ns, u""_ns},
+ {u"</images/cat.jpg wutwut> ; rel=prefetch"_ns, true,
+ u"/images/cat.jpgwutwut"_ns, u"prefetch"_ns, u""_ns},
+ // TODO(1744051): don't ignore spaces in href
+ // {u"</images/cat.jpg wutwut \t > ; rel=prefetch"_ns, true,
+ // u"/images/cat.jpg wutwut"_ns, u"prefetch"_ns, u""_ns},
+ {u"</images/cat.jpg wutwut \t > ; rel=prefetch"_ns, true,
+ u"/images/cat.jpgwutwut"_ns, u"prefetch"_ns, u""_ns},
+ {u"</images/cat.jpg>; rel=prefetch "_ns, true, u"/images/cat.jpg"_ns,
+ u"prefetch"_ns, u""_ns},
+ {u"</images/cat.jpg>; Rel=prefetch "_ns, true, u"/images/cat.jpg"_ns,
+ u"prefetch"_ns, u""_ns},
+ {u"</images/cat.jpg>; Rel=PReFetCh "_ns, true, u"/images/cat.jpg"_ns,
+ u"PReFetCh"_ns, u""_ns},
+ {u"</images/cat.jpg>; rel=prefetch; rel=somethingelse"_ns, true,
+ u"/images/cat.jpg"_ns, u"prefetch"_ns, u""_ns},
+ {u"</images/cat.jpg>\t\t ; \trel=prefetch \t "_ns, true,
+ u"/images/cat.jpg"_ns, u"prefetch"_ns, u""_ns},
+ {u"</images/cat.jpg>; rel= prefetch"_ns, true, u"/images/cat.jpg"_ns,
+ u"prefetch"_ns, u""_ns},
+ {u"<../images/cat.jpg?dog>; rel= prefetch"_ns, true,
+ u"../images/cat.jpg?dog"_ns, u"prefetch"_ns, u""_ns},
+ {u"</images/cat.jpg>; rel =prefetch"_ns, true, u"/images/cat.jpg"_ns,
+ u"prefetch"_ns, u""_ns},
+ {u"</images/cat.jpg>; rel pel=prefetch"_ns, false},
+ // different from chromium test case, because we already check for
+ // existence of "rel" parameter
+ {u"< /images/cat.jpg>"_ns, false},
+ {u"</images/cat.jpg>; wut=sup; rel =prefetch"_ns, true,
+ u"/images/cat.jpg"_ns, u"prefetch"_ns, u""_ns},
+ {u"</images/cat.jpg>; wut=sup ; rel =prefetch"_ns, true,
+ u"/images/cat.jpg"_ns, u"prefetch"_ns, u""_ns},
+ {u"</images/cat.jpg>; wut=sup ; rel =prefetch \t ;"_ns, true,
+ u"/images/cat.jpg"_ns, u"prefetch"_ns, u""_ns},
+ // TODO(1744051): forbid non-whitespace characters between '>' and the first
+ // semicolon making it conform RFC 8288 Sec 3
+ // {u"</images/cat.jpg> wut=sup ; rel =prefetch \t ;"_ns, false},
+ {u"</images/cat.jpg> wut=sup ; rel =prefetch \t ;"_ns, true,
+ u"/images/cat.jpg"_ns, u"prefetch"_ns, u""_ns},
+ {u"< /images/cat.jpg"_ns, false},
+ // TODO(1744051): don't ignore spaces in href
+ // {u"< http://wut.com/ sdfsdf ?sd>; rel=dns-prefetch"_ns, true,
+ // u"http://wut.com/ sdfsdf ?sd"_ns, u"dns-prefetch"_ns, u""_ns},
+ {u"< http://wut.com/ sdfsdf ?sd>; rel=dns-prefetch"_ns, true,
+ u"http://wut.com/sdfsdf?sd"_ns, u"dns-prefetch"_ns, u""_ns},
+ {u"< http://wut.com/%20%20%3dsdfsdf?sd>; rel=dns-prefetch"_ns, true,
+ u"http://wut.com/%20%20%3dsdfsdf?sd"_ns, u"dns-prefetch"_ns, u""_ns},
+ {u"< http://wut.com/dfsdf?sdf=ghj&wer=rty>; rel=prefetch"_ns, true,
+ u"http://wut.com/dfsdf?sdf=ghj&wer=rty"_ns, u"prefetch"_ns, u""_ns},
+ {u"< http://wut.com/dfsdf?sdf=ghj&wer=rty>;;;;; rel=prefetch"_ns, true,
+ u"http://wut.com/dfsdf?sdf=ghj&wer=rty"_ns, u"prefetch"_ns, u""_ns},
+ {u"< http://wut.com/%20%20%3dsdfsdf?sd>; rel=preload;as=image"_ns, true,
+ u"http://wut.com/%20%20%3dsdfsdf?sd"_ns, u"preload"_ns, u"image"_ns},
+ {u"< http://wut.com/%20%20%3dsdfsdf?sd>; rel=preload;as=whatever"_ns,
+ true, u"http://wut.com/%20%20%3dsdfsdf?sd"_ns, u"preload"_ns,
+ u"whatever"_ns},
+ {u"</images/cat.jpg>; rel=prefetch;"_ns, true, u"/images/cat.jpg"_ns,
+ u"prefetch"_ns, u""_ns},
+ {u"</images/cat.jpg>; rel=prefetch ;"_ns, true, u"/images/cat.jpg"_ns,
+ u"prefetch"_ns, u""_ns},
+ {u"</images/ca,t.jpg>; rel=prefetch ;"_ns, true, u"/images/ca,t.jpg"_ns,
+ u"prefetch"_ns, u""_ns},
+ {u"<simple.css>; rel=stylesheet; title=\"title with a DQUOTE and "
+ "backslash\""_ns,
+ true, u"simple.css"_ns, u"stylesheet"_ns, u""_ns},
+ // TODO(1744051): forbid missing end quote
+ // {u"<simple.css>; rel=stylesheet; title=\"title with a DQUOTE \\\" and "
+ // "backslash: \\\""_ns, false},
+ {u"<simple.css>; rel=stylesheet; title=\"title with a DQUOTE \\\" and backslash: \\\""_ns,
+ true, u"simple.css"_ns, u"stylesheet"_ns, u""_ns},
+ {u"<simple.css>; title=\"title with a DQUOTE \\\" and backslash: \"; "
+ "rel=stylesheet; "_ns,
+ true, u"simple.css"_ns, u"stylesheet"_ns, u""_ns},
+ {u"<simple.css>; title=\'title with a DQUOTE \\\' and backslash: \'; "
+ "rel=stylesheet; "_ns,
+ true, u"simple.css"_ns, u"stylesheet"_ns, u""_ns},
+ {u"<simple.css>; title=\"title with a DQUOTE \\\" and ;backslash,: \"; "
+ "rel=stylesheet; "_ns,
+ true, u"simple.css"_ns, u"stylesheet"_ns, u""_ns},
+ {u"<simple.css>; title=\"title with a DQUOTE \' and ;backslash,: \"; "
+ "rel=stylesheet; "_ns,
+ true, u"simple.css"_ns, u"stylesheet"_ns, u""_ns},
+ {u"<simple.css>; title=\"\"; rel=stylesheet; "_ns, true, u"simple.css"_ns,
+ u"stylesheet"_ns, u""_ns},
+ {u"<simple.css>; title=\"\"; rel=\"stylesheet\"; "_ns, true,
+ u"simple.css"_ns, u"stylesheet"_ns, u""_ns},
+ // TODO(1744051): forbid missing end quote
+ // {u"<simple.css>; rel=stylesheet; title=\""_ns, false},
+ {u"<simple.css>; rel=stylesheet; title=\""_ns, true, u"simple.css"_ns,
+ u"stylesheet"_ns, u""_ns},
+ {u"<simple.css>; rel=stylesheet; title=\"\""_ns, true, u"simple.css"_ns,
+ u"stylesheet"_ns, u""_ns},
+ // TODO(1744051): forbid missing end quote
+ // {u"<simple.css>; rel=\"stylesheet\"; title=\""_ns, false},
+ {u"<simple.css>; rel=\"stylesheet\"; title=\""_ns, true, u"simple.css"_ns,
+ u"stylesheet"_ns, u""_ns},
+ // TODO(1744051): forbid missing end quote
+ // {u"<simple.css>; rel=\";style,sheet\"; title=\""_ns, false},
+ {u"<simple.css>; rel=\";style,sheet\"; title=\""_ns, true, u"simple.css"_ns,
+ u";style,sheet"_ns, u""_ns},
+ // TODO(1744051): forbid missing end quote
+ // {u"<simple.css>; rel=\"bla'sdf\"; title=\""_ns, false}
+ {u"<simple.css>; rel=\"bla'sdf\"; title=\""_ns, true, u"simple.css"_ns,
+ u"bla'sdf"_ns, u""_ns},
+ // TODO(1744051): allow explicit empty rel
+ // {u"<simple.css>; rel=\"\"; title=\"\""_ns, true, u"simple.css"_ns,
+ // u""_ns, u""_ns}
+ {u"<simple.css>; rel=\"\"; title=\"\""_ns, false},
+ {u"<simple.css>; rel=''; title=\"\""_ns, true, u"simple.css"_ns, u"''"_ns,
+ u""_ns},
+ {u"<simple.css>; rel=''; bla"_ns, true, u"simple.css"_ns, u"''"_ns, u""_ns},
+ {u"<simple.css>; rel='prefetch"_ns, true, u"simple.css"_ns, u"'prefetch"_ns,
+ u""_ns},
+ // TODO(1744051): forbid missing end quote
+ // {u"<simple.css>; rel=\"prefetch"_ns, false},
+ {u"<simple.css>; rel=\"prefetch"_ns, true, u"simple.css"_ns,
+ u"\"prefetch"_ns, u""_ns},
+ {u"<simple.css>; rel=\""_ns, false},
+ {u"simple.css; rel=prefetch"_ns, false},
+ {u"<simple.css>; rel=prefetch; rel=foobar"_ns, true, u"simple.css"_ns,
+ u"prefetch"_ns, u""_ns},
+};
+
+INSTANTIATE_TEST_SUITE_P(TestLinkHeader, SimpleParseTest,
+ testing::ValuesIn(simple_parse_tests));
+
+// Test anchor
+
+struct AnchorTestData {
+ nsString baseURI;
+ // building the new anchor in combination with the baseURI
+ nsString anchor;
+ nsString href;
+ const char* resolved;
+};
+
+class AnchorTest : public ::testing::TestWithParam<AnchorTestData> {};
+
+const AnchorTestData anchor_tests[] = {
+ {u"http://example.com/path/to/index.html"_ns, u""_ns, u"page.html"_ns,
+ "http://example.com/path/to/page.html"},
+ {u"http://example.com/path/to/index.html"_ns,
+ u"http://example.com/path/"_ns, u"page.html"_ns,
+ "http://example.com/path/page.html"},
+ {u"http://example.com/path/to/index.html"_ns,
+ u"http://example.com/path/"_ns, u"/page.html"_ns,
+ "http://example.com/page.html"},
+ {u"http://example.com/path/to/index.html"_ns, u".."_ns, u"page.html"_ns,
+ "http://example.com/path/page.html"},
+ {u"http://example.com/path/to/index.html"_ns, u".."_ns,
+ u"from/page.html"_ns, "http://example.com/path/from/page.html"},
+ {u"http://example.com/path/to/index.html"_ns, u"/hello/"_ns,
+ u"page.html"_ns, "http://example.com/hello/page.html"},
+ {u"http://example.com/path/to/index.html"_ns, u"/hello"_ns, u"page.html"_ns,
+ "http://example.com/page.html"},
+ {u"http://example.com/path/to/index.html"_ns, u"#necko"_ns, u"page.html"_ns,
+ "http://example.com/path/to/page.html"},
+ {u"http://example.com/path/to/index.html"_ns, u"https://example.net/"_ns,
+ u"to/page.html"_ns, "https://example.net/to/page.html"},
+};
+
+LinkHeader LinkHeaderFromHrefAndAnchor(nsAString const& aHref,
+ nsAString const& aAnchor) {
+ LinkHeader l;
+ l.mHref = aHref;
+ l.mAnchor = aAnchor;
+ return l;
+}
+
+TEST_P(AnchorTest, Anchor) {
+ const AnchorTestData test = GetParam();
+
+ LinkHeader linkHeader = LinkHeaderFromHrefAndAnchor(test.href, test.anchor);
+
+ nsCOMPtr<nsIURI> baseURI;
+ ASSERT_NS_SUCCEEDED(NS_NewURI(getter_AddRefs(baseURI), test.baseURI));
+
+ nsCOMPtr<nsIURI> resolved;
+ ASSERT_TRUE(NS_SUCCEEDED(
+ linkHeader.NewResolveHref(getter_AddRefs(resolved), baseURI)));
+
+ ASSERT_STREQ(resolved->GetSpecOrDefault().get(), test.resolved);
+}
+
+INSTANTIATE_TEST_SUITE_P(TestLinkHeader, AnchorTest,
+ testing::ValuesIn(anchor_tests));
diff --git a/netwerk/test/gtest/TestMIMEInputStream.cpp b/netwerk/test/gtest/TestMIMEInputStream.cpp
new file mode 100644
index 0000000000..a2f3f7a43d
--- /dev/null
+++ b/netwerk/test/gtest/TestMIMEInputStream.cpp
@@ -0,0 +1,268 @@
+#include "gtest/gtest.h"
+
+#include "Helpers.h"
+#include "mozilla/SpinEventLoopUntil.h"
+#include "nsComponentManagerUtils.h"
+#include "nsCOMPtr.h"
+#include "nsStreamUtils.h"
+#include "nsString.h"
+#include "nsStringStream.h"
+#include "nsIMIMEInputStream.h"
+#include "nsISeekableStream.h"
+
+using mozilla::GetCurrentSerialEventTarget;
+using mozilla::SpinEventLoopUntil;
+
+namespace {
+
+class SeekableLengthInputStream final : public testing::LengthInputStream,
+ public nsISeekableStream {
+ public:
+ SeekableLengthInputStream(const nsACString& aBuffer,
+ bool aIsInputStreamLength,
+ bool aIsAsyncInputStreamLength,
+ nsresult aLengthRv = NS_OK,
+ bool aNegativeValue = false)
+ : testing::LengthInputStream(aBuffer, aIsInputStreamLength,
+ aIsAsyncInputStreamLength, aLengthRv,
+ aNegativeValue) {}
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ NS_IMETHOD
+ Seek(int32_t aWhence, int64_t aOffset) override {
+ MOZ_CRASH("This method should not be called.");
+ return NS_ERROR_FAILURE;
+ }
+
+ NS_IMETHOD
+ Tell(int64_t* aResult) override {
+ MOZ_CRASH("This method should not be called.");
+ return NS_ERROR_FAILURE;
+ }
+
+ NS_IMETHOD
+ SetEOF() override {
+ MOZ_CRASH("This method should not be called.");
+ return NS_ERROR_FAILURE;
+ }
+
+ private:
+ ~SeekableLengthInputStream() = default;
+};
+
+NS_IMPL_ISUPPORTS_INHERITED(SeekableLengthInputStream,
+ testing::LengthInputStream, nsISeekableStream)
+
+} // namespace
+
+// nsIInputStreamLength && nsIAsyncInputStreamLength
+
+TEST(TestNsMIMEInputStream, QIInputStreamLength)
+{
+ nsCString buf;
+ buf.AssignLiteral("Hello world");
+
+ for (int i = 0; i < 4; i++) {
+ nsCOMPtr<nsIInputStream> mis;
+ {
+ RefPtr<SeekableLengthInputStream> stream =
+ new SeekableLengthInputStream(buf, i % 2, i > 1);
+
+ nsresult rv;
+ nsCOMPtr<nsIMIMEInputStream> m(
+ do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv));
+ ASSERT_EQ(NS_OK, rv);
+
+ rv = m->SetData(stream);
+ ASSERT_EQ(NS_OK, rv);
+
+ mis = m;
+ ASSERT_TRUE(!!mis);
+ }
+
+ {
+ nsCOMPtr<nsIInputStreamLength> qi = do_QueryInterface(mis);
+ ASSERT_EQ(!!(i % 2), !!qi);
+ }
+
+ {
+ nsCOMPtr<nsIAsyncInputStreamLength> qi = do_QueryInterface(mis);
+ ASSERT_EQ(i > 1, !!qi);
+ }
+ }
+}
+
+TEST(TestNsMIMEInputStream, InputStreamLength)
+{
+ nsCString buf;
+ buf.AssignLiteral("Hello world");
+
+ nsCOMPtr<nsIInputStream> mis;
+ {
+ RefPtr<SeekableLengthInputStream> stream =
+ new SeekableLengthInputStream(buf, true, false);
+
+ nsresult rv;
+ nsCOMPtr<nsIMIMEInputStream> m(
+ do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv));
+ ASSERT_EQ(NS_OK, rv);
+
+ rv = m->SetData(stream);
+ ASSERT_EQ(NS_OK, rv);
+
+ mis = m;
+ ASSERT_TRUE(!!mis);
+ }
+
+ nsCOMPtr<nsIInputStreamLength> qi = do_QueryInterface(mis);
+ ASSERT_TRUE(!!qi);
+
+ int64_t size;
+ nsresult rv = qi->Length(&size);
+ ASSERT_EQ(NS_OK, rv);
+ ASSERT_EQ(int64_t(buf.Length()), size);
+}
+
+TEST(TestNsMIMEInputStream, NegativeInputStreamLength)
+{
+ nsCString buf;
+ buf.AssignLiteral("Hello world");
+
+ nsCOMPtr<nsIInputStream> mis;
+ {
+ RefPtr<SeekableLengthInputStream> stream =
+ new SeekableLengthInputStream(buf, true, false, NS_OK, true);
+
+ nsresult rv;
+ nsCOMPtr<nsIMIMEInputStream> m(
+ do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv));
+ ASSERT_EQ(NS_OK, rv);
+
+ rv = m->SetData(stream);
+ ASSERT_EQ(NS_OK, rv);
+
+ mis = m;
+ ASSERT_TRUE(!!mis);
+ }
+
+ nsCOMPtr<nsIInputStreamLength> qi = do_QueryInterface(mis);
+ ASSERT_TRUE(!!qi);
+
+ int64_t size;
+ nsresult rv = qi->Length(&size);
+ ASSERT_EQ(NS_OK, rv);
+ ASSERT_EQ(-1, size);
+}
+
+TEST(TestNsMIMEInputStream, AsyncInputStreamLength)
+{
+ nsCString buf;
+ buf.AssignLiteral("Hello world");
+
+ nsCOMPtr<nsIInputStream> mis;
+ {
+ RefPtr<SeekableLengthInputStream> stream =
+ new SeekableLengthInputStream(buf, false, true);
+
+ nsresult rv;
+ nsCOMPtr<nsIMIMEInputStream> m(
+ do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv));
+ ASSERT_EQ(NS_OK, rv);
+
+ rv = m->SetData(stream);
+ ASSERT_EQ(NS_OK, rv);
+
+ mis = m;
+ ASSERT_TRUE(!!mis);
+ }
+
+ nsCOMPtr<nsIAsyncInputStreamLength> qi = do_QueryInterface(mis);
+ ASSERT_TRUE(!!qi);
+
+ RefPtr<testing::LengthCallback> callback = new testing::LengthCallback();
+
+ nsresult rv = qi->AsyncLengthWait(callback, GetCurrentSerialEventTarget());
+ ASSERT_EQ(NS_OK, rv);
+
+ MOZ_ALWAYS_TRUE(SpinEventLoopUntil(
+ "TEST(TestNsMIMEInputStream, AsyncInputStreamLength)"_ns,
+ [&]() { return callback->Called(); }));
+ ASSERT_EQ(int64_t(buf.Length()), callback->Size());
+}
+
+TEST(TestNsMIMEInputStream, NegativeAsyncInputStreamLength)
+{
+ nsCString buf;
+ buf.AssignLiteral("Hello world");
+
+ nsCOMPtr<nsIInputStream> mis;
+ {
+ RefPtr<SeekableLengthInputStream> stream =
+ new SeekableLengthInputStream(buf, false, true, NS_OK, true);
+
+ nsresult rv;
+ nsCOMPtr<nsIMIMEInputStream> m(
+ do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv));
+ ASSERT_EQ(NS_OK, rv);
+
+ rv = m->SetData(stream);
+ ASSERT_EQ(NS_OK, rv);
+
+ mis = m;
+ ASSERT_TRUE(!!mis);
+ }
+
+ nsCOMPtr<nsIAsyncInputStreamLength> qi = do_QueryInterface(mis);
+ ASSERT_TRUE(!!qi);
+
+ RefPtr<testing::LengthCallback> callback = new testing::LengthCallback();
+
+ nsresult rv = qi->AsyncLengthWait(callback, GetCurrentSerialEventTarget());
+ ASSERT_EQ(NS_OK, rv);
+
+ MOZ_ALWAYS_TRUE(SpinEventLoopUntil(
+ "TEST(TestNsMIMEInputStream, NegativeAsyncInputStreamLength)"_ns,
+ [&]() { return callback->Called(); }));
+ ASSERT_EQ(-1, callback->Size());
+}
+
+TEST(TestNsMIMEInputStream, AbortLengthCallback)
+{
+ nsCString buf;
+ buf.AssignLiteral("Hello world");
+
+ nsCOMPtr<nsIInputStream> mis;
+ {
+ RefPtr<SeekableLengthInputStream> stream =
+ new SeekableLengthInputStream(buf, false, true, NS_OK, true);
+
+ nsresult rv;
+ nsCOMPtr<nsIMIMEInputStream> m(
+ do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv));
+ ASSERT_EQ(NS_OK, rv);
+
+ rv = m->SetData(stream);
+ ASSERT_EQ(NS_OK, rv);
+
+ mis = m;
+ ASSERT_TRUE(!!mis);
+ }
+
+ nsCOMPtr<nsIAsyncInputStreamLength> qi = do_QueryInterface(mis);
+ ASSERT_TRUE(!!qi);
+
+ RefPtr<testing::LengthCallback> callback1 = new testing::LengthCallback();
+ nsresult rv = qi->AsyncLengthWait(callback1, GetCurrentSerialEventTarget());
+ ASSERT_EQ(NS_OK, rv);
+
+ RefPtr<testing::LengthCallback> callback2 = new testing::LengthCallback();
+ rv = qi->AsyncLengthWait(callback2, GetCurrentSerialEventTarget());
+ ASSERT_EQ(NS_OK, rv);
+
+ MOZ_ALWAYS_TRUE(
+ SpinEventLoopUntil("TEST(TestNsMIMEInputStream, AbortLengthCallback)"_ns,
+ [&]() { return callback2->Called(); }));
+ ASSERT_TRUE(!callback1->Called());
+ ASSERT_EQ(-1, callback2->Size());
+}
diff --git a/netwerk/test/gtest/TestMozURL.cpp b/netwerk/test/gtest/TestMozURL.cpp
new file mode 100644
index 0000000000..826adb241d
--- /dev/null
+++ b/netwerk/test/gtest/TestMozURL.cpp
@@ -0,0 +1,392 @@
+#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"));
+}
+
+TEST(TestMozURL, BaseDomain)
+{
+ 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;
+ ASSERT_EQ(url->BaseDomain(out), NS_OK);
+ ASSERT_TRUE(out.EqualsLiteral("example.net"));
+
+ RefPtr<MozURL> url2;
+ ASSERT_EQ(MozURL::Init(getter_AddRefs(url2), "file:///tmp/foo"_ns), NS_OK);
+ ASSERT_EQ(url2->BaseDomain(out), NS_OK);
+ ASSERT_TRUE(out.EqualsLiteral("/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);
+ ASSERT_EQ(url3->BaseDomain(out), NS_OK);
+ ASSERT_TRUE(out.EqualsLiteral("53711a8f-65ed-e742-9671-1f02e267c0bc"));
+
+ RefPtr<MozURL> url4;
+ ASSERT_EQ(MozURL::Init(getter_AddRefs(url4), "resource://foo/bar.html"_ns),
+ NS_OK);
+ ASSERT_EQ(url4->BaseDomain(out), NS_OK);
+ ASSERT_TRUE(out.EqualsLiteral("foo"));
+
+ RefPtr<MozURL> url5;
+ ASSERT_EQ(MozURL::Init(getter_AddRefs(url5), "about:home"_ns), NS_OK);
+ ASSERT_EQ(url5->BaseDomain(out), NS_OK);
+ 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;
+}
+
+bool IsUUID(const nsACString& aString) {
+ if (!IsAscii(aString)) {
+ return false;
+ }
+
+ std::regex pattern(
+ "^\\{[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89ABab"
+ "][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}\\}$");
+ return regex_match(nsCString(aString).get(), pattern);
+}
+
+bool BaseDomainsEqual(const nsACString& aBaseDomain1,
+ const nsACString& aBaseDomain2) {
+ if (IsUUID(aBaseDomain1) && IsUUID(aBaseDomain2)) {
+ return true;
+ }
+ return aBaseDomain1 == aBaseDomain2;
+}
+
+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);
+
+ bool baseDomainSucceeded = NS_SUCCEEDED(rv);
+
+ 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));
+
+ nsCString baseDomain2;
+ rv = url->BaseDomain(baseDomain2);
+
+ bool baseDomain2Succeeded = NS_SUCCEEDED(rv);
+
+ EXPECT_TRUE(baseDomainSucceeded == baseDomain2Succeeded);
+
+ if (baseDomainSucceeded) {
+ EXPECT_TRUE(BaseDomainsEqual(baseDomain, baseDomain2));
+ }
+}
+
+} // 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));
+ }
+}
diff --git a/netwerk/test/gtest/TestNamedPipeService.cpp b/netwerk/test/gtest/TestNamedPipeService.cpp
new file mode 100644
index 0000000000..b91a17a93e
--- /dev/null
+++ b/netwerk/test/gtest/TestNamedPipeService.cpp
@@ -0,0 +1,281 @@
+/* -*- 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 "TestCommon.h"
+#include "gtest/gtest.h"
+
+#include <windows.h>
+
+#include "mozilla/Atomics.h"
+#include "mozilla/gtest/MozAssertions.h"
+#include "mozilla/Monitor.h"
+#include "nsNamedPipeService.h"
+#include "nsNetCID.h"
+
+#define PIPE_NAME L"\\\\.\\pipe\\TestNPS"
+#define TEST_STR "Hello World"
+
+using namespace mozilla;
+
+/**
+ * Unlike a monitor, an event allows a thread to wait on another thread
+ * completing an action without regard to ordering of the wait and the notify.
+ */
+class Event {
+ public:
+ explicit Event(const char* aName) : mMonitor(aName) {}
+
+ ~Event() = default;
+
+ void Set() {
+ MonitorAutoLock lock(mMonitor);
+ MOZ_ASSERT(!mSignaled);
+ mSignaled = true;
+ mMonitor.Notify();
+ }
+ void Wait() {
+ MonitorAutoLock lock(mMonitor);
+ while (!mSignaled) {
+ lock.Wait();
+ }
+ mSignaled = false;
+ }
+
+ private:
+ Monitor mMonitor MOZ_UNANNOTATED;
+ bool mSignaled = false;
+};
+
+class nsNamedPipeDataObserver final : public nsINamedPipeDataObserver {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSINAMEDPIPEDATAOBSERVER
+
+ explicit nsNamedPipeDataObserver(HANDLE aPipe);
+
+ int Read(void* aBuffer, uint32_t aSize);
+ int Write(const void* aBuffer, uint32_t aSize);
+
+ uint32_t Transferred() const { return mBytesTransferred; }
+
+ private:
+ ~nsNamedPipeDataObserver() = default;
+
+ HANDLE mPipe;
+ OVERLAPPED mOverlapped;
+ Atomic<uint32_t> mBytesTransferred;
+ Event mEvent;
+};
+
+NS_IMPL_ISUPPORTS(nsNamedPipeDataObserver, nsINamedPipeDataObserver)
+
+nsNamedPipeDataObserver::nsNamedPipeDataObserver(HANDLE aPipe)
+ : mPipe(aPipe), mOverlapped(), mBytesTransferred(0), mEvent("named-pipe") {
+ mOverlapped.hEvent = CreateEventA(nullptr, TRUE, TRUE, "named-pipe");
+}
+
+int nsNamedPipeDataObserver::Read(void* aBuffer, uint32_t aSize) {
+ DWORD bytesRead = 0;
+ if (!ReadFile(mPipe, aBuffer, aSize, &bytesRead, &mOverlapped)) {
+ switch (GetLastError()) {
+ case ERROR_IO_PENDING: {
+ mEvent.Wait();
+ }
+ if (!GetOverlappedResult(mPipe, &mOverlapped, &bytesRead, FALSE)) {
+ ADD_FAILURE() << "GetOverlappedResult failed";
+ return -1;
+ }
+ if (mBytesTransferred != bytesRead) {
+ ADD_FAILURE() << "GetOverlappedResult mismatch";
+ return -1;
+ }
+
+ break;
+ default:
+ ADD_FAILURE() << "ReadFile error " << GetLastError();
+ return -1;
+ }
+ } else {
+ mEvent.Wait();
+
+ if (mBytesTransferred != bytesRead) {
+ ADD_FAILURE() << "GetOverlappedResult mismatch";
+ return -1;
+ }
+ }
+
+ mBytesTransferred = 0;
+ return bytesRead;
+}
+
+int nsNamedPipeDataObserver::Write(const void* aBuffer, uint32_t aSize) {
+ DWORD bytesWritten = 0;
+ if (!WriteFile(mPipe, aBuffer, aSize, &bytesWritten, &mOverlapped)) {
+ switch (GetLastError()) {
+ case ERROR_IO_PENDING: {
+ mEvent.Wait();
+ }
+ if (!GetOverlappedResult(mPipe, &mOverlapped, &bytesWritten, FALSE)) {
+ ADD_FAILURE() << "GetOverlappedResult failed";
+ return -1;
+ }
+ if (mBytesTransferred != bytesWritten) {
+ ADD_FAILURE() << "GetOverlappedResult mismatch";
+ return -1;
+ }
+
+ break;
+ default:
+ ADD_FAILURE() << "WriteFile error " << GetLastError();
+ return -1;
+ }
+ } else {
+ mEvent.Wait();
+
+ if (mBytesTransferred != bytesWritten) {
+ ADD_FAILURE() << "GetOverlappedResult mismatch";
+ return -1;
+ }
+ }
+
+ mBytesTransferred = 0;
+ return bytesWritten;
+}
+
+NS_IMETHODIMP
+nsNamedPipeDataObserver::OnDataAvailable(uint32_t aBytesTransferred,
+ void* aOverlapped) {
+ if (aOverlapped != &mOverlapped) {
+ ADD_FAILURE() << "invalid overlapped object";
+ return NS_ERROR_FAILURE;
+ }
+
+ DWORD bytesTransferred = 0;
+ BOOL ret =
+ GetOverlappedResult(mPipe, reinterpret_cast<LPOVERLAPPED>(aOverlapped),
+ &bytesTransferred, FALSE);
+
+ if (!ret) {
+ ADD_FAILURE() << "GetOverlappedResult failed";
+ return NS_ERROR_FAILURE;
+ }
+
+ if (bytesTransferred != aBytesTransferred) {
+ ADD_FAILURE() << "GetOverlappedResult mismatch";
+ return NS_ERROR_FAILURE;
+ }
+
+ mBytesTransferred += aBytesTransferred;
+ mEvent.Set();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNamedPipeDataObserver::OnError(uint32_t aError, void* aOverlapped) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+BOOL CreateAndConnectInstance(LPOVERLAPPED aOverlapped, LPHANDLE aPipe);
+BOOL ConnectToNewClient(HANDLE aPipe, LPOVERLAPPED aOverlapped);
+
+BOOL CreateAndConnectInstance(LPOVERLAPPED aOverlapped, LPHANDLE aPipe) {
+ // FIXME: adjust parameters
+ *aPipe =
+ CreateNamedPipeW(PIPE_NAME, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 1,
+ 65536, 65536, 3000, NULL);
+
+ if (*aPipe == INVALID_HANDLE_VALUE) {
+ ADD_FAILURE() << "CreateNamedPipe failed " << GetLastError();
+ return FALSE;
+ }
+
+ return ConnectToNewClient(*aPipe, aOverlapped);
+}
+
+BOOL ConnectToNewClient(HANDLE aPipe, LPOVERLAPPED aOverlapped) {
+ if (ConnectNamedPipe(aPipe, aOverlapped)) {
+ ADD_FAILURE()
+ << "Unexpected, overlapped ConnectNamedPipe() always returns 0.";
+ return FALSE;
+ }
+
+ switch (GetLastError()) {
+ case ERROR_IO_PENDING:
+ return TRUE;
+
+ case ERROR_PIPE_CONNECTED:
+ if (SetEvent(aOverlapped->hEvent)) break;
+
+ [[fallthrough]];
+ default: // error
+ ADD_FAILURE() << "ConnectNamedPipe failed " << GetLastError();
+ break;
+ }
+
+ return FALSE;
+}
+
+static nsresult CreateNamedPipe(LPHANDLE aServer, LPHANDLE aClient) {
+ OVERLAPPED overlapped;
+ overlapped.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+ BOOL ret;
+
+ ret = CreateAndConnectInstance(&overlapped, aServer);
+ if (!ret) {
+ ADD_FAILURE() << "pipe server should be pending";
+ return NS_ERROR_FAILURE;
+ }
+
+ *aClient = CreateFileW(PIPE_NAME, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
+ OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr);
+
+ if (*aClient == INVALID_HANDLE_VALUE) {
+ ADD_FAILURE() << "Unable to create pipe client";
+ CloseHandle(*aServer);
+ return NS_ERROR_FAILURE;
+ }
+
+ DWORD pipeMode = PIPE_READMODE_MESSAGE;
+ if (!SetNamedPipeHandleState(*aClient, &pipeMode, nullptr, nullptr)) {
+ ADD_FAILURE() << "SetNamedPipeHandleState error " << GetLastError();
+ CloseHandle(*aServer);
+ CloseHandle(*aClient);
+ return NS_ERROR_FAILURE;
+ }
+
+ WaitForSingleObjectEx(overlapped.hEvent, INFINITE, TRUE);
+
+ return NS_OK;
+}
+
+TEST(TestNamedPipeService, Test)
+{
+ nsCOMPtr<nsINamedPipeService> svc = net::NamedPipeService::GetOrCreate();
+
+ HANDLE readPipe, writePipe;
+ nsresult rv = CreateNamedPipe(&readPipe, &writePipe);
+ ASSERT_NS_SUCCEEDED(rv);
+
+ RefPtr<nsNamedPipeDataObserver> readObserver =
+ new nsNamedPipeDataObserver(readPipe);
+ RefPtr<nsNamedPipeDataObserver> writeObserver =
+ new nsNamedPipeDataObserver(writePipe);
+
+ ASSERT_NS_SUCCEEDED(svc->AddDataObserver(readPipe, readObserver));
+ ASSERT_NS_SUCCEEDED(svc->AddDataObserver(writePipe, writeObserver));
+ ASSERT_EQ(std::size_t(writeObserver->Write(TEST_STR, sizeof(TEST_STR))),
+ sizeof(TEST_STR));
+
+ char buffer[sizeof(TEST_STR)];
+ ASSERT_EQ(std::size_t(readObserver->Read(buffer, sizeof(buffer))),
+ sizeof(TEST_STR));
+ ASSERT_STREQ(buffer, TEST_STR) << "I/O mismatch";
+
+ ASSERT_NS_SUCCEEDED(svc->RemoveDataObserver(readPipe, readObserver));
+ ASSERT_NS_SUCCEEDED(svc->RemoveDataObserver(writePipe, writeObserver));
+}
diff --git a/netwerk/test/gtest/TestNetworkLinkIdHashingDarwin.cpp b/netwerk/test/gtest/TestNetworkLinkIdHashingDarwin.cpp
new file mode 100644
index 0000000000..a07c9438bd
--- /dev/null
+++ b/netwerk/test/gtest/TestNetworkLinkIdHashingDarwin.cpp
@@ -0,0 +1,93 @@
+#include <arpa/inet.h>
+
+#include "gtest/gtest.h"
+#include "mozilla/SHA1.h"
+#include "nsString.h"
+#include "nsPrintfCString.h"
+#include "mozilla/Logging.h"
+#include "nsNetworkLinkService.h"
+
+using namespace mozilla;
+
+in6_addr StringToSockAddr(const std::string& str) {
+ sockaddr_in6 ip;
+ inet_pton(AF_INET6, str.c_str(), &(ip.sin6_addr));
+ return ip.sin6_addr;
+}
+
+TEST(TestNetworkLinkIdHashingDarwin, Single)
+{
+ // Setup
+ SHA1Sum expected_sha1;
+ SHA1Sum::Hash expected_digest;
+
+ in6_addr a1 = StringToSockAddr("2001:db8:8714:3a91::1");
+
+ // Prefix
+ expected_sha1.update(&a1, sizeof(in6_addr));
+ // Netmask
+ expected_sha1.update(&a1, sizeof(in6_addr));
+ expected_sha1.finish(expected_digest);
+
+ std::vector<prefix_and_netmask> prefixNetmaskStore;
+ prefixNetmaskStore.push_back(std::make_pair(a1, a1));
+ SHA1Sum actual_sha1;
+ // Run
+ nsNetworkLinkService::HashSortedPrefixesAndNetmasks(prefixNetmaskStore,
+ &actual_sha1);
+ SHA1Sum::Hash actual_digest;
+ actual_sha1.finish(actual_digest);
+
+ // Assert
+ ASSERT_EQ(0, memcmp(&expected_digest, &actual_digest, sizeof(SHA1Sum::Hash)));
+}
+
+TEST(TestNetworkLinkIdHashingDarwin, Multiple)
+{
+ // Setup
+ SHA1Sum expected_sha1;
+ SHA1Sum::Hash expected_digest;
+
+ std::vector<in6_addr> addresses;
+ addresses.push_back(StringToSockAddr("2001:db8:8714:3a91::1"));
+ addresses.push_back(StringToSockAddr("2001:db8:8714:3a91::2"));
+ addresses.push_back(StringToSockAddr("2001:db8:8714:3a91::3"));
+ addresses.push_back(StringToSockAddr("2001:db8:8714:3a91::4"));
+
+ for (const auto& address : addresses) {
+ // Prefix
+ expected_sha1.update(&address, sizeof(in6_addr));
+ // Netmask
+ expected_sha1.update(&address, sizeof(in6_addr));
+ }
+ expected_sha1.finish(expected_digest);
+
+ // Ordered
+ std::vector<prefix_and_netmask> ordered;
+ for (const auto& address : addresses) {
+ ordered.push_back(std::make_pair(address, address));
+ }
+ SHA1Sum ordered_sha1;
+
+ // Unordered
+ std::vector<prefix_and_netmask> reversed;
+ for (auto it = addresses.rbegin(); it != addresses.rend(); ++it) {
+ reversed.push_back(std::make_pair(*it, *it));
+ }
+ SHA1Sum reversed_sha1;
+
+ // Run
+ nsNetworkLinkService::HashSortedPrefixesAndNetmasks(ordered, &ordered_sha1);
+ SHA1Sum::Hash ordered_digest;
+ ordered_sha1.finish(ordered_digest);
+
+ nsNetworkLinkService::HashSortedPrefixesAndNetmasks(reversed, &reversed_sha1);
+ SHA1Sum::Hash reversed_digest;
+ reversed_sha1.finish(reversed_digest);
+
+ // Assert
+ ASSERT_EQ(0,
+ memcmp(&expected_digest, &ordered_digest, sizeof(SHA1Sum::Hash)));
+ ASSERT_EQ(0,
+ memcmp(&expected_digest, &reversed_digest, sizeof(SHA1Sum::Hash)));
+}
diff --git a/netwerk/test/gtest/TestNetworkLinkIdHashingWindows.cpp b/netwerk/test/gtest/TestNetworkLinkIdHashingWindows.cpp
new file mode 100644
index 0000000000..eb2097d0a4
--- /dev/null
+++ b/netwerk/test/gtest/TestNetworkLinkIdHashingWindows.cpp
@@ -0,0 +1,88 @@
+#include <combaseapi.h>
+
+#include "gtest/gtest.h"
+#include "mozilla/SHA1.h"
+#include "nsNotifyAddrListener.h"
+
+using namespace mozilla;
+
+GUID StringToGuid(const std::string& str) {
+ GUID guid;
+ sscanf(str.c_str(),
+ "%8lx-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
+ &guid.Data1, &guid.Data2, &guid.Data3, &guid.Data4[0], &guid.Data4[1],
+ &guid.Data4[2], &guid.Data4[3], &guid.Data4[4], &guid.Data4[5],
+ &guid.Data4[6], &guid.Data4[7]);
+
+ return guid;
+}
+
+TEST(TestGuidHashWindows, Single)
+{
+ // Setup
+ SHA1Sum expected_sha1;
+ SHA1Sum::Hash expected_digest;
+
+ GUID g1 = StringToGuid("264555b1-289c-4494-83d1-e158d1d95115");
+
+ expected_sha1.update(&g1, sizeof(GUID));
+ expected_sha1.finish(expected_digest);
+
+ std::vector<GUID> nwGUIDS;
+ nwGUIDS.push_back(g1);
+ SHA1Sum actual_sha1;
+ // Run
+ nsNotifyAddrListener::HashSortedNetworkIds(nwGUIDS, actual_sha1);
+ SHA1Sum::Hash actual_digest;
+ actual_sha1.finish(actual_digest);
+
+ // Assert
+ ASSERT_EQ(0, memcmp(&expected_digest, &actual_digest, sizeof(SHA1Sum::Hash)));
+}
+
+TEST(TestNetworkLinkIdHashingWindows, Multiple)
+{
+ // Setup
+ SHA1Sum expected_sha1;
+ SHA1Sum::Hash expected_digest;
+
+ std::vector<GUID> nwGUIDS;
+ nwGUIDS.push_back(StringToGuid("00000000-0000-0000-0000-000000000001"));
+ nwGUIDS.push_back(StringToGuid("00000000-0000-0000-0000-000000000002"));
+ nwGUIDS.push_back(StringToGuid("00000000-0000-0000-0000-000000000003"));
+ nwGUIDS.push_back(StringToGuid("00000000-0000-0000-0000-000000000004"));
+
+ for (const auto& guid : nwGUIDS) {
+ expected_sha1.update(&guid, sizeof(GUID));
+ }
+ expected_sha1.finish(expected_digest);
+
+ // Ordered
+ std::vector<GUID> ordered;
+ for (const auto& guid : nwGUIDS) {
+ ordered.push_back(guid);
+ }
+ SHA1Sum ordered_sha1;
+
+ // Unordered
+ std::vector<GUID> reversed;
+ for (auto it = nwGUIDS.rbegin(); it != nwGUIDS.rend(); ++it) {
+ reversed.push_back(*it);
+ }
+ SHA1Sum reversed_sha1;
+
+ // Run
+ nsNotifyAddrListener::HashSortedNetworkIds(ordered, ordered_sha1);
+ SHA1Sum::Hash ordered_digest;
+ ordered_sha1.finish(ordered_digest);
+
+ nsNotifyAddrListener::HashSortedNetworkIds(reversed, reversed_sha1);
+ SHA1Sum::Hash reversed_digest;
+ reversed_sha1.finish(reversed_digest);
+
+ // Assert
+ ASSERT_EQ(0,
+ memcmp(&expected_digest, &ordered_digest, sizeof(SHA1Sum::Hash)));
+ ASSERT_EQ(0,
+ memcmp(&expected_digest, &reversed_digest, sizeof(SHA1Sum::Hash)));
+}
diff --git a/netwerk/test/gtest/TestPACMan.cpp b/netwerk/test/gtest/TestPACMan.cpp
new file mode 100644
index 0000000000..46c57cfc79
--- /dev/null
+++ b/netwerk/test/gtest/TestPACMan.cpp
@@ -0,0 +1,246 @@
+#include <utility>
+
+#include "gtest/gtest.h"
+#include "nsServiceManagerUtils.h"
+#include "../../../xpcom/threads/nsThreadManager.h"
+#include "nsIDHCPClient.h"
+#include "nsIPrefBranch.h"
+#include "nsComponentManager.h"
+#include "nsIPrefService.h"
+#include "nsNetCID.h"
+#include "mozilla/ModuleUtils.h"
+#include "mozilla/GenericFactory.h"
+#include "../../base/nsPACMan.h"
+
+#define TEST_WPAD_DHCP_OPTION "http://pac/pac.dat"
+#define TEST_ASSIGNED_PAC_URL "http://assignedpac/pac.dat"
+#define WPAD_PREF 4
+#define NETWORK_PROXY_TYPE_PREF_NAME "network.proxy.type"
+#define GETTING_NETWORK_PROXY_TYPE_FAILED (-1)
+
+nsCString WPADOptionResult;
+
+namespace mozilla {
+namespace net {
+
+nsresult SetNetworkProxyType(int32_t pref) {
+ nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+
+ if (!prefs) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+ return prefs->SetIntPref(NETWORK_PROXY_TYPE_PREF_NAME, pref);
+}
+
+nsresult GetNetworkProxyType(int32_t* pref) {
+ nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+
+ if (!prefs) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+ return prefs->GetIntPref(NETWORK_PROXY_TYPE_PREF_NAME, pref);
+}
+
+class nsTestDHCPClient final : public nsIDHCPClient {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIDHCPCLIENT
+
+ nsTestDHCPClient() = default;
+
+ nsresult Init() { return NS_OK; };
+
+ private:
+ ~nsTestDHCPClient() = default;
+};
+
+NS_IMETHODIMP
+nsTestDHCPClient::GetOption(uint8_t option, nsACString& _retval) {
+ _retval.Assign(WPADOptionResult);
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsTestDHCPClient, nsIDHCPClient)
+
+#define NS_TESTDHCPCLIENTSERVICE_CID /* {FEBF1D69-4D7D-4891-9524-045AD18B5593} \
+ */ \
+ { \
+ 0xFEBF1D69, 0x4D7D, 0x4891, { \
+ 0x95, 0x24, 0x04, 0x5a, 0xd1, 0x8b, 0x55, 0x93 \
+ } \
+ }
+
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsTestDHCPClient, Init)
+NS_DEFINE_NAMED_CID(NS_TESTDHCPCLIENTSERVICE_CID);
+
+void SetOptionResult(const char* result) { WPADOptionResult.Assign(result); }
+
+class ProcessPendingEventsAction final : public Runnable {
+ public:
+ ProcessPendingEventsAction() : Runnable("net::ProcessPendingEventsAction") {}
+
+ NS_IMETHOD
+ Run() override {
+ if (NS_HasPendingEvents(nullptr)) {
+ NS_WARNING("Found pending requests on PAC thread");
+ nsresult rv;
+ rv = NS_ProcessPendingEvents(nullptr);
+ EXPECT_EQ(NS_OK, rv);
+ }
+ NS_WARNING("No pending requests on PAC thread");
+ return NS_OK;
+ }
+};
+
+class TestPACMan : public ::testing::Test {
+ protected:
+ RefPtr<nsPACMan> mPACMan;
+
+ void ProcessAllEvents() {
+ ProcessPendingEventsOnPACThread();
+ nsresult rv;
+ while (NS_HasPendingEvents(nullptr)) {
+ NS_WARNING("Pending events on main thread");
+ rv = NS_ProcessPendingEvents(nullptr);
+ ASSERT_EQ(NS_OK, rv);
+ ProcessPendingEventsOnPACThread();
+ }
+ NS_WARNING("End of pending events on main thread");
+ }
+
+ // This method is used to ensure that all pending events on the main thread
+ // and the Proxy thread are processsed.
+ // It iterates over ProcessAllEvents because simply calling ProcessAllEvents
+ // once did not reliably process the events on both threads on all platforms.
+ void ProcessAllEventsTenTimes() {
+ for (int i = 0; i < 10; i++) {
+ ProcessAllEvents();
+ }
+ }
+
+ virtual void SetUp() {
+ ASSERT_EQ(NS_OK, GetNetworkProxyType(&originalNetworkProxyTypePref));
+ nsCOMPtr<nsIFactory> factory;
+ nsresult rv = nsComponentManagerImpl::gComponentManager->GetClassObject(
+ kNS_TESTDHCPCLIENTSERVICE_CID, NS_GET_IID(nsIFactory),
+ getter_AddRefs(factory));
+ if (NS_SUCCEEDED(rv) && factory) {
+ rv = nsComponentManagerImpl::gComponentManager->UnregisterFactory(
+ kNS_TESTDHCPCLIENTSERVICE_CID, factory);
+ ASSERT_EQ(NS_OK, rv);
+ }
+ factory = new mozilla::GenericFactory(nsTestDHCPClientConstructor);
+ nsComponentManagerImpl::gComponentManager->RegisterFactory(
+ kNS_TESTDHCPCLIENTSERVICE_CID, "nsTestDHCPClient",
+ NS_DHCPCLIENT_CONTRACTID, factory);
+
+ mPACMan = new nsPACMan(nullptr);
+ mPACMan->SetWPADOverDHCPEnabled(true);
+ mPACMan->Init(nullptr);
+ ASSERT_EQ(NS_OK, SetNetworkProxyType(WPAD_PREF));
+ }
+
+ virtual void TearDown() {
+ mPACMan->Shutdown();
+ if (originalNetworkProxyTypePref != GETTING_NETWORK_PROXY_TYPE_FAILED) {
+ ASSERT_EQ(NS_OK, SetNetworkProxyType(originalNetworkProxyTypePref));
+ }
+ }
+
+ nsCOMPtr<nsIDHCPClient> GetPACManDHCPCient() { return mPACMan->mDHCPClient; }
+
+ void SetPACManDHCPCient(nsCOMPtr<nsIDHCPClient> aValue) {
+ mPACMan->mDHCPClient = std::move(aValue);
+ }
+
+ void AssertPACSpecEqualTo(const char* aExpected) {
+ ASSERT_STREQ(aExpected, mPACMan->mPACURISpec.Data());
+ }
+
+ private:
+ int32_t originalNetworkProxyTypePref = GETTING_NETWORK_PROXY_TYPE_FAILED;
+
+ void ProcessPendingEventsOnPACThread() {
+ RefPtr<ProcessPendingEventsAction> action =
+ new ProcessPendingEventsAction();
+
+ mPACMan->DispatchToPAC(action.forget(), /*aSync =*/true);
+ }
+};
+
+TEST_F(TestPACMan, TestCreateDHCPClientAndGetOption) {
+ SetOptionResult(TEST_WPAD_DHCP_OPTION);
+ nsCString spec;
+
+ GetPACManDHCPCient()->GetOption(252, spec);
+
+ ASSERT_STREQ(TEST_WPAD_DHCP_OPTION, spec.Data());
+}
+
+TEST_F(TestPACMan, TestCreateDHCPClientAndGetEmptyOption) {
+ SetOptionResult("");
+ nsCString spec;
+ spec.AssignLiteral(TEST_ASSIGNED_PAC_URL);
+
+ GetPACManDHCPCient()->GetOption(252, spec);
+
+ ASSERT_TRUE(spec.IsEmpty());
+}
+
+TEST_F(TestPACMan,
+ WhenTheDHCPClientExistsAndDHCPIsNonEmptyDHCPOptionIsUsedAsPACUri) {
+ SetOptionResult(TEST_WPAD_DHCP_OPTION);
+
+ mPACMan->LoadPACFromURI(""_ns);
+ ProcessAllEventsTenTimes();
+
+ ASSERT_STREQ(TEST_WPAD_DHCP_OPTION, WPADOptionResult.Data());
+ AssertPACSpecEqualTo(TEST_WPAD_DHCP_OPTION);
+}
+
+TEST_F(TestPACMan, WhenTheDHCPResponseIsEmptyWPADDefaultsToStandardURL) {
+ SetOptionResult(""_ns.Data());
+
+ mPACMan->LoadPACFromURI(""_ns);
+ ASSERT_TRUE(NS_HasPendingEvents(nullptr));
+ ProcessAllEventsTenTimes();
+
+ ASSERT_STREQ("", WPADOptionResult.Data());
+ AssertPACSpecEqualTo("http://wpad/wpad.dat");
+}
+
+TEST_F(TestPACMan, WhenThereIsNoDHCPClientWPADDefaultsToStandardURL) {
+ SetOptionResult(TEST_WPAD_DHCP_OPTION);
+ SetPACManDHCPCient(nullptr);
+
+ mPACMan->LoadPACFromURI(""_ns);
+ ProcessAllEventsTenTimes();
+
+ ASSERT_STREQ(TEST_WPAD_DHCP_OPTION, WPADOptionResult.Data());
+ AssertPACSpecEqualTo("http://wpad/wpad.dat");
+}
+
+TEST_F(TestPACMan, WhenWPADOverDHCPIsPreffedOffWPADDefaultsToStandardURL) {
+ SetOptionResult(TEST_WPAD_DHCP_OPTION);
+ mPACMan->SetWPADOverDHCPEnabled(false);
+
+ mPACMan->LoadPACFromURI(""_ns);
+ ProcessAllEventsTenTimes();
+
+ ASSERT_STREQ(TEST_WPAD_DHCP_OPTION, WPADOptionResult.Data());
+ AssertPACSpecEqualTo("http://wpad/wpad.dat");
+}
+
+TEST_F(TestPACMan, WhenPACUriIsSetDirectlyItIsUsedRatherThanWPAD) {
+ SetOptionResult(TEST_WPAD_DHCP_OPTION);
+ nsCString spec;
+ spec.AssignLiteral(TEST_ASSIGNED_PAC_URL);
+
+ mPACMan->LoadPACFromURI(spec);
+ ProcessAllEventsTenTimes();
+
+ AssertPACSpecEqualTo(TEST_ASSIGNED_PAC_URL);
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/test/gtest/TestProtocolProxyService.cpp b/netwerk/test/gtest/TestProtocolProxyService.cpp
new file mode 100644
index 0000000000..a26f5f62a8
--- /dev/null
+++ b/netwerk/test/gtest/TestProtocolProxyService.cpp
@@ -0,0 +1,164 @@
+#include "gtest/gtest.h"
+
+#include "nsCOMPtr.h"
+#include "nsNetCID.h"
+#include "nsString.h"
+#include "nsComponentManagerUtils.h"
+#include "../../base/nsProtocolProxyService.h"
+#include "nsServiceManagerUtils.h"
+#include "mozilla/Preferences.h"
+#include "nsNetUtil.h"
+
+namespace mozilla {
+namespace net {
+
+TEST(TestProtocolProxyService, LoadHostFilters)
+{
+ nsCOMPtr<nsIProtocolProxyService2> ps =
+ do_GetService(NS_PROTOCOLPROXYSERVICE_CID);
+ ASSERT_TRUE(ps);
+ mozilla::net::nsProtocolProxyService* pps =
+ static_cast<mozilla::net::nsProtocolProxyService*>(ps.get());
+
+ nsCOMPtr<nsIURI> url;
+ nsAutoCString spec;
+
+ auto CheckLoopbackURLs = [&](bool expected) {
+ // loopback IPs are always filtered
+ spec = "http://127.0.0.1";
+ ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
+ ASSERT_EQ(pps->CanUseProxy(url, 80), expected);
+ spec = "http://[::1]";
+ ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
+ ASSERT_EQ(pps->CanUseProxy(url, 80), expected);
+ spec = "http://localhost";
+ ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
+ ASSERT_EQ(pps->CanUseProxy(url, 80), expected);
+ };
+
+ auto CheckURLs = [&](bool expected) {
+ spec = "http://example.com";
+ ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
+ ASSERT_EQ(pps->CanUseProxy(url, 80), expected);
+
+ spec = "https://10.2.3.4";
+ ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
+ ASSERT_EQ(pps->CanUseProxy(url, 443), expected);
+
+ spec = "http://1.2.3.4";
+ ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
+ ASSERT_EQ(pps->CanUseProxy(url, 80), expected);
+
+ spec = "http://1.2.3.4:8080";
+ ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
+ ASSERT_EQ(pps->CanUseProxy(url, 80), expected);
+
+ spec = "http://[2001::1]";
+ ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
+ ASSERT_EQ(pps->CanUseProxy(url, 80), expected);
+
+ spec = "http://2.3.4.5:7777";
+ ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
+ ASSERT_EQ(pps->CanUseProxy(url, 80), expected);
+
+ spec = "http://[abcd::2]:123";
+ ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
+ ASSERT_EQ(pps->CanUseProxy(url, 80), expected);
+
+ spec = "http://bla.test.com";
+ ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
+ ASSERT_EQ(pps->CanUseProxy(url, 80), expected);
+ };
+
+ auto CheckPortDomain = [&](bool expected) {
+ spec = "http://blabla.com:10";
+ ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
+ ASSERT_EQ(pps->CanUseProxy(url, 80), expected);
+ };
+
+ auto CheckLocalDomain = [&](bool expected) {
+ spec = "http://test";
+ ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
+ ASSERT_EQ(pps->CanUseProxy(url, 80), expected);
+ };
+
+ // --------------------------------------------------------------------------
+
+ nsAutoCString filter;
+
+ // Anything is allowed when there are no filters set
+ printf("Testing empty filter: %s\n", filter.get());
+ pps->LoadHostFilters(filter);
+
+ CheckLoopbackURLs(false);
+ CheckLocalDomain(true);
+ CheckURLs(true);
+ CheckPortDomain(true);
+
+ // --------------------------------------------------------------------------
+
+ filter =
+ "example.com, 1.2.3.4/16, [2001::1], 10.0.0.0/8, 2.3.0.0/16:7777, "
+ "[abcd::1]/64:123, *.test.com";
+ printf("Testing filter: %s\n", filter.get());
+ pps->LoadHostFilters(filter);
+
+ CheckLoopbackURLs(false);
+ // Check URLs can no longer use filtered proxy
+ CheckURLs(false);
+ CheckLocalDomain(true);
+ CheckPortDomain(true);
+
+ // --------------------------------------------------------------------------
+
+ // This is space separated. See bug 1346711 comment 4. We check this to keep
+ // backwards compatibility.
+ filter = "<local> blabla.com:10";
+ printf("Testing filter: %s\n", filter.get());
+ pps->LoadHostFilters(filter);
+
+ CheckLoopbackURLs(false);
+ CheckURLs(true);
+ CheckLocalDomain(false);
+ CheckPortDomain(false);
+
+ // Check that we don't crash on weird input
+ filter = "a b c abc:1x2, ,, * ** *.* *:10 :20 :40/12 */12:90";
+ printf("Testing filter: %s\n", filter.get());
+ pps->LoadHostFilters(filter);
+
+ // Check that filtering works properly when the filter is set to "<local>"
+ filter = "<local>";
+ printf("Testing filter: %s\n", filter.get());
+ pps->LoadHostFilters(filter);
+
+ CheckLoopbackURLs(false);
+ CheckURLs(true);
+ CheckLocalDomain(false);
+ CheckPortDomain(true);
+
+ // Check that allow_hijacking_localhost works with empty filter
+ Preferences::SetBool("network.proxy.allow_hijacking_localhost", true);
+
+ filter = "";
+ printf("Testing filter: %s\n", filter.get());
+ pps->LoadHostFilters(filter);
+
+ CheckLoopbackURLs(true);
+ CheckLocalDomain(true);
+ CheckURLs(true);
+ CheckPortDomain(true);
+
+ // Check that allow_hijacking_localhost works with non-trivial filter
+ filter = "127.0.0.1, [::1], localhost, blabla.com:10";
+ printf("Testing filter: %s\n", filter.get());
+ pps->LoadHostFilters(filter);
+
+ CheckLoopbackURLs(false);
+ CheckLocalDomain(true);
+ CheckURLs(true);
+ CheckPortDomain(false);
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/test/gtest/TestReadStreamToString.cpp b/netwerk/test/gtest/TestReadStreamToString.cpp
new file mode 100644
index 0000000000..f5fa0a4979
--- /dev/null
+++ b/netwerk/test/gtest/TestReadStreamToString.cpp
@@ -0,0 +1,190 @@
+#include "gtest/gtest.h"
+
+#include "Helpers.h"
+#include "nsCOMPtr.h"
+#include "nsNetUtil.h"
+#include "nsStringStream.h"
+
+// Here we test the reading a pre-allocated size
+TEST(TestReadStreamToString, SyncStreamPreAllocatedSize)
+{
+ nsCString buffer;
+ buffer.AssignLiteral("Hello world!");
+
+ nsCOMPtr<nsIInputStream> stream;
+ ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), buffer));
+
+ uint64_t written;
+ nsAutoCString result;
+ result.SetLength(5);
+
+ void* ptr = result.BeginWriting();
+
+ ASSERT_EQ(NS_OK, NS_ReadInputStreamToString(stream, result, 5, &written));
+ ASSERT_EQ((uint64_t)5, written);
+ ASSERT_TRUE(nsCString(buffer.get(), 5).Equals(result));
+
+ // The pointer should be equal: no relocation.
+ ASSERT_EQ(ptr, result.BeginWriting());
+}
+
+// Here we test the reading the full size of a sync stream
+TEST(TestReadStreamToString, SyncStreamFullSize)
+{
+ nsCString buffer;
+ buffer.AssignLiteral("Hello world!");
+
+ nsCOMPtr<nsIInputStream> stream;
+ ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), buffer));
+
+ uint64_t written;
+ nsAutoCString result;
+
+ ASSERT_EQ(NS_OK, NS_ReadInputStreamToString(stream, result, buffer.Length(),
+ &written));
+ ASSERT_EQ(buffer.Length(), written);
+ ASSERT_TRUE(buffer.Equals(result));
+}
+
+// Here we test the reading less than the full size of a sync stream
+TEST(TestReadStreamToString, SyncStreamLessThan)
+{
+ nsCString buffer;
+ buffer.AssignLiteral("Hello world!");
+
+ nsCOMPtr<nsIInputStream> stream;
+ ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), buffer));
+
+ uint64_t written;
+ nsAutoCString result;
+
+ ASSERT_EQ(NS_OK, NS_ReadInputStreamToString(stream, result, 5, &written));
+ ASSERT_EQ((uint64_t)5, written);
+ ASSERT_TRUE(nsCString(buffer.get(), 5).Equals(result));
+}
+
+// Here we test the reading more than the full size of a sync stream
+TEST(TestReadStreamToString, SyncStreamMoreThan)
+{
+ nsCString buffer;
+ buffer.AssignLiteral("Hello world!");
+
+ nsCOMPtr<nsIInputStream> stream;
+ ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), buffer));
+
+ uint64_t written;
+ nsAutoCString result;
+
+ // Reading more than the buffer size.
+ ASSERT_EQ(NS_OK, NS_ReadInputStreamToString(stream, result,
+ buffer.Length() + 5, &written));
+ ASSERT_EQ(buffer.Length(), written);
+ ASSERT_TRUE(buffer.Equals(result));
+}
+
+// Here we test the reading a sync stream without passing the size
+TEST(TestReadStreamToString, SyncStreamUnknownSize)
+{
+ nsCString buffer;
+ buffer.AssignLiteral("Hello world!");
+
+ nsCOMPtr<nsIInputStream> stream;
+ ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), buffer));
+
+ uint64_t written;
+ nsAutoCString result;
+
+ // Reading all without passing the size
+ ASSERT_EQ(NS_OK, NS_ReadInputStreamToString(stream, result, -1, &written));
+ ASSERT_EQ(buffer.Length(), written);
+ ASSERT_TRUE(buffer.Equals(result));
+}
+
+// Here we test the reading the full size of an async stream
+TEST(TestReadStreamToString, AsyncStreamFullSize)
+{
+ nsCString buffer;
+ buffer.AssignLiteral("Hello world!");
+
+ nsCOMPtr<nsIInputStream> stream = new testing::AsyncStringStream(buffer);
+
+ uint64_t written;
+ nsAutoCString result;
+
+ ASSERT_EQ(NS_OK, NS_ReadInputStreamToString(stream, result, buffer.Length(),
+ &written));
+ ASSERT_EQ(buffer.Length(), written);
+ ASSERT_TRUE(buffer.Equals(result));
+}
+
+// Here we test the reading less than the full size of an async stream
+TEST(TestReadStreamToString, AsyncStreamLessThan)
+{
+ nsCString buffer;
+ buffer.AssignLiteral("Hello world!");
+
+ nsCOMPtr<nsIInputStream> stream = new testing::AsyncStringStream(buffer);
+
+ uint64_t written;
+ nsAutoCString result;
+
+ ASSERT_EQ(NS_OK, NS_ReadInputStreamToString(stream, result, 5, &written));
+ ASSERT_EQ((uint64_t)5, written);
+ ASSERT_TRUE(nsCString(buffer.get(), 5).Equals(result));
+}
+
+// Here we test the reading more than the full size of an async stream
+TEST(TestReadStreamToString, AsyncStreamMoreThan)
+{
+ nsCString buffer;
+ buffer.AssignLiteral("Hello world!");
+
+ nsCOMPtr<nsIInputStream> stream = new testing::AsyncStringStream(buffer);
+
+ uint64_t written;
+ nsAutoCString result;
+
+ // Reading more than the buffer size.
+ ASSERT_EQ(NS_OK, NS_ReadInputStreamToString(stream, result,
+ buffer.Length() + 5, &written));
+ ASSERT_EQ(buffer.Length(), written);
+ ASSERT_TRUE(buffer.Equals(result));
+}
+
+// Here we test the reading an async stream without passing the size
+TEST(TestReadStreamToString, AsyncStreamUnknownSize)
+{
+ nsCString buffer;
+ buffer.AssignLiteral("Hello world!");
+
+ nsCOMPtr<nsIInputStream> stream = new testing::AsyncStringStream(buffer);
+
+ uint64_t written;
+ nsAutoCString result;
+
+ // Reading all without passing the size
+ ASSERT_EQ(NS_OK, NS_ReadInputStreamToString(stream, result, -1, &written));
+ ASSERT_EQ(buffer.Length(), written);
+ ASSERT_TRUE(buffer.Equals(result));
+}
+
+// Here we test the reading an async big stream without passing the size
+TEST(TestReadStreamToString, AsyncStreamUnknownBigSize)
+{
+ nsCString buffer;
+
+ buffer.SetLength(4096 * 2);
+ for (uint32_t i = 0; i < 4096 * 2; ++i) {
+ buffer.BeginWriting()[i] = i % 10;
+ }
+
+ nsCOMPtr<nsIInputStream> stream = new testing::AsyncStringStream(buffer);
+
+ uint64_t written;
+ nsAutoCString result;
+
+ // Reading all without passing the size
+ ASSERT_EQ(NS_OK, NS_ReadInputStreamToString(stream, result, -1, &written));
+ ASSERT_EQ(buffer.Length(), written);
+ ASSERT_TRUE(buffer.Equals(result));
+}
diff --git a/netwerk/test/gtest/TestSSLTokensCache.cpp b/netwerk/test/gtest/TestSSLTokensCache.cpp
new file mode 100644
index 0000000000..0f67a532eb
--- /dev/null
+++ b/netwerk/test/gtest/TestSSLTokensCache.cpp
@@ -0,0 +1,154 @@
+#include <numeric>
+
+#include "CertVerifier.h"
+#include "CommonSocketControl.h"
+#include "SSLTokensCache.h"
+#include "TransportSecurityInfo.h"
+#include "gtest/gtest.h"
+#include "mozilla/Preferences.h"
+#include "nsITransportSecurityInfo.h"
+#include "nsIWebProgressListener.h"
+#include "nsIX509Cert.h"
+#include "nsIX509CertDB.h"
+#include "nsServiceManagerUtils.h"
+#include "sslproto.h"
+
+static already_AddRefed<CommonSocketControl> createDummySocketControl() {
+ nsCOMPtr<nsIX509CertDB> certDB(do_GetService(NS_X509CERTDB_CONTRACTID));
+ EXPECT_TRUE(certDB);
+ nsLiteralCString base64(
+ "MIIBbjCCARWgAwIBAgIUOyCxVVqw03yUxKSfSojsMF8K/"
+ "ikwCgYIKoZIzj0EAwIwHTEbMBkGA1UEAwwScm9vdF9zZWNwMjU2azFfMjU2MCIYDzIwMjAxM"
+ "TI3MDAwMDAwWhgPMjAyMzAyMDUwMDAwMDBaMC8xLTArBgNVBAMMJGludF9zZWNwMjU2cjFfM"
+ "jU2LXJvb3Rfc2VjcDI1NmsxXzI1NjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABE+/"
+ "u7th4Pj5saYKWayHBOLsBQtCPjz3LpI/"
+ "LE95S0VcKmnSM0VsNsQRnQcG4A7tyNGTkNeZG3stB6ME6qBKpsCjHTAbMAwGA1UdEwQFMAMB"
+ "Af8wCwYDVR0PBAQDAgEGMAoGCCqGSM49BAMCA0cAMEQCIFuwodUwyOUnIR4KN5ZCSrU7y4iz"
+ "4/1EWRdHm5kWKi8dAiB6Ixn9sw3uBVbyxnQKYqGnOwM+qLOkJK0W8XkIE3n5sg==");
+ nsCOMPtr<nsIX509Cert> cert;
+ EXPECT_TRUE(NS_SUCCEEDED(
+ certDB->ConstructX509FromBase64(base64, getter_AddRefs(cert))));
+ EXPECT_TRUE(cert);
+ nsTArray<nsTArray<uint8_t>> succeededCertChain;
+ for (size_t i = 0; i < 3; i++) {
+ nsTArray<uint8_t> certDER;
+ EXPECT_TRUE(NS_SUCCEEDED(cert->GetRawDER(certDER)));
+ succeededCertChain.AppendElement(std::move(certDER));
+ }
+ RefPtr<CommonSocketControl> socketControl(
+ new CommonSocketControl(nsLiteralCString("example.com"), 433, 0));
+ socketControl->SetServerCert(cert, mozilla::psm::EVStatus::NotEV);
+ socketControl->SetSucceededCertChain(std::move(succeededCertChain));
+ return socketControl.forget();
+}
+
+static auto MakeTestData(const size_t aDataSize) {
+ auto data = nsTArray<uint8_t>();
+ data.SetLength(aDataSize);
+ std::iota(data.begin(), data.end(), 0);
+ return data;
+}
+
+static void putToken(const nsACString& aKey, uint32_t aSize) {
+ RefPtr<CommonSocketControl> socketControl = createDummySocketControl();
+ nsTArray<uint8_t> token = MakeTestData(aSize);
+ nsresult rv = mozilla::net::SSLTokensCache::Put(aKey, token.Elements(), aSize,
+ socketControl, aSize);
+ ASSERT_EQ(rv, NS_OK);
+}
+
+static void getAndCheckResult(const nsACString& aKey, uint32_t aExpectedSize) {
+ nsTArray<uint8_t> result;
+ mozilla::net::SessionCacheInfo unused;
+ nsresult rv = mozilla::net::SSLTokensCache::Get(aKey, result, unused);
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(result.Length(), (size_t)aExpectedSize);
+}
+
+TEST(TestTokensCache, SinglePut)
+{
+ mozilla::net::SSLTokensCache::Clear();
+ mozilla::Preferences::SetInt("network.ssl_tokens_cache_records_per_entry", 1);
+ mozilla::Preferences::SetBool("network.ssl_tokens_cache_use_only_once", true);
+
+ putToken("anon:www.example.com:443"_ns, 100);
+ nsTArray<uint8_t> result;
+ mozilla::net::SessionCacheInfo unused;
+ nsresult rv = mozilla::net::SSLTokensCache::Get("anon:www.example.com:443"_ns,
+ result, unused);
+ ASSERT_EQ(rv, NS_OK);
+ rv = mozilla::net::SSLTokensCache::Get("anon:www.example.com:443"_ns, result,
+ unused);
+ ASSERT_EQ(rv, NS_ERROR_NOT_AVAILABLE);
+}
+
+TEST(TestTokensCache, MultiplePut)
+{
+ mozilla::net::SSLTokensCache::Clear();
+ mozilla::Preferences::SetInt("network.ssl_tokens_cache_records_per_entry", 3);
+
+ putToken("anon:www.example1.com:443"_ns, 300);
+ // This record will be removed because
+ // "network.ssl_tokens_cache_records_per_entry" is 3.
+ putToken("anon:www.example1.com:443"_ns, 100);
+ putToken("anon:www.example1.com:443"_ns, 200);
+ putToken("anon:www.example1.com:443"_ns, 400);
+
+ // Test if records are ordered by the expiration time
+ getAndCheckResult("anon:www.example1.com:443"_ns, 200);
+ getAndCheckResult("anon:www.example1.com:443"_ns, 300);
+ getAndCheckResult("anon:www.example1.com:443"_ns, 400);
+}
+
+TEST(TestTokensCache, RemoveAll)
+{
+ mozilla::net::SSLTokensCache::Clear();
+ mozilla::Preferences::SetInt("network.ssl_tokens_cache_records_per_entry", 3);
+
+ putToken("anon:www.example1.com:443"_ns, 100);
+ putToken("anon:www.example1.com:443"_ns, 200);
+ putToken("anon:www.example1.com:443"_ns, 300);
+
+ putToken("anon:www.example2.com:443"_ns, 100);
+ putToken("anon:www.example2.com:443"_ns, 200);
+ putToken("anon:www.example2.com:443"_ns, 300);
+
+ nsTArray<uint8_t> result;
+ mozilla::net::SessionCacheInfo unused;
+ nsresult rv = mozilla::net::SSLTokensCache::Get(
+ "anon:www.example1.com:443"_ns, result, unused);
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(result.Length(), (size_t)100);
+
+ rv = mozilla::net::SSLTokensCache::RemoveAll("anon:www.example1.com:443"_ns);
+ ASSERT_EQ(rv, NS_OK);
+
+ rv = mozilla::net::SSLTokensCache::Get("anon:www.example1.com:443"_ns, result,
+ unused);
+ ASSERT_EQ(rv, NS_ERROR_NOT_AVAILABLE);
+
+ rv = mozilla::net::SSLTokensCache::Get("anon:www.example2.com:443"_ns, result,
+ unused);
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(result.Length(), (size_t)100);
+}
+
+TEST(TestTokensCache, Eviction)
+{
+ mozilla::net::SSLTokensCache::Clear();
+
+ mozilla::Preferences::SetInt("network.ssl_tokens_cache_records_per_entry", 3);
+ mozilla::Preferences::SetInt("network.ssl_tokens_cache_capacity", 8);
+
+ putToken("anon:www.example2.com:443"_ns, 300);
+ putToken("anon:www.example2.com:443"_ns, 400);
+ putToken("anon:www.example2.com:443"_ns, 500);
+ // The one has expiration time "300" will be removed because we only allow 3
+ // records per entry.
+ putToken("anon:www.example2.com:443"_ns, 600);
+
+ putToken("anon:www.example3.com:443"_ns, 600);
+ putToken("anon:www.example3.com:443"_ns, 500);
+ // The one has expiration time "400" was evicted, so we get "500".
+ getAndCheckResult("anon:www.example2.com:443"_ns, 500);
+}
diff --git a/netwerk/test/gtest/TestServerTimingHeader.cpp b/netwerk/test/gtest/TestServerTimingHeader.cpp
new file mode 100644
index 0000000000..183726a440
--- /dev/null
+++ b/netwerk/test/gtest/TestServerTimingHeader.cpp
@@ -0,0 +1,238 @@
+#include "gtest/gtest.h"
+
+#include "mozilla/Unused.h"
+#include "mozilla/net/nsServerTiming.h"
+#include <string>
+#include <vector>
+
+using namespace mozilla;
+using namespace mozilla::net;
+
+void testServerTimingHeader(
+ const char* headerValue,
+ std::vector<std::vector<std::string>> expectedResults) {
+ nsAutoCString header(headerValue);
+ ServerTimingParser parser(header);
+ parser.Parse();
+
+ nsTArray<nsCOMPtr<nsIServerTiming>> results =
+ parser.TakeServerTimingHeaders();
+
+ ASSERT_EQ(results.Length(), expectedResults.size());
+
+ unsigned i = 0;
+ for (const auto& header : results) {
+ std::vector<std::string> expectedResult(expectedResults[i++]);
+ nsCString name;
+ mozilla::Unused << header->GetName(name);
+ ASSERT_TRUE(name.Equals(expectedResult[0].c_str()));
+
+ double duration;
+ mozilla::Unused << header->GetDuration(&duration);
+ ASSERT_EQ(duration, atof(expectedResult[1].c_str()));
+
+ nsCString description;
+ mozilla::Unused << header->GetDescription(description);
+ ASSERT_TRUE(description.Equals(expectedResult[2].c_str()));
+ }
+}
+
+TEST(TestServerTimingHeader, HeaderParsing)
+{
+ // Test cases below are copied from
+ // https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/network/HTTPParsersTest.cpp
+
+ testServerTimingHeader("", {});
+ testServerTimingHeader("metric", {{"metric", "0", ""}});
+ testServerTimingHeader("metric;dur", {{"metric", "0", ""}});
+ testServerTimingHeader("metric;dur=123.4", {{"metric", "123.4", ""}});
+ testServerTimingHeader("metric;dur=\"123.4\"", {{"metric", "123.4", ""}});
+
+ testServerTimingHeader("metric;desc", {{"metric", "0", ""}});
+ testServerTimingHeader("metric;desc=description",
+ {{"metric", "0", "description"}});
+ testServerTimingHeader("metric;desc=\"description\"",
+ {{"metric", "0", "description"}});
+
+ testServerTimingHeader("metric;dur;desc", {{"metric", "0", ""}});
+ testServerTimingHeader("metric;dur=123.4;desc", {{"metric", "123.4", ""}});
+ testServerTimingHeader("metric;dur;desc=description",
+ {{"metric", "0", "description"}});
+ testServerTimingHeader("metric;dur=123.4;desc=description",
+ {{"metric", "123.4", "description"}});
+ testServerTimingHeader("metric;desc;dur", {{"metric", "0", ""}});
+ testServerTimingHeader("metric;desc;dur=123.4", {{"metric", "123.4", ""}});
+ testServerTimingHeader("metric;desc=description;dur",
+ {{"metric", "0", "description"}});
+ testServerTimingHeader("metric;desc=description;dur=123.4",
+ {{"metric", "123.4", "description"}});
+
+ // special chars in name
+ testServerTimingHeader("aB3!#$%&'*+-.^_`|~",
+ {{"aB3!#$%&'*+-.^_`|~", "0", ""}});
+
+ // delimiter chars in quoted description
+ testServerTimingHeader("metric;desc=\"descr;,=iption\";dur=123.4",
+ {{"metric", "123.4", "descr;,=iption"}});
+
+ // whitespace
+ testServerTimingHeader("metric ; ", {{"metric", "0", ""}});
+ testServerTimingHeader("metric , ", {{"metric", "0", ""}});
+ testServerTimingHeader("metric ; dur = 123.4 ; desc = description",
+ {{"metric", "123.4", "description"}});
+ testServerTimingHeader("metric ; desc = description ; dur = 123.4",
+ {{"metric", "123.4", "description"}});
+
+ // multiple entries
+ testServerTimingHeader(
+ "metric1;dur=12.3;desc=description1,metric2;dur=45.6;"
+ "desc=description2,metric3;dur=78.9;desc=description3",
+ {{"metric1", "12.3", "description1"},
+ {"metric2", "45.6", "description2"},
+ {"metric3", "78.9", "description3"}});
+ testServerTimingHeader("metric1,metric2 ,metric3, metric4 , metric5",
+ {{"metric1", "0", ""},
+ {"metric2", "0", ""},
+ {"metric3", "0", ""},
+ {"metric4", "0", ""},
+ {"metric5", "0", ""}});
+
+ // quoted-strings
+ // metric;desc=\ --> ''
+ testServerTimingHeader("metric;desc=\\", {{"metric", "0", ""}});
+ // metric;desc=" --> ''
+ testServerTimingHeader("metric;desc=\"", {{"metric", "0", ""}});
+ // metric;desc=\\ --> ''
+ testServerTimingHeader("metric;desc=\\\\", {{"metric", "0", ""}});
+ // metric;desc=\" --> ''
+ testServerTimingHeader("metric;desc=\\\"", {{"metric", "0", ""}});
+ // metric;desc="\ --> ''
+ testServerTimingHeader("metric;desc=\"\\", {{"metric", "0", ""}});
+ // metric;desc="" --> ''
+ testServerTimingHeader("metric;desc=\"\"", {{"metric", "0", ""}});
+ // metric;desc=\\\ --> ''
+ testServerTimingHeader(R"(metric;desc=\\\)", {{"metric", "0", ""}});
+ // metric;desc=\\" --> ''
+ testServerTimingHeader(R"(metric;desc=\\")", {{"metric", "0", ""}});
+ // metric;desc=\"\ --> ''
+ testServerTimingHeader(R"(metric;desc=\"\)", {{"metric", "0", ""}});
+ // metric;desc=\"" --> ''
+ testServerTimingHeader(R"(metric;desc=\"")", {{"metric", "0", ""}});
+ // metric;desc="\\ --> ''
+ testServerTimingHeader(R"(metric;desc="\\)", {{"metric", "0", ""}});
+ // metric;desc="\" --> ''
+ testServerTimingHeader(R"(metric;desc="\")", {{"metric", "0", ""}});
+ // metric;desc=""\ --> ''
+ testServerTimingHeader(R"(metric;desc=""\)", {{"metric", "0", ""}});
+ // metric;desc=""" --> ''
+ testServerTimingHeader(R"(metric;desc=""")", {{"metric", "0", ""}});
+ // metric;desc=\\\\ --> ''
+ testServerTimingHeader(R"(metric;desc=\\\\)", {{"metric", "0", ""}});
+ // metric;desc=\\\" --> ''
+ testServerTimingHeader(R"(metric;desc=\\\")", {{"metric", "0", ""}});
+ // metric;desc=\\"\ --> ''
+ testServerTimingHeader(R"(metric;desc=\\"\)", {{"metric", "0", ""}});
+ // metric;desc=\\"" --> ''
+ testServerTimingHeader(R"(metric;desc=\\"")", {{"metric", "0", ""}});
+ // metric;desc=\"\\ --> ''
+ testServerTimingHeader(R"(metric;desc=\"\\)", {{"metric", "0", ""}});
+ // metric;desc=\"\" --> ''
+ testServerTimingHeader(R"(metric;desc=\"\")", {{"metric", "0", ""}});
+ // metric;desc=\""\ --> ''
+ testServerTimingHeader(R"(metric;desc=\""\)", {{"metric", "0", ""}});
+ // metric;desc=\""" --> ''
+ testServerTimingHeader(R"(metric;desc=\""")", {{"metric", "0", ""}});
+ // metric;desc="\\\ --> ''
+ testServerTimingHeader(R"(metric;desc="\\\)", {{"metric", "0", ""}});
+ // metric;desc="\\" --> '\'
+ testServerTimingHeader(R"(metric;desc="\\")", {{"metric", "0", "\\"}});
+ // metric;desc="\"\ --> ''
+ testServerTimingHeader(R"(metric;desc="\"\)", {{"metric", "0", ""}});
+ // metric;desc="\"" --> '"'
+ testServerTimingHeader(R"(metric;desc="\"")", {{"metric", "0", "\""}});
+ // metric;desc=""\\ --> ''
+ testServerTimingHeader(R"(metric;desc=""\\)", {{"metric", "0", ""}});
+ // metric;desc=""\" --> ''
+ testServerTimingHeader(R"(metric;desc=""\")", {{"metric", "0", ""}});
+ // metric;desc="""\ --> ''
+ testServerTimingHeader(R"(metric;desc="""\)", {{"metric", "0", ""}});
+ // metric;desc="""" --> ''
+ testServerTimingHeader(R"(metric;desc="""")", {{"metric", "0", ""}});
+
+ // duplicate entry names
+ testServerTimingHeader(
+ "metric;dur=12.3;desc=description1,metric;dur=45.6;"
+ "desc=description2",
+ {{"metric", "12.3", "description1"}, {"metric", "45.6", "description2"}});
+
+ // non-numeric durations
+ testServerTimingHeader("metric;dur=foo", {{"metric", "0", ""}});
+ testServerTimingHeader("metric;dur=\"foo\"", {{"metric", "0", ""}});
+
+ // unrecognized param names
+ testServerTimingHeader(
+ "metric;foo=bar;desc=description;foo=bar;dur=123.4;foo=bar",
+ {{"metric", "123.4", "description"}});
+
+ // duplicate param names
+ testServerTimingHeader("metric;dur=123.4;dur=567.8",
+ {{"metric", "123.4", ""}});
+ testServerTimingHeader("metric;desc=description1;desc=description2",
+ {{"metric", "0", "description1"}});
+ testServerTimingHeader("metric;dur=foo;dur=567.8", {{"metric", "", ""}});
+
+ // unspecified param values
+ testServerTimingHeader("metric;dur;dur=123.4", {{"metric", "0", ""}});
+ testServerTimingHeader("metric;desc;desc=description", {{"metric", "0", ""}});
+
+ // param name case
+ testServerTimingHeader("metric;DuR=123.4;DeSc=description",
+ {{"metric", "123.4", "description"}});
+
+ // nonsense
+ testServerTimingHeader("metric=foo;dur;dur=123.4,metric2",
+ {{"metric", "0", ""}, {"metric2", "0", ""}});
+ testServerTimingHeader("metric\"foo;dur;dur=123.4,metric2",
+ {{"metric", "0", ""}});
+
+ // nonsense - return zero entries
+ testServerTimingHeader(" ", {});
+ testServerTimingHeader("=", {});
+ testServerTimingHeader("[", {});
+ testServerTimingHeader("]", {});
+ testServerTimingHeader(";", {});
+ testServerTimingHeader(",", {});
+ testServerTimingHeader("=;", {});
+ testServerTimingHeader(";=", {});
+ testServerTimingHeader("=,", {});
+ testServerTimingHeader(",=", {});
+ testServerTimingHeader(";,", {});
+ testServerTimingHeader(",;", {});
+ testServerTimingHeader("=;,", {});
+
+ // Invalid token
+ testServerTimingHeader("met=ric", {{"met", "0", ""}});
+ testServerTimingHeader("met ric", {{"met", "0", ""}});
+ testServerTimingHeader("met[ric", {{"met", "0", ""}});
+ testServerTimingHeader("met]ric", {{"met", "0", ""}});
+ testServerTimingHeader("metric;desc=desc=123, metric2",
+ {{"metric", "0", "desc"}, {"metric2", "0", ""}});
+ testServerTimingHeader("met ric;desc=de sc , metric2",
+ {{"met", "0", "de"}, {"metric2", "0", ""}});
+
+ // test cases from https://w3c.github.io/server-timing/#examples
+ testServerTimingHeader(
+ " miss, ,db;dur=53, app;dur=47.2 ",
+ {{"miss", "0", ""}, {"db", "53", ""}, {"app", "47.2", ""}});
+ testServerTimingHeader(" customView, dc;desc=atl ",
+ {{"customView", "0", ""}, {"dc", "0", "atl"}});
+ testServerTimingHeader(" total;dur=123.4 ", {{"total", "123.4", ""}});
+
+ // test cases for comma in quoted string
+ testServerTimingHeader(R"( metric ; desc="descr\"\";,=iption";dur=123.4)",
+ {{"metric", "123.4", "descr\"\";,=iption"}});
+ testServerTimingHeader(
+ " metric2;dur=\"123.4\";;desc=\",;\\\",;,\";;, metric ; desc = \" "
+ "\\\", ;\\\" \"; dur=123.4,",
+ {{"metric2", "123.4", ",;\",;,"}, {"metric", "123.4", " \", ;\" "}});
+}
diff --git a/netwerk/test/gtest/TestSocketTransportService.cpp b/netwerk/test/gtest/TestSocketTransportService.cpp
new file mode 100644
index 0000000000..89adad3740
--- /dev/null
+++ b/netwerk/test/gtest/TestSocketTransportService.cpp
@@ -0,0 +1,164 @@
+#include "gtest/gtest.h"
+
+#include "nsCOMPtr.h"
+#include "nsISocketTransport.h"
+#include "nsString.h"
+#include "nsComponentManagerUtils.h"
+#include "../../base/nsSocketTransportService2.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace net {
+
+TEST(TestSocketTransportService, PortRemappingPreferenceReading)
+{
+ nsCOMPtr<nsISocketTransportService> service =
+ do_GetService("@mozilla.org/network/socket-transport-service;1");
+ ASSERT_TRUE(service);
+
+ auto* sts = gSocketTransportService;
+ ASSERT_TRUE(sts);
+
+ NS_DispatchAndSpinEventLoopUntilComplete(
+ "test"_ns, sts, NS_NewRunnableFunction("test", [&]() {
+ auto CheckPortRemap = [&](uint16_t input, uint16_t output) -> bool {
+ sts->ApplyPortRemap(&input);
+ return input == output;
+ };
+
+ // Ill-formed prefs
+ ASSERT_FALSE(sts->UpdatePortRemapPreference(";"_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference(" ;"_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("; "_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("foo"_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference(" foo"_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference(" foo "_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("10=20;"_ns));
+
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("1"_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("1="_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("1,="_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("1-="_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("1-,="_ns));
+
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("1=2,"_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("1=2-3"_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("1-2,=3"_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("1-2,3"_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("1-2,3-4"_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("1-2,3-4,"_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("1-2,3-4="_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("10000000=10"_ns));
+
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("1=2;3"_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("1-4=2;3"_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("1-4=2;3="_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("1-foo=2;3=15"_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("1-4=foo;3=15"_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("1-4=2;foo=15"_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("1-4=2;3=foo"_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("1-4=2x3=15"_ns));
+ ASSERT_FALSE(sts->UpdatePortRemapPreference("1+4=2;3=15"_ns));
+
+ // Well-formed prefs
+ ASSERT_TRUE(sts->UpdatePortRemapPreference("1=2"_ns));
+ ASSERT_TRUE(CheckPortRemap(1, 2));
+ ASSERT_TRUE(CheckPortRemap(2, 2));
+ ASSERT_TRUE(CheckPortRemap(3, 3));
+
+ ASSERT_TRUE(sts->UpdatePortRemapPreference("10=20"_ns));
+ ASSERT_TRUE(CheckPortRemap(1, 1));
+ ASSERT_TRUE(CheckPortRemap(2, 2));
+ ASSERT_TRUE(CheckPortRemap(3, 3));
+ ASSERT_TRUE(CheckPortRemap(10, 20));
+
+ ASSERT_TRUE(sts->UpdatePortRemapPreference("100-200=1000"_ns));
+ ASSERT_TRUE(CheckPortRemap(10, 10));
+ ASSERT_TRUE(CheckPortRemap(99, 99));
+ ASSERT_TRUE(CheckPortRemap(100, 1000));
+ ASSERT_TRUE(CheckPortRemap(101, 1000));
+ ASSERT_TRUE(CheckPortRemap(200, 1000));
+ ASSERT_TRUE(CheckPortRemap(201, 201));
+
+ ASSERT_TRUE(sts->UpdatePortRemapPreference("100-200,500=1000"_ns));
+ ASSERT_TRUE(CheckPortRemap(10, 10));
+ ASSERT_TRUE(CheckPortRemap(99, 99));
+ ASSERT_TRUE(CheckPortRemap(100, 1000));
+ ASSERT_TRUE(CheckPortRemap(101, 1000));
+ ASSERT_TRUE(CheckPortRemap(200, 1000));
+ ASSERT_TRUE(CheckPortRemap(201, 201));
+ ASSERT_TRUE(CheckPortRemap(499, 499));
+ ASSERT_TRUE(CheckPortRemap(500, 1000));
+ ASSERT_TRUE(CheckPortRemap(501, 501));
+
+ ASSERT_TRUE(sts->UpdatePortRemapPreference("1-3=10;5-8,12=20"_ns));
+ ASSERT_TRUE(CheckPortRemap(1, 10));
+ ASSERT_TRUE(CheckPortRemap(2, 10));
+ ASSERT_TRUE(CheckPortRemap(3, 10));
+ ASSERT_TRUE(CheckPortRemap(4, 4));
+ ASSERT_TRUE(CheckPortRemap(5, 20));
+ ASSERT_TRUE(CheckPortRemap(8, 20));
+ ASSERT_TRUE(CheckPortRemap(11, 11));
+ ASSERT_TRUE(CheckPortRemap(12, 20));
+
+ ASSERT_TRUE(sts->UpdatePortRemapPreference("80=8080;443=8080"_ns));
+ ASSERT_TRUE(CheckPortRemap(80, 8080));
+ ASSERT_TRUE(CheckPortRemap(443, 8080));
+
+ // Later rules rewrite earlier rules
+ ASSERT_TRUE(sts->UpdatePortRemapPreference("10=100;10=200"_ns));
+ ASSERT_TRUE(CheckPortRemap(10, 200));
+
+ ASSERT_TRUE(sts->UpdatePortRemapPreference("10-20=100;10-20=200"_ns));
+ ASSERT_TRUE(CheckPortRemap(10, 200));
+ ASSERT_TRUE(CheckPortRemap(20, 200));
+
+ ASSERT_TRUE(sts->UpdatePortRemapPreference("10-20=100;15=200"_ns));
+ ASSERT_TRUE(CheckPortRemap(10, 100));
+ ASSERT_TRUE(CheckPortRemap(15, 200));
+ ASSERT_TRUE(CheckPortRemap(20, 100));
+
+ ASSERT_TRUE(sts->UpdatePortRemapPreference(
+ " 100 - 200 = 1000 ; 150 = 2000 "_ns));
+ ASSERT_TRUE(CheckPortRemap(100, 1000));
+ ASSERT_TRUE(CheckPortRemap(150, 2000));
+ ASSERT_TRUE(CheckPortRemap(200, 1000));
+
+ // Turn off any mapping
+ ASSERT_TRUE(sts->UpdatePortRemapPreference(""_ns));
+ for (uint32_t port = 0; port < 65536; ++port) {
+ ASSERT_TRUE(CheckPortRemap((uint16_t)port, (uint16_t)port));
+ }
+ }));
+}
+
+TEST(TestSocketTransportService, StatusValues)
+{
+ static_assert(static_cast<nsresult>(nsISocketTransport::STATUS_RESOLVING) ==
+ NS_NET_STATUS_RESOLVING_HOST);
+ static_assert(static_cast<nsresult>(nsISocketTransport::STATUS_RESOLVED) ==
+ NS_NET_STATUS_RESOLVED_HOST);
+ static_assert(
+ static_cast<nsresult>(nsISocketTransport::STATUS_CONNECTING_TO) ==
+ NS_NET_STATUS_CONNECTING_TO);
+ static_assert(
+ static_cast<nsresult>(nsISocketTransport::STATUS_CONNECTED_TO) ==
+ NS_NET_STATUS_CONNECTED_TO);
+ static_assert(static_cast<nsresult>(nsISocketTransport::STATUS_SENDING_TO) ==
+ NS_NET_STATUS_SENDING_TO);
+ static_assert(static_cast<nsresult>(nsISocketTransport::STATUS_WAITING_FOR) ==
+ NS_NET_STATUS_WAITING_FOR);
+ static_assert(
+ static_cast<nsresult>(nsISocketTransport::STATUS_RECEIVING_FROM) ==
+ NS_NET_STATUS_RECEIVING_FROM);
+ static_assert(static_cast<nsresult>(
+ nsISocketTransport::STATUS_TLS_HANDSHAKE_STARTING) ==
+ NS_NET_STATUS_TLS_HANDSHAKE_STARTING);
+ static_assert(
+ static_cast<nsresult>(nsISocketTransport::STATUS_TLS_HANDSHAKE_ENDED) ==
+ NS_NET_STATUS_TLS_HANDSHAKE_ENDED);
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/test/gtest/TestStandardURL.cpp b/netwerk/test/gtest/TestStandardURL.cpp
new file mode 100644
index 0000000000..035c92fcc2
--- /dev/null
+++ b/netwerk/test/gtest/TestStandardURL.cpp
@@ -0,0 +1,441 @@
+#include "gtest/gtest.h"
+#include "gtest/MozGTestBench.h" // For MOZ_GTEST_BENCH
+
+#include "nsCOMPtr.h"
+#include "nsNetCID.h"
+#include "nsIURL.h"
+#include "nsIStandardURL.h"
+#include "nsString.h"
+#include "nsPrintfCString.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIURIMutator.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "mozilla/Unused.h"
+#include "nsSerializationHelper.h"
+#include "mozilla/Base64.h"
+#include "nsEscape.h"
+
+using namespace mozilla;
+
+// In nsStandardURL.cpp
+extern nsresult Test_NormalizeIPv4(const nsACString& host, nsCString& result);
+extern nsresult Test_ParseIPv4Number(const nsACString& input, int32_t base,
+ uint32_t& number, uint32_t maxNumber);
+extern int32_t Test_ValidateIPv4Number(const nsACString& host, int32_t bases[4],
+ int32_t dotIndex[3], bool& onlyBase10,
+ int32_t length);
+TEST(TestStandardURL, Simple)
+{
+ nsCOMPtr<nsIURI> url;
+ ASSERT_EQ(NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID)
+ .SetSpec("http://example.com"_ns)
+ .Finalize(url),
+ NS_OK);
+ ASSERT_TRUE(url);
+
+ ASSERT_EQ(NS_MutateURI(url).SetSpec("http://example.com"_ns).Finalize(url),
+ NS_OK);
+
+ nsAutoCString out;
+
+ ASSERT_EQ(url->GetSpec(out), NS_OK);
+ ASSERT_TRUE(out == "http://example.com/"_ns);
+
+ ASSERT_EQ(url->Resolve("foo.html?q=45"_ns, out), NS_OK);
+ ASSERT_TRUE(out == "http://example.com/foo.html?q=45"_ns);
+
+ ASSERT_EQ(NS_MutateURI(url).SetScheme("foo"_ns).Finalize(url), NS_OK);
+
+ ASSERT_EQ(url->GetScheme(out), NS_OK);
+ ASSERT_TRUE(out == "foo"_ns);
+
+ ASSERT_EQ(url->GetHost(out), NS_OK);
+ ASSERT_TRUE(out == "example.com"_ns);
+ ASSERT_EQ(NS_MutateURI(url).SetHost("www.yahoo.com"_ns).Finalize(url), NS_OK);
+ ASSERT_EQ(url->GetHost(out), NS_OK);
+ ASSERT_TRUE(out == "www.yahoo.com"_ns);
+
+ ASSERT_EQ(NS_MutateURI(url)
+ .SetPathQueryRef(nsLiteralCString(
+ "/some-path/one-the-net/about.html?with-a-query#for-you"))
+ .Finalize(url),
+ NS_OK);
+ ASSERT_EQ(url->GetPathQueryRef(out), NS_OK);
+ ASSERT_TRUE(out ==
+ nsLiteralCString(
+ "/some-path/one-the-net/about.html?with-a-query#for-you"));
+
+ ASSERT_EQ(NS_MutateURI(url)
+ .SetQuery(nsLiteralCString(
+ "a=b&d=c&what-ever-you-want-to-be-called=45"))
+ .Finalize(url),
+ NS_OK);
+ ASSERT_EQ(url->GetQuery(out), NS_OK);
+ ASSERT_TRUE(out == "a=b&d=c&what-ever-you-want-to-be-called=45"_ns);
+
+ ASSERT_EQ(NS_MutateURI(url).SetRef("#some-book-mark"_ns).Finalize(url),
+ NS_OK);
+ ASSERT_EQ(url->GetRef(out), NS_OK);
+ ASSERT_TRUE(out == "some-book-mark"_ns);
+}
+
+TEST(TestStandardURL, NormalizeGood)
+{
+ nsCString result;
+ const char* manual[] = {"0.0.0.0",
+ "0.0.0.0",
+ "0",
+ "0.0.0.0",
+ "000",
+ "0.0.0.0",
+ "0x00",
+ "0.0.0.0",
+ "10.20.100.200",
+ "10.20.100.200",
+ "255.255.255.255",
+ "255.255.255.255",
+ "0XFF.0xFF.0xff.0xFf",
+ "255.255.255.255",
+ "0x000ff.0X00FF.0x0ff.0xff",
+ "255.255.255.255",
+ "0x000fA.0X00FB.0x0fC.0xfD",
+ "250.251.252.253",
+ "0x000fE.0X00FF.0x0fC.0xfD",
+ "254.255.252.253",
+ "0x000fa.0x00fb.0x0fc.0xfd",
+ "250.251.252.253",
+ "0x000fe.0x00ff.0x0fc.0xfd",
+ "254.255.252.253",
+ "0377.0377.0377.0377",
+ "255.255.255.255",
+ "0000377.000377.00377.0377",
+ "255.255.255.255",
+ "65535",
+ "0.0.255.255",
+ "0xfFFf",
+ "0.0.255.255",
+ "0x00000ffff",
+ "0.0.255.255",
+ "0177777",
+ "0.0.255.255",
+ "000177777",
+ "0.0.255.255",
+ "0.13.65535",
+ "0.13.255.255",
+ "0.22.0xffff",
+ "0.22.255.255",
+ "0.123.0177777",
+ "0.123.255.255",
+ "65536",
+ "0.1.0.0",
+ "0200000",
+ "0.1.0.0",
+ "0x10000",
+ "0.1.0.0"};
+ for (uint32_t i = 0; i < sizeof(manual) / sizeof(manual[0]); i += 2) {
+ nsCString encHost(manual[i + 0]);
+ ASSERT_EQ(NS_OK, Test_NormalizeIPv4(encHost, result));
+ ASSERT_TRUE(result.Equals(manual[i + 1]));
+ }
+
+ // Make sure we're getting the numbers correctly interpreted:
+ for (int i = 0; i < 256; i++) {
+ nsCString encHost = nsPrintfCString("0x%x", i);
+ ASSERT_EQ(NS_OK, Test_NormalizeIPv4(encHost, result));
+ ASSERT_TRUE(result.Equals(nsPrintfCString("0.0.0.%d", i)));
+
+ encHost = nsPrintfCString("0%o", i);
+ ASSERT_EQ(NS_OK, Test_NormalizeIPv4(encHost, result));
+ ASSERT_TRUE(result.Equals(nsPrintfCString("0.0.0.%d", i)));
+ }
+
+ // Some random numbers in the range, mixing hex, decimal, octal
+ for (int i = 0; i < 8; i++) {
+ int val[4] = {i * 11 + 13, i * 18 + 22, i * 4 + 28, i * 15 + 2};
+
+ nsCString encHost =
+ nsPrintfCString("%d.%d.%d.%d", val[0], val[1], val[2], val[3]);
+ ASSERT_EQ(NS_OK, Test_NormalizeIPv4(encHost, result));
+ ASSERT_TRUE(result.Equals(encHost));
+
+ nsCString encHostM =
+ nsPrintfCString("0x%x.0x%x.0x%x.0x%x", val[0], val[1], val[2], val[3]);
+ ASSERT_EQ(NS_OK, Test_NormalizeIPv4(encHostM, result));
+ ASSERT_TRUE(result.Equals(encHost));
+
+ encHostM =
+ nsPrintfCString("0%o.0%o.0%o.0%o", val[0], val[1], val[2], val[3]);
+ ASSERT_EQ(NS_OK, Test_NormalizeIPv4(encHostM, result));
+ ASSERT_TRUE(result.Equals(encHost));
+
+ encHostM =
+ nsPrintfCString("0x%x.%d.0%o.%d", val[0], val[1], val[2], val[3]);
+ ASSERT_EQ(NS_OK, Test_NormalizeIPv4(encHostM, result));
+ ASSERT_TRUE(result.Equals(encHost));
+
+ encHostM =
+ nsPrintfCString("%d.0%o.0%o.0x%x", val[0], val[1], val[2], val[3]);
+ ASSERT_EQ(NS_OK, Test_NormalizeIPv4(encHostM, result));
+ ASSERT_TRUE(result.Equals(encHost));
+
+ encHostM =
+ nsPrintfCString("0%o.0%o.0x%x.0x%x", val[0], val[1], val[2], val[3]);
+ ASSERT_EQ(NS_OK, Test_NormalizeIPv4(encHostM, result));
+ ASSERT_TRUE(result.Equals(encHost));
+ }
+}
+
+TEST(TestStandardURL, NormalizeBad)
+{
+ nsAutoCString result;
+ const char* manual[] = {
+ "x22.232.12.32", "122..12.32", "122.12.32.12.32", "122.12.32..",
+ "122.12.xx.22", "122.12.0xx.22", "0xx.12.01.22", "12.12.02x.22",
+ "1q.12.2.22", "122.01f.02.22", "12a.01.02.22", "12.01.02.20x1",
+ "10x2.01.02.20", "0xx.01.02.20", "10.x.02.20", "10.00x2.02.20",
+ "10.13.02x2.20", "10.x13.02.20", "10.0x134def.02.20", "\0.2.2.2",
+ "256.2.2.2", "2.256.2.2", "2.2.256.2", "2.2.2.256",
+ "2.2.-2.3", "+2.2.2.3", "13.0x2x2.2.3", "0x2x2.13.2.3"};
+
+ for (auto& i : manual) {
+ nsCString encHost(i);
+ ASSERT_EQ(NS_ERROR_FAILURE, Test_NormalizeIPv4(encHost, result));
+ }
+}
+
+TEST(TestStandardURL, From_test_standardurldotjs)
+{
+ // These are test (success and failure) cases from test_standardurl.js
+ nsAutoCString result;
+
+ const char* localIPv4s[] = {
+ "127.0.0.1",
+ "127.0.1",
+ "127.1",
+ "2130706433",
+ "0177.00.00.01",
+ "0177.00.01",
+ "0177.01",
+ "00000000000000000000000000177.0000000.0000000.0001",
+ "000000177.0000001",
+ "017700000001",
+ "0x7f.0x00.0x00.0x01",
+ "0x7f.0x01",
+ "0x7f000001",
+ "0x007f.0x0000.0x0000.0x0001",
+ "000177.0.00000.0x0001",
+ "127.0.0.1.",
+
+ "0X7F.0X00.0X00.0X01",
+ "0X7F.0X01",
+ "0X7F000001",
+ "0X007F.0X0000.0X0000.0X0001",
+ "000177.0.00000.0X0001"};
+ for (auto& localIPv4 : localIPv4s) {
+ nsCString encHost(localIPv4);
+ ASSERT_EQ(NS_OK, Test_NormalizeIPv4(encHost, result));
+ ASSERT_TRUE(result.EqualsLiteral("127.0.0.1"));
+ }
+
+ const char* nonIPv4s[] = {"0xfffffffff", "0x100000000",
+ "4294967296", "1.2.0x10000",
+ "1.0x1000000", "256.0.0.1",
+ "1.256.1", "-1.0.0.0",
+ "1.2.3.4.5", "010000000000000000",
+ "2+3", "0.0.0.-1",
+ "1.2.3.4..", "1..2",
+ ".1.2.3.4", ".127"};
+ for (auto& nonIPv4 : nonIPv4s) {
+ nsCString encHost(nonIPv4);
+ ASSERT_EQ(NS_ERROR_FAILURE, Test_NormalizeIPv4(encHost, result));
+ }
+
+ const char* oneOrNoDotsIPv4s[] = {"127", "127."};
+ for (auto& localIPv4 : oneOrNoDotsIPv4s) {
+ nsCString encHost(localIPv4);
+ ASSERT_EQ(NS_OK, Test_NormalizeIPv4(encHost, result));
+ ASSERT_TRUE(result.EqualsLiteral("0.0.0.127"));
+ }
+}
+
+#define TEST_COUNT 10000
+
+MOZ_GTEST_BENCH(TestStandardURL, DISABLED_Perf, [] {
+ nsCOMPtr<nsIURI> url;
+ ASSERT_EQ(NS_OK, NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID)
+ .SetSpec("http://example.com"_ns)
+ .Finalize(url));
+
+ nsAutoCString out;
+ for (int i = TEST_COUNT; i; --i) {
+ ASSERT_EQ(NS_MutateURI(url).SetSpec("http://example.com"_ns).Finalize(url),
+ NS_OK);
+ ASSERT_EQ(url->GetSpec(out), NS_OK);
+ url->Resolve("foo.html?q=45"_ns, out);
+ mozilla::Unused << NS_MutateURI(url).SetScheme("foo"_ns).Finalize(url);
+ url->GetScheme(out);
+ mozilla::Unused
+ << NS_MutateURI(url).SetHost("www.yahoo.com"_ns).Finalize(url);
+ url->GetHost(out);
+ mozilla::Unused
+ << NS_MutateURI(url)
+ .SetPathQueryRef(nsLiteralCString(
+ "/some-path/one-the-net/about.html?with-a-query#for-you"))
+ .Finalize(url);
+ url->GetPathQueryRef(out);
+ mozilla::Unused << NS_MutateURI(url)
+ .SetQuery(nsLiteralCString(
+ "a=b&d=c&what-ever-you-want-to-be-called=45"))
+ .Finalize(url);
+ url->GetQuery(out);
+ mozilla::Unused
+ << NS_MutateURI(url).SetRef("#some-book-mark"_ns).Finalize(url);
+ url->GetRef(out);
+ }
+});
+
+// Note the five calls in the loop, so divide by 100k
+MOZ_GTEST_BENCH(TestStandardURL, DISABLED_NormalizePerf, [] {
+ nsAutoCString result;
+ for (int i = 0; i < 20000; i++) {
+ nsAutoCString encHost("123.232.12.32");
+ ASSERT_EQ(NS_OK, Test_NormalizeIPv4(encHost, result));
+ nsAutoCString encHost2("83.62.12.92");
+ ASSERT_EQ(NS_OK, Test_NormalizeIPv4(encHost2, result));
+ nsAutoCString encHost3("8.7.6.5");
+ ASSERT_EQ(NS_OK, Test_NormalizeIPv4(encHost3, result));
+ nsAutoCString encHost4("111.159.123.220");
+ ASSERT_EQ(NS_OK, Test_NormalizeIPv4(encHost4, result));
+ nsAutoCString encHost5("1.160.204.200");
+ ASSERT_EQ(NS_OK, Test_NormalizeIPv4(encHost5, result));
+ }
+});
+
+// Bug 1394785 - ignore unstable test on OSX
+#ifndef XP_MACOSX
+// Note the five calls in the loop, so divide by 100k
+MOZ_GTEST_BENCH(TestStandardURL, DISABLED_NormalizePerfFails, [] {
+ nsAutoCString result;
+ for (int i = 0; i < 20000; i++) {
+ nsAutoCString encHost("123.292.12.32");
+ ASSERT_EQ(NS_ERROR_FAILURE, Test_NormalizeIPv4(encHost, result));
+ nsAutoCString encHost2("83.62.12.0x13292");
+ ASSERT_EQ(NS_ERROR_FAILURE, Test_NormalizeIPv4(encHost2, result));
+ nsAutoCString encHost3("8.7.6.0xhello");
+ ASSERT_EQ(NS_ERROR_FAILURE, Test_NormalizeIPv4(encHost3, result));
+ nsAutoCString encHost4("111.159.notonmywatch.220");
+ ASSERT_EQ(NS_ERROR_FAILURE, Test_NormalizeIPv4(encHost4, result));
+ nsAutoCString encHost5("1.160.204.20f");
+ ASSERT_EQ(NS_ERROR_FAILURE, Test_NormalizeIPv4(encHost5, result));
+ }
+});
+#endif
+
+TEST(TestStandardURL, Mutator)
+{
+ nsAutoCString out;
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID)
+ .SetSpec("http://example.com"_ns)
+ .Finalize(uri);
+ ASSERT_EQ(rv, NS_OK);
+
+ ASSERT_EQ(uri->GetSpec(out), NS_OK);
+ ASSERT_TRUE(out == "http://example.com/"_ns);
+
+ rv = NS_MutateURI(uri)
+ .SetScheme("ftp"_ns)
+ .SetHost("mozilla.org"_ns)
+ .SetPathQueryRef("/path?query#ref"_ns)
+ .Finalize(uri);
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(uri->GetSpec(out), NS_OK);
+ ASSERT_TRUE(out == "ftp://mozilla.org/path?query#ref"_ns);
+
+ nsCOMPtr<nsIURL> url;
+ rv = NS_MutateURI(uri).SetScheme("https"_ns).Finalize(url);
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(url->GetSpec(out), NS_OK);
+ ASSERT_TRUE(out == "https://mozilla.org/path?query#ref"_ns);
+}
+
+TEST(TestStandardURL, Deserialize_Bug1392739)
+{
+ mozilla::ipc::StandardURLParams standard_params;
+ standard_params.urlType() = nsIStandardURL::URLTYPE_STANDARD;
+ standard_params.spec().Truncate();
+ standard_params.host() = mozilla::ipc::StandardURLSegment(4294967295, 1);
+
+ mozilla::ipc::URIParams params(standard_params);
+
+ nsCOMPtr<nsIURIMutator> mutator =
+ do_CreateInstance(NS_STANDARDURLMUTATOR_CID);
+ ASSERT_EQ(mutator->Deserialize(params), NS_ERROR_FAILURE);
+}
+
+TEST(TestStandardURL, CorruptSerialization)
+{
+ auto spec = "http://user:pass@example.com/path/to/file.ext?query#hash"_ns;
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID)
+ .SetSpec(spec)
+ .Finalize(uri);
+ ASSERT_EQ(rv, NS_OK);
+
+ nsAutoCString serialization;
+ nsCOMPtr<nsISerializable> serializable = do_QueryInterface(uri);
+ ASSERT_TRUE(serializable);
+
+ // Check that the URL is normally serializable.
+ ASSERT_EQ(NS_OK, NS_SerializeToString(serializable, serialization));
+ nsCOMPtr<nsISupports> deserializedObject;
+ ASSERT_EQ(NS_OK, NS_DeserializeObject(serialization,
+ getter_AddRefs(deserializedObject)));
+
+ nsAutoCString canonicalBin;
+ Unused << Base64Decode(serialization, canonicalBin);
+
+// The spec serialization begins at byte 49
+// If the implementation of nsStandardURL::Write changes, this test will need
+// to be adjusted.
+#define SPEC_OFFSET 49
+
+ ASSERT_EQ(Substring(canonicalBin, SPEC_OFFSET, 7), "http://"_ns);
+
+ nsAutoCString corruptedBin = canonicalBin;
+ // change mScheme.mPos
+ corruptedBin.BeginWriting()[SPEC_OFFSET + spec.Length()] = 1;
+ Unused << Base64Encode(corruptedBin, serialization);
+ ASSERT_EQ(
+ NS_ERROR_MALFORMED_URI,
+ NS_DeserializeObject(serialization, getter_AddRefs(deserializedObject)));
+
+ corruptedBin = canonicalBin;
+ // change mScheme.mLen
+ corruptedBin.BeginWriting()[SPEC_OFFSET + spec.Length() + 4] = 127;
+ Unused << Base64Encode(corruptedBin, serialization);
+ ASSERT_EQ(
+ NS_ERROR_MALFORMED_URI,
+ NS_DeserializeObject(serialization, getter_AddRefs(deserializedObject)));
+}
+
+TEST(TestStandardURL, ParseIPv4Num)
+{
+ auto host = "0x.0x.0"_ns;
+
+ int32_t bases[4] = {10, 10, 10, 10};
+ bool onlyBase10 = true; // Track this as a special case
+ int32_t dotIndex[3]; // The positions of the dots in the string
+ int32_t length = static_cast<int32_t>(host.Length());
+
+ ASSERT_EQ(2,
+ Test_ValidateIPv4Number(host, bases, dotIndex, onlyBase10, length));
+
+ nsCString result;
+ ASSERT_EQ(NS_OK, Test_NormalizeIPv4("0x.0x.0"_ns, result));
+
+ uint32_t number;
+ Test_ParseIPv4Number("0x10"_ns, 16, number, 255);
+ ASSERT_EQ(number, (uint32_t)16);
+}
diff --git a/netwerk/test/gtest/TestUDPSocket.cpp b/netwerk/test/gtest/TestUDPSocket.cpp
new file mode 100644
index 0000000000..fe6e9b223d
--- /dev/null
+++ b/netwerk/test/gtest/TestUDPSocket.cpp
@@ -0,0 +1,410 @@
+/* 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 "TestCommon.h"
+#include "gtest/gtest.h"
+#include "nsIUDPSocket.h"
+#include "nsISocketTransport.h"
+#include "nsIOutputStream.h"
+#include "nsINetAddr.h"
+#include "nsITimer.h"
+#include "nsContentUtils.h"
+#include "mozilla/gtest/MozAssertions.h"
+#include "mozilla/net/DNS.h"
+#include "prerror.h"
+#include "nsComponentManagerUtils.h"
+
+#define REQUEST 0x68656c6f
+#define RESPONSE 0x6f6c6568
+#define MULTICAST_TIMEOUT 2000
+
+enum TestPhase { TEST_OUTPUT_STREAM, TEST_SEND_API, TEST_MULTICAST, TEST_NONE };
+
+static TestPhase phase = TEST_NONE;
+
+static bool CheckMessageContent(nsIUDPMessage* aMessage,
+ uint32_t aExpectedContent) {
+ nsCString data;
+ aMessage->GetData(data);
+
+ const char* buffer = data.get();
+ uint32_t len = data.Length();
+
+ FallibleTArray<uint8_t>& rawData = aMessage->GetDataAsTArray();
+ uint32_t rawLen = rawData.Length();
+
+ if (len != rawLen) {
+ ADD_FAILURE() << "Raw data length " << rawLen
+ << " does not match String data length " << len;
+ return false;
+ }
+
+ for (uint32_t i = 0; i < len; i++) {
+ if (buffer[i] != rawData[i]) {
+ ADD_FAILURE();
+ return false;
+ }
+ }
+
+ uint32_t input = 0;
+ for (uint32_t i = 0; i < len; i++) {
+ input += buffer[i] << (8 * i);
+ }
+
+ if (len != sizeof(uint32_t)) {
+ ADD_FAILURE() << "Message length mismatch, expected " << sizeof(uint32_t)
+ << " got " << len;
+ return false;
+ }
+ if (input != aExpectedContent) {
+ ADD_FAILURE() << "Message content mismatch, expected 0x" << std::hex
+ << aExpectedContent << " got 0x" << input;
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * UDPClientListener: listens for incomming UDP packets
+ */
+class UDPClientListener : public nsIUDPSocketListener {
+ protected:
+ virtual ~UDPClientListener();
+
+ public:
+ explicit UDPClientListener(WaitForCondition* waiter) : mWaiter(waiter) {}
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIUDPSOCKETLISTENER
+ nsresult mResult = NS_ERROR_FAILURE;
+ RefPtr<WaitForCondition> mWaiter;
+};
+
+NS_IMPL_ISUPPORTS(UDPClientListener, nsIUDPSocketListener)
+
+UDPClientListener::~UDPClientListener() = default;
+
+NS_IMETHODIMP
+UDPClientListener::OnPacketReceived(nsIUDPSocket* socket,
+ nsIUDPMessage* message) {
+ mResult = NS_OK;
+
+ uint16_t port;
+ nsCString ip;
+ nsCOMPtr<nsINetAddr> fromAddr;
+ message->GetFromAddr(getter_AddRefs(fromAddr));
+ fromAddr->GetPort(&port);
+ fromAddr->GetAddress(ip);
+
+ if (TEST_SEND_API == phase && CheckMessageContent(message, REQUEST)) {
+ uint32_t count;
+ nsTArray<uint8_t> data;
+ const uint32_t dataBuffer = RESPONSE;
+ data.AppendElements((const uint8_t*)&dataBuffer, sizeof(uint32_t));
+ mResult = socket->SendWithAddr(fromAddr, data, &count);
+ if (mResult == NS_OK && count == sizeof(uint32_t)) {
+ SUCCEED();
+ } else {
+ ADD_FAILURE();
+ }
+ return NS_OK;
+ }
+ if (TEST_OUTPUT_STREAM != phase || !CheckMessageContent(message, RESPONSE)) {
+ mResult = NS_ERROR_FAILURE;
+ }
+
+ // Notify thread
+ mWaiter->Notify();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+UDPClientListener::OnStopListening(nsIUDPSocket*, nsresult) {
+ mWaiter->Notify();
+ return NS_OK;
+}
+
+/*
+ * UDPServerListener: listens for incomming UDP packets
+ */
+class UDPServerListener : public nsIUDPSocketListener {
+ protected:
+ virtual ~UDPServerListener();
+
+ public:
+ explicit UDPServerListener(WaitForCondition* waiter) : mWaiter(waiter) {}
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIUDPSOCKETLISTENER
+
+ nsresult mResult = NS_ERROR_FAILURE;
+ RefPtr<WaitForCondition> mWaiter;
+};
+
+NS_IMPL_ISUPPORTS(UDPServerListener, nsIUDPSocketListener)
+
+UDPServerListener::~UDPServerListener() = default;
+
+NS_IMETHODIMP
+UDPServerListener::OnPacketReceived(nsIUDPSocket* socket,
+ nsIUDPMessage* message) {
+ mResult = NS_OK;
+
+ uint16_t port;
+ nsCString ip;
+ nsCOMPtr<nsINetAddr> fromAddr;
+ message->GetFromAddr(getter_AddRefs(fromAddr));
+ fromAddr->GetPort(&port);
+ fromAddr->GetAddress(ip);
+ SUCCEED();
+
+ if (TEST_OUTPUT_STREAM == phase && CheckMessageContent(message, REQUEST)) {
+ nsCOMPtr<nsIOutputStream> outstream;
+ message->GetOutputStream(getter_AddRefs(outstream));
+
+ uint32_t count;
+ const uint32_t data = RESPONSE;
+ mResult = outstream->Write((const char*)&data, sizeof(uint32_t), &count);
+
+ if (mResult == NS_OK && count == sizeof(uint32_t)) {
+ SUCCEED();
+ } else {
+ ADD_FAILURE();
+ }
+ return NS_OK;
+ }
+ if (TEST_MULTICAST == phase && CheckMessageContent(message, REQUEST)) {
+ mResult = NS_OK;
+ } else if (TEST_SEND_API != phase ||
+ !CheckMessageContent(message, RESPONSE)) {
+ mResult = NS_ERROR_FAILURE;
+ }
+
+ // Notify thread
+ mWaiter->Notify();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+UDPServerListener::OnStopListening(nsIUDPSocket*, nsresult) {
+ mWaiter->Notify();
+ return NS_OK;
+}
+
+/**
+ * Multicast timer callback: detects delivery failure
+ */
+class MulticastTimerCallback : public nsITimerCallback, public nsINamed {
+ protected:
+ virtual ~MulticastTimerCallback();
+
+ public:
+ explicit MulticastTimerCallback(WaitForCondition* waiter)
+ : mResult(NS_ERROR_NOT_INITIALIZED), mWaiter(waiter) {}
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSITIMERCALLBACK
+ NS_DECL_NSINAMED
+
+ nsresult mResult;
+ RefPtr<WaitForCondition> mWaiter;
+};
+
+NS_IMPL_ISUPPORTS(MulticastTimerCallback, nsITimerCallback, nsINamed)
+
+MulticastTimerCallback::~MulticastTimerCallback() = default;
+
+NS_IMETHODIMP
+MulticastTimerCallback::Notify(nsITimer* timer) {
+ if (TEST_MULTICAST != phase) {
+ return NS_OK;
+ }
+ // Multicast ping failed
+ printf("Multicast ping timeout expired\n");
+ mResult = NS_ERROR_FAILURE;
+ mWaiter->Notify();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+MulticastTimerCallback::GetName(nsACString& aName) {
+ aName.AssignLiteral("MulticastTimerCallback");
+ return NS_OK;
+}
+
+/**** Main ****/
+
+TEST(TestUDPSocket, TestUDPSocketMain)
+{
+ nsresult rv;
+
+ // Create UDPSocket
+ nsCOMPtr<nsIUDPSocket> server, client;
+ server = do_CreateInstance("@mozilla.org/network/udp-socket;1", &rv);
+ ASSERT_NS_SUCCEEDED(rv);
+
+ client = do_CreateInstance("@mozilla.org/network/udp-socket;1", &rv);
+ ASSERT_NS_SUCCEEDED(rv);
+
+ RefPtr<WaitForCondition> waiter = new WaitForCondition();
+
+ // Create UDPServerListener to process UDP packets
+ RefPtr<UDPServerListener> serverListener = new UDPServerListener(waiter);
+
+ nsCOMPtr<nsIPrincipal> systemPrincipal = nsContentUtils::GetSystemPrincipal();
+
+ // Bind server socket to 0.0.0.0
+ rv = server->Init(0, false, systemPrincipal, true, 0);
+ ASSERT_NS_SUCCEEDED(rv);
+ int32_t serverPort;
+ server->GetPort(&serverPort);
+ server->AsyncListen(serverListener);
+
+ // Bind clinet on arbitrary port
+ RefPtr<UDPClientListener> clientListener = new UDPClientListener(waiter);
+ client->Init(0, false, systemPrincipal, true, 0);
+ client->AsyncListen(clientListener);
+
+ // Write data to server
+ uint32_t count;
+ nsTArray<uint8_t> data;
+ const uint32_t dataBuffer = REQUEST;
+ data.AppendElements((const uint8_t*)&dataBuffer, sizeof(uint32_t));
+
+ phase = TEST_OUTPUT_STREAM;
+ rv = client->Send("127.0.0.1"_ns, serverPort, data, &count);
+ ASSERT_NS_SUCCEEDED(rv);
+ EXPECT_EQ(count, sizeof(uint32_t));
+
+ // Wait for server
+ waiter->Wait(1);
+ ASSERT_NS_SUCCEEDED(serverListener->mResult);
+
+ // Read response from server
+ ASSERT_NS_SUCCEEDED(clientListener->mResult);
+
+ mozilla::net::NetAddr clientAddr;
+ rv = client->GetAddress(&clientAddr);
+ ASSERT_NS_SUCCEEDED(rv);
+ // The client address is 0.0.0.0, but Windows won't receive packets there, so
+ // use 127.0.0.1 explicitly
+ clientAddr.inet.ip = PR_htonl(127 << 24 | 1);
+
+ phase = TEST_SEND_API;
+ rv = server->SendWithAddress(&clientAddr, data.Elements(), data.Length(),
+ &count);
+ ASSERT_NS_SUCCEEDED(rv);
+ EXPECT_EQ(count, sizeof(uint32_t));
+
+ // Wait for server
+ waiter->Wait(1);
+ ASSERT_NS_SUCCEEDED(serverListener->mResult);
+
+ // Read response from server
+ ASSERT_NS_SUCCEEDED(clientListener->mResult);
+
+ // Setup timer to detect multicast failure
+ nsCOMPtr<nsITimer> timer = NS_NewTimer();
+ ASSERT_TRUE(timer);
+ RefPtr<MulticastTimerCallback> timerCb = new MulticastTimerCallback(waiter);
+
+ // Join multicast group
+ printf("Joining multicast group\n");
+ phase = TEST_MULTICAST;
+ mozilla::net::NetAddr multicastAddr;
+ multicastAddr.inet.family = AF_INET;
+ multicastAddr.inet.ip = PR_htonl(224 << 24 | 255);
+ multicastAddr.inet.port = PR_htons(serverPort);
+ rv = server->JoinMulticastAddr(multicastAddr, nullptr);
+ ASSERT_NS_SUCCEEDED(rv);
+
+ // Send multicast ping
+ timerCb->mResult = NS_OK;
+ timer->InitWithCallback(timerCb, MULTICAST_TIMEOUT, nsITimer::TYPE_ONE_SHOT);
+ rv = client->SendWithAddress(&multicastAddr, data.Elements(), data.Length(),
+ &count);
+ ASSERT_NS_SUCCEEDED(rv);
+ EXPECT_EQ(count, sizeof(uint32_t));
+
+ // Wait for server to receive successfully
+ waiter->Wait(1);
+ ASSERT_NS_SUCCEEDED(serverListener->mResult);
+ ASSERT_NS_SUCCEEDED(timerCb->mResult);
+ timer->Cancel();
+
+ // Disable multicast loopback
+ printf("Disable multicast loopback\n");
+ client->SetMulticastLoopback(false);
+ server->SetMulticastLoopback(false);
+
+ // Send multicast ping
+ timerCb->mResult = NS_OK;
+ timer->InitWithCallback(timerCb, MULTICAST_TIMEOUT, nsITimer::TYPE_ONE_SHOT);
+ rv = client->SendWithAddress(&multicastAddr, data.Elements(), data.Length(),
+ &count);
+ ASSERT_NS_SUCCEEDED(rv);
+ EXPECT_EQ(count, sizeof(uint32_t));
+
+ // Wait for server to fail to receive
+ waiter->Wait(1);
+ ASSERT_FALSE(NS_SUCCEEDED(timerCb->mResult));
+ timer->Cancel();
+
+ // Reset state
+ client->SetMulticastLoopback(true);
+ server->SetMulticastLoopback(true);
+
+ // Change multicast interface
+ mozilla::net::NetAddr loopbackAddr;
+ loopbackAddr.inet.family = AF_INET;
+ loopbackAddr.inet.ip = PR_htonl(INADDR_LOOPBACK);
+ client->SetMulticastInterfaceAddr(loopbackAddr);
+
+ // Send multicast ping
+ timerCb->mResult = NS_OK;
+ timer->InitWithCallback(timerCb, MULTICAST_TIMEOUT, nsITimer::TYPE_ONE_SHOT);
+ rv = client->SendWithAddress(&multicastAddr, data.Elements(), data.Length(),
+ &count);
+ ASSERT_NS_SUCCEEDED(rv);
+ EXPECT_EQ(count, sizeof(uint32_t));
+
+ // Wait for server to fail to receive
+ waiter->Wait(1);
+ ASSERT_FALSE(NS_SUCCEEDED(timerCb->mResult));
+ timer->Cancel();
+
+ // Reset state
+ mozilla::net::NetAddr anyAddr;
+ anyAddr.inet.family = AF_INET;
+ anyAddr.inet.ip = PR_htonl(INADDR_ANY);
+ client->SetMulticastInterfaceAddr(anyAddr);
+
+ // Leave multicast group
+ rv = server->LeaveMulticastAddr(multicastAddr, nullptr);
+ ASSERT_NS_SUCCEEDED(rv);
+
+ // Send multicast ping
+ timerCb->mResult = NS_OK;
+ timer->InitWithCallback(timerCb, MULTICAST_TIMEOUT, nsITimer::TYPE_ONE_SHOT);
+ rv = client->SendWithAddress(&multicastAddr, data.Elements(), data.Length(),
+ &count);
+ ASSERT_NS_SUCCEEDED(rv);
+ EXPECT_EQ(count, sizeof(uint32_t));
+
+ // Wait for server to fail to receive
+ waiter->Wait(1);
+ ASSERT_FALSE(NS_SUCCEEDED(timerCb->mResult));
+ timer->Cancel();
+
+ goto close; // suppress warning about unused label
+
+close:
+ // Close server
+ server->Close();
+ client->Close();
+
+ // Wait for client and server to see closing
+ waiter->Wait(2);
+}
diff --git a/netwerk/test/gtest/TestURIMutator.cpp b/netwerk/test/gtest/TestURIMutator.cpp
new file mode 100644
index 0000000000..255ed640eb
--- /dev/null
+++ b/netwerk/test/gtest/TestURIMutator.cpp
@@ -0,0 +1,163 @@
+#include "gtest/gtest.h"
+#include "nsCOMPtr.h"
+#include "nsNetCID.h"
+#include "nsIURIMutator.h"
+#include "nsIURL.h"
+#include "nsThreadPool.h"
+#include "nsNetUtil.h"
+
+TEST(TestURIMutator, Mutator)
+{
+ nsAutoCString out;
+
+ // This test instantiates a new nsStandardURL::Mutator (via contractID)
+ // and uses it to create a new URI.
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID)
+ .SetSpec("http://example.com"_ns)
+ .Finalize(uri);
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(uri->GetSpec(out), NS_OK);
+ ASSERT_TRUE(out == "http://example.com/"_ns);
+
+ // This test verifies that we can use NS_MutateURI to change a URI
+ rv = NS_MutateURI(uri)
+ .SetScheme("ftp"_ns)
+ .SetHost("mozilla.org"_ns)
+ .SetPathQueryRef("/path?query#ref"_ns)
+ .Finalize(uri);
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(uri->GetSpec(out), NS_OK);
+ ASSERT_TRUE(out == "ftp://mozilla.org/path?query#ref"_ns);
+
+ // This test verifies that we can pass nsIURL to Finalize, and
+ nsCOMPtr<nsIURL> url;
+ rv = NS_MutateURI(uri).SetScheme("https"_ns).Finalize(url);
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(url->GetSpec(out), NS_OK);
+ ASSERT_TRUE(out == "https://mozilla.org/path?query#ref"_ns);
+
+ // This test verifies that we can pass nsIURL** to Finalize.
+ // We need to use the explicit template because it's actually passing
+ // getter_AddRefs
+ nsCOMPtr<nsIURL> url2;
+ rv = NS_MutateURI(url)
+ .SetRef("newref"_ns)
+ .Finalize<nsIURL>(getter_AddRefs(url2));
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(url2->GetSpec(out), NS_OK);
+ ASSERT_TRUE(out == "https://mozilla.org/path?query#newref"_ns);
+
+ // This test verifies that we can pass nsIURI** to Finalize.
+ // No need to be explicit.
+ auto functionSetRef = [](nsIURI* aURI, nsIURI** aResult) -> nsresult {
+ return NS_MutateURI(aURI).SetRef("originalRef"_ns).Finalize(aResult);
+ };
+
+ nsCOMPtr<nsIURI> newURI;
+ rv = functionSetRef(url2, getter_AddRefs(newURI));
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(newURI->GetSpec(out), NS_OK);
+ ASSERT_TRUE(out == "https://mozilla.org/path?query#originalRef"_ns);
+
+ // This test verifies that we can pass nsIURI** to Finalize.
+ nsCOMPtr<nsIURI> uri2;
+ rv =
+ NS_MutateURI(url2).SetQuery("newquery"_ns).Finalize(getter_AddRefs(uri2));
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(uri2->GetSpec(out), NS_OK);
+ ASSERT_TRUE(out == "https://mozilla.org/path?newquery#newref"_ns);
+
+ // This test verifies that we can pass nsIURI** to Finalize.
+ // No need to be explicit.
+ auto functionSetQuery = [](nsIURI* aURI, nsIURL** aResult) -> nsresult {
+ return NS_MutateURI(aURI).SetQuery("originalQuery"_ns).Finalize(aResult);
+ };
+
+ nsCOMPtr<nsIURL> newURL;
+ rv = functionSetQuery(uri2, getter_AddRefs(newURL));
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(newURL->GetSpec(out), NS_OK);
+ ASSERT_TRUE(out == "https://mozilla.org/path?originalQuery#newref"_ns);
+
+ // Check that calling Finalize twice will fail.
+ NS_MutateURI mutator(newURL);
+ rv = mutator.SetQuery(""_ns).Finalize(uri2);
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(uri2->GetSpec(out), NS_OK);
+ ASSERT_TRUE(out == "https://mozilla.org/path#newref"_ns);
+ nsCOMPtr<nsIURI> uri3;
+ rv = mutator.Finalize(uri3);
+ ASSERT_EQ(rv, NS_ERROR_NOT_AVAILABLE);
+ ASSERT_TRUE(uri3 == nullptr);
+
+ // Make sure changing scheme updates the default port
+ rv = NS_NewURI(getter_AddRefs(uri),
+ "https://example.org:80/path?query#ref"_ns);
+ ASSERT_EQ(rv, NS_OK);
+ rv = NS_MutateURI(uri).SetScheme("http"_ns).Finalize(uri);
+ ASSERT_EQ(rv, NS_OK);
+ rv = uri->GetSpec(out);
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(out, "http://example.org/path?query#ref"_ns);
+ int32_t port;
+ rv = uri->GetPort(&port);
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(port, -1);
+ rv = uri->GetFilePath(out);
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(out, "/path"_ns);
+ rv = uri->GetQuery(out);
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(out, "query"_ns);
+ rv = uri->GetRef(out);
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(out, "ref"_ns);
+
+ // Make sure changing scheme does not change non-default port
+ rv = NS_NewURI(getter_AddRefs(uri), "https://example.org:123"_ns);
+ ASSERT_EQ(rv, NS_OK);
+ rv = NS_MutateURI(uri).SetScheme("http"_ns).Finalize(uri);
+ ASSERT_EQ(rv, NS_OK);
+ rv = uri->GetSpec(out);
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(out, "http://example.org:123/"_ns);
+ rv = uri->GetPort(&port);
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_EQ(port, 123);
+}
+
+extern MOZ_THREAD_LOCAL(uint32_t) gTlsURLRecursionCount;
+
+TEST(TestURIMutator, OnAnyThread)
+{
+ nsCOMPtr<nsIThreadPool> pool = new nsThreadPool();
+ pool->SetThreadLimit(60);
+
+ pool = new nsThreadPool();
+ for (int i = 0; i < 1000; ++i) {
+ nsCOMPtr<nsIRunnable> task =
+ NS_NewRunnableFunction("gtest-OnAnyThread", []() {
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), "http://example.com"_ns);
+ ASSERT_EQ(rv, NS_OK);
+ nsAutoCString out;
+ ASSERT_EQ(uri->GetSpec(out), NS_OK);
+ ASSERT_TRUE(out == "http://example.com/"_ns);
+ });
+ EXPECT_TRUE(task);
+
+ pool->Dispatch(task, NS_DISPATCH_NORMAL);
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), "http://example.com"_ns);
+ ASSERT_EQ(rv, NS_OK);
+ nsAutoCString out;
+ ASSERT_EQ(uri->GetSpec(out), NS_OK);
+ ASSERT_TRUE(out == "http://example.com/"_ns);
+
+ pool->Shutdown();
+
+ ASSERT_EQ(gTlsURLRecursionCount.get(), 0u);
+}
diff --git a/netwerk/test/gtest/moz.build b/netwerk/test/gtest/moz.build
new file mode 100644
index 0000000000..b6d82b41db
--- /dev/null
+++ b/netwerk/test/gtest/moz.build
@@ -0,0 +1,80 @@
+# -*- Mode: python; c-basic-offset: 4; 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 += [
+ "TestBase64Stream.cpp",
+ "TestBind.cpp",
+ "TestBufferedInputStream.cpp",
+ "TestCommon.cpp",
+ "TestCookie.cpp",
+ "TestDNSPacket.cpp",
+ "TestHeaders.cpp",
+ "TestHttpAtom.cpp",
+ "TestHttpAuthUtils.cpp",
+ "TestHttpChannel.cpp",
+ "TestHttpResponseHead.cpp",
+ "TestInputStreamTransport.cpp",
+ "TestIsValidIp.cpp",
+ "TestLinkHeader.cpp",
+ "TestMIMEInputStream.cpp",
+ "TestMozURL.cpp",
+ "TestProtocolProxyService.cpp",
+ "TestReadStreamToString.cpp",
+ "TestServerTimingHeader.cpp",
+ "TestSocketTransportService.cpp",
+ "TestSSLTokensCache.cpp",
+ "TestStandardURL.cpp",
+ "TestUDPSocket.cpp",
+]
+
+if CONFIG["OS_TARGET"] == "WINNT":
+ UNIFIED_SOURCES += [
+ "TestNamedPipeService.cpp",
+ ]
+
+# skip the test on windows10-aarch64
+if not (CONFIG["OS_TARGET"] == "WINNT" and CONFIG["TARGET_CPU"] == "aarch64"):
+ UNIFIED_SOURCES += [
+ "TestPACMan.cpp",
+ "TestURIMutator.cpp",
+ ]
+
+# run the test on windows only
+if CONFIG["OS_TARGET"] == "WINNT":
+ UNIFIED_SOURCES += ["TestNetworkLinkIdHashingWindows.cpp"]
+
+# run the test on mac only
+if CONFIG["OS_TARGET"] == "Darwin":
+ UNIFIED_SOURCES += ["TestNetworkLinkIdHashingDarwin.cpp"]
+
+TEST_HARNESS_FILES.gtest += [
+ "urltestdata.json",
+]
+
+USE_LIBS += [
+ "jsoncpp",
+]
+
+LOCAL_INCLUDES += [
+ "/netwerk/base",
+ "/netwerk/cookie",
+ "/toolkit/components/jsoncpp/include",
+ "/xpcom/tests/gtest",
+]
+
+# windows includes only
+if CONFIG["OS_TARGET"] == "WINNT":
+ LOCAL_INCLUDES += ["/netwerk/system/win32"]
+
+# mac includes only
+if CONFIG["OS_TARGET"] == "Darwin":
+ LOCAL_INCLUDES += ["/netwerk/system/mac"]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul-gtest"
+
+LOCAL_INCLUDES += ["!/xpcom", "/xpcom/components"]
diff --git a/netwerk/test/gtest/urltestdata-orig.json b/netwerk/test/gtest/urltestdata-orig.json
new file mode 100644
index 0000000000..5565c938fd
--- /dev/null
+++ b/netwerk/test/gtest/urltestdata-orig.json
@@ -0,0 +1,6148 @@
+[
+ "# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/script-tests/segments.js",
+ {
+ "input": "http://example\t.\norg",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://user:pass@foo:21/bar;par?b#c",
+ "base": "http://example.org/foo/bar",
+ "href": "http://user:pass@foo:21/bar;par?b#c",
+ "origin": "http://foo:21",
+ "protocol": "http:",
+ "username": "user",
+ "password": "pass",
+ "host": "foo:21",
+ "hostname": "foo",
+ "port": "21",
+ "pathname": "/bar;par",
+ "search": "?b",
+ "hash": "#c"
+ },
+ {
+ "input": "https://test:@test",
+ "base": "about:blank",
+ "href": "https://test@test/",
+ "origin": "https://test",
+ "protocol": "https:",
+ "username": "test",
+ "password": "",
+ "host": "test",
+ "hostname": "test",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "https://:@test",
+ "base": "about:blank",
+ "href": "https://test/",
+ "origin": "https://test",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "test",
+ "hostname": "test",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "non-special://test:@test/x",
+ "base": "about:blank",
+ "href": "non-special://test@test/x",
+ "origin": "null",
+ "protocol": "non-special:",
+ "username": "test",
+ "password": "",
+ "host": "test",
+ "hostname": "test",
+ "port": "",
+ "pathname": "/x",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "non-special://:@test/x",
+ "base": "about:blank",
+ "href": "non-special://test/x",
+ "origin": "null",
+ "protocol": "non-special:",
+ "username": "",
+ "password": "",
+ "host": "test",
+ "hostname": "test",
+ "port": "",
+ "pathname": "/x",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:foo.com",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/foo.com",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/foo.com",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "\t :foo.com \n",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/:foo.com",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/:foo.com",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": " foo.com ",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/foo.com",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/foo.com",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "a:\t foo.com",
+ "base": "http://example.org/foo/bar",
+ "href": "a: foo.com",
+ "origin": "null",
+ "protocol": "a:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": " foo.com",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://f:21/ b ? d # e ",
+ "base": "http://example.org/foo/bar",
+ "href": "http://f:21/%20b%20?%20d%20# e",
+ "origin": "http://f:21",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "f:21",
+ "hostname": "f",
+ "port": "21",
+ "pathname": "/%20b%20",
+ "search": "?%20d%20",
+ "hash": "# e"
+ },
+ {
+ "input": "lolscheme:x x#x x",
+ "base": "about:blank",
+ "href": "lolscheme:x x#x x",
+ "protocol": "lolscheme:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "x x",
+ "search": "",
+ "hash": "#x x"
+ },
+ {
+ "input": "http://f:/c",
+ "base": "http://example.org/foo/bar",
+ "href": "http://f/c",
+ "origin": "http://f",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "f",
+ "hostname": "f",
+ "port": "",
+ "pathname": "/c",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://f:0/c",
+ "base": "http://example.org/foo/bar",
+ "href": "http://f:0/c",
+ "origin": "http://f:0",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "f:0",
+ "hostname": "f",
+ "port": "0",
+ "pathname": "/c",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://f:00000000000000/c",
+ "base": "http://example.org/foo/bar",
+ "href": "http://f:0/c",
+ "origin": "http://f:0",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "f:0",
+ "hostname": "f",
+ "port": "0",
+ "pathname": "/c",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://f:00000000000000000000080/c",
+ "base": "http://example.org/foo/bar",
+ "href": "http://f/c",
+ "origin": "http://f",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "f",
+ "hostname": "f",
+ "port": "",
+ "pathname": "/c",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://f:b/c",
+ "base": "http://example.org/foo/bar",
+ "failure": true
+ },
+ {
+ "input": "http://f: /c",
+ "base": "http://example.org/foo/bar",
+ "failure": true
+ },
+ {
+ "input": "http://f:\n/c",
+ "base": "http://example.org/foo/bar",
+ "href": "http://f/c",
+ "origin": "http://f",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "f",
+ "hostname": "f",
+ "port": "",
+ "pathname": "/c",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://f:fifty-two/c",
+ "base": "http://example.org/foo/bar",
+ "failure": true
+ },
+ {
+ "input": "http://f:999999/c",
+ "base": "http://example.org/foo/bar",
+ "failure": true
+ },
+ {
+ "input": "non-special://f:999999/c",
+ "base": "http://example.org/foo/bar",
+ "failure": true
+ },
+ {
+ "input": "http://f: 21 / b ? d # e ",
+ "base": "http://example.org/foo/bar",
+ "failure": true
+ },
+ {
+ "input": "",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/bar",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": " \t",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/bar",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": ":foo.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/:foo.com/",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/:foo.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": ":foo.com\\",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/:foo.com/",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/:foo.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": ":",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/:",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/:",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": ":a",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/:a",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/:a",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": ":/",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/:/",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": ":\\",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/:/",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": ":#",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/:#",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/:",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "#",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/bar#",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "#/",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/bar#/",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "",
+ "hash": "#/"
+ },
+ {
+ "input": "#\\",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/bar#\\",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "",
+ "hash": "#\\"
+ },
+ {
+ "input": "#;?",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/bar#;?",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "",
+ "hash": "#;?"
+ },
+ {
+ "input": "?",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/bar?",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": ":23",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/:23",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/:23",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/:23",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/:23",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/:23",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "::",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/::",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/::",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "::23",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/::23",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/::23",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "foo://",
+ "base": "http://example.org/foo/bar",
+ "href": "foo:///",
+ "origin": "null",
+ "protocol": "foo:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://a:b@c:29/d",
+ "base": "http://example.org/foo/bar",
+ "href": "http://a:b@c:29/d",
+ "origin": "http://c:29",
+ "protocol": "http:",
+ "username": "a",
+ "password": "b",
+ "host": "c:29",
+ "hostname": "c",
+ "port": "29",
+ "pathname": "/d",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http::@c:29",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/:@c:29",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/:@c:29",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://&a:foo(b]c@d:2/",
+ "base": "http://example.org/foo/bar",
+ "href": "http://&a:foo(b%5Dc@d:2/",
+ "origin": "http://d:2",
+ "protocol": "http:",
+ "username": "&a",
+ "password": "foo(b%5Dc",
+ "host": "d:2",
+ "hostname": "d",
+ "port": "2",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://::@c@d:2",
+ "base": "http://example.org/foo/bar",
+ "href": "http://:%3A%40c@d:2/",
+ "origin": "http://d:2",
+ "protocol": "http:",
+ "username": "",
+ "password": "%3A%40c",
+ "host": "d:2",
+ "hostname": "d",
+ "port": "2",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://foo.com:b@d/",
+ "base": "http://example.org/foo/bar",
+ "href": "http://foo.com:b@d/",
+ "origin": "http://d",
+ "protocol": "http:",
+ "username": "foo.com",
+ "password": "b",
+ "host": "d",
+ "hostname": "d",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://foo.com/\\@",
+ "base": "http://example.org/foo/bar",
+ "href": "http://foo.com//@",
+ "origin": "http://foo.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "foo.com",
+ "hostname": "foo.com",
+ "port": "",
+ "pathname": "//@",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:\\\\foo.com\\",
+ "base": "http://example.org/foo/bar",
+ "href": "http://foo.com/",
+ "origin": "http://foo.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "foo.com",
+ "hostname": "foo.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:\\\\a\\b:c\\d@foo.com\\",
+ "base": "http://example.org/foo/bar",
+ "href": "http://a/b:c/d@foo.com/",
+ "origin": "http://a",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "a",
+ "hostname": "a",
+ "port": "",
+ "pathname": "/b:c/d@foo.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "foo:/",
+ "base": "http://example.org/foo/bar",
+ "href": "foo:/",
+ "origin": "null",
+ "protocol": "foo:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "foo:/bar.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "foo:/bar.com/",
+ "origin": "null",
+ "protocol": "foo:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/bar.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "foo://///////",
+ "base": "http://example.org/foo/bar",
+ "href": "foo://///////",
+ "origin": "null",
+ "protocol": "foo:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "///////",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "foo://///////bar.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "foo://///////bar.com/",
+ "origin": "null",
+ "protocol": "foo:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "///////bar.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "foo:////://///",
+ "base": "http://example.org/foo/bar",
+ "href": "foo:////://///",
+ "origin": "null",
+ "protocol": "foo:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//://///",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "c:/foo",
+ "base": "http://example.org/foo/bar",
+ "href": "c:/foo",
+ "origin": "null",
+ "protocol": "c:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/foo",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "//foo/bar",
+ "base": "http://example.org/foo/bar",
+ "href": "http://foo/bar",
+ "origin": "http://foo",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "foo",
+ "hostname": "foo",
+ "port": "",
+ "pathname": "/bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://foo/path;a??e#f#g",
+ "base": "http://example.org/foo/bar",
+ "href": "http://foo/path;a??e#f#g",
+ "origin": "http://foo",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "foo",
+ "hostname": "foo",
+ "port": "",
+ "pathname": "/path;a",
+ "search": "??e",
+ "hash": "#f#g"
+ },
+ {
+ "input": "http://foo/abcd?efgh?ijkl",
+ "base": "http://example.org/foo/bar",
+ "href": "http://foo/abcd?efgh?ijkl",
+ "origin": "http://foo",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "foo",
+ "hostname": "foo",
+ "port": "",
+ "pathname": "/abcd",
+ "search": "?efgh?ijkl",
+ "hash": ""
+ },
+ {
+ "input": "http://foo/abcd#foo?bar",
+ "base": "http://example.org/foo/bar",
+ "href": "http://foo/abcd#foo?bar",
+ "origin": "http://foo",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "foo",
+ "hostname": "foo",
+ "port": "",
+ "pathname": "/abcd",
+ "search": "",
+ "hash": "#foo?bar"
+ },
+ {
+ "input": "[61:24:74]:98",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/[61:24:74]:98",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/[61:24:74]:98",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:[61:27]/:foo",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/[61:27]/:foo",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/[61:27]/:foo",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://[1::2]:3:4",
+ "base": "http://example.org/foo/bar",
+ "failure": true
+ },
+ {
+ "input": "http://2001::1",
+ "base": "http://example.org/foo/bar",
+ "failure": true
+ },
+ {
+ "input": "http://2001::1]",
+ "base": "http://example.org/foo/bar",
+ "failure": true
+ },
+ {
+ "input": "http://2001::1]:80",
+ "base": "http://example.org/foo/bar",
+ "failure": true
+ },
+ {
+ "input": "http://[2001::1]",
+ "base": "http://example.org/foo/bar",
+ "href": "http://[2001::1]/",
+ "origin": "http://[2001::1]",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "[2001::1]",
+ "hostname": "[2001::1]",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://[::127.0.0.1]",
+ "base": "http://example.org/foo/bar",
+ "href": "http://[::7f00:1]/",
+ "origin": "http://[::7f00:1]",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "[::7f00:1]",
+ "hostname": "[::7f00:1]",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://[0:0:0:0:0:0:13.1.68.3]",
+ "base": "http://example.org/foo/bar",
+ "href": "http://[::d01:4403]/",
+ "origin": "http://[::d01:4403]",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "[::d01:4403]",
+ "hostname": "[::d01:4403]",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://[2001::1]:80",
+ "base": "http://example.org/foo/bar",
+ "href": "http://[2001::1]/",
+ "origin": "http://[2001::1]",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "[2001::1]",
+ "hostname": "[2001::1]",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/example.com/",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ftp:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "ftp://example.com/",
+ "origin": "ftp://example.com",
+ "protocol": "ftp:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "https:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "https://example.com/",
+ "origin": "https://example.com",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "madeupscheme:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "madeupscheme:/example.com/",
+ "origin": "null",
+ "protocol": "madeupscheme:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "file:///example.com/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://example:1/",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "file://example:test/",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "file://example%/",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "file://[example]/",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "ftps:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "ftps:/example.com/",
+ "origin": "null",
+ "protocol": "ftps:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "gopher:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "gopher://example.com/",
+ "origin": "gopher://example.com",
+ "protocol": "gopher:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ws:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "ws://example.com/",
+ "origin": "ws://example.com",
+ "protocol": "ws:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "wss:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "wss://example.com/",
+ "origin": "wss://example.com",
+ "protocol": "wss:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "data:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "data:/example.com/",
+ "origin": "null",
+ "protocol": "data:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "javascript:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "javascript:/example.com/",
+ "origin": "null",
+ "protocol": "javascript:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "mailto:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "mailto:/example.com/",
+ "origin": "null",
+ "protocol": "mailto:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/example.com/",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ftp:example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "ftp://example.com/",
+ "origin": "ftp://example.com",
+ "protocol": "ftp:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "https:example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "https://example.com/",
+ "origin": "https://example.com",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "madeupscheme:example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "madeupscheme:example.com/",
+ "origin": "null",
+ "protocol": "madeupscheme:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ftps:example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "ftps:example.com/",
+ "origin": "null",
+ "protocol": "ftps:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "gopher:example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "gopher://example.com/",
+ "origin": "gopher://example.com",
+ "protocol": "gopher:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ws:example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "ws://example.com/",
+ "origin": "ws://example.com",
+ "protocol": "ws:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "wss:example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "wss://example.com/",
+ "origin": "wss://example.com",
+ "protocol": "wss:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "data:example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "data:example.com/",
+ "origin": "null",
+ "protocol": "data:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "javascript:example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "javascript:example.com/",
+ "origin": "null",
+ "protocol": "javascript:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "mailto:example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "mailto:example.com/",
+ "origin": "null",
+ "protocol": "mailto:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/a/b/c",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/a/b/c",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/a/b/c",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/a/ /c",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/a/%20/c",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/a/%20/c",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/a%2fc",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/a%2fc",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/a%2fc",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/a/%2f/c",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/a/%2f/c",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/a/%2f/c",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "#β",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/bar#%CE%B2",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "",
+ "hash": "#%CE%B2"
+ },
+ {
+ "input": "data:text/html,test#test",
+ "base": "http://example.org/foo/bar",
+ "href": "data:text/html,test#test",
+ "origin": "null",
+ "protocol": "data:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "text/html,test",
+ "search": "",
+ "hash": "#test"
+ },
+ {
+ "input": "tel:1234567890",
+ "base": "http://example.org/foo/bar",
+ "href": "tel:1234567890",
+ "origin": "null",
+ "protocol": "tel:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "1234567890",
+ "search": "",
+ "hash": ""
+ },
+ "# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/file.html",
+ {
+ "input": "file:c:\\foo\\bar.html",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///c:/foo/bar.html",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/c:/foo/bar.html",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": " File:c|////foo\\bar.html",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///c:////foo/bar.html",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/c:////foo/bar.html",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "C|/foo/bar",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///C:/foo/bar",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/C|\\foo\\bar",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///C:/foo/bar",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "//C|/foo/bar",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///C:/foo/bar",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "//server/file",
+ "base": "file:///tmp/mock/path",
+ "href": "file://server/file",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "server",
+ "hostname": "server",
+ "port": "",
+ "pathname": "/file",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "\\\\server\\file",
+ "base": "file:///tmp/mock/path",
+ "href": "file://server/file",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "server",
+ "hostname": "server",
+ "port": "",
+ "pathname": "/file",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/\\server/file",
+ "base": "file:///tmp/mock/path",
+ "href": "file://server/file",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "server",
+ "hostname": "server",
+ "port": "",
+ "pathname": "/file",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:///foo/bar.txt",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///foo/bar.txt",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/foo/bar.txt",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:///home/me",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///home/me",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/home/me",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "//",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "///",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "///test",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///test",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://test",
+ "base": "file:///tmp/mock/path",
+ "href": "file://test/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "test",
+ "hostname": "test",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://localhost",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://localhost/",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://localhost/test",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///test",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "test",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///tmp/mock/test",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/tmp/mock/test",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:test",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///tmp/mock/test",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/tmp/mock/test",
+ "search": "",
+ "hash": ""
+ },
+ "# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/script-tests/path.js",
+ {
+ "input": "http://example.com/././foo",
+ "base": "about:blank",
+ "href": "http://example.com/foo",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/./.foo",
+ "base": "about:blank",
+ "href": "http://example.com/.foo",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/.foo",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/.",
+ "base": "about:blank",
+ "href": "http://example.com/foo/",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/./",
+ "base": "about:blank",
+ "href": "http://example.com/foo/",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/bar/..",
+ "base": "about:blank",
+ "href": "http://example.com/foo/",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/bar/../",
+ "base": "about:blank",
+ "href": "http://example.com/foo/",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/..bar",
+ "base": "about:blank",
+ "href": "http://example.com/foo/..bar",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo/..bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/bar/../ton",
+ "base": "about:blank",
+ "href": "http://example.com/foo/ton",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo/ton",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/bar/../ton/../../a",
+ "base": "about:blank",
+ "href": "http://example.com/a",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/a",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/../../..",
+ "base": "about:blank",
+ "href": "http://example.com/",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/../../../ton",
+ "base": "about:blank",
+ "href": "http://example.com/ton",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/ton",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/%2e",
+ "base": "about:blank",
+ "href": "http://example.com/foo/",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/%2e%2",
+ "base": "about:blank",
+ "href": "http://example.com/foo/%2e%2",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo/%2e%2",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/%2e./%2e%2e/.%2e/%2e.bar",
+ "base": "about:blank",
+ "href": "http://example.com/%2e.bar",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/%2e.bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com////../..",
+ "base": "about:blank",
+ "href": "http://example.com//",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "//",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/bar//../..",
+ "base": "about:blank",
+ "href": "http://example.com/foo/",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/bar//..",
+ "base": "about:blank",
+ "href": "http://example.com/foo/bar/",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo/bar/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo",
+ "base": "about:blank",
+ "href": "http://example.com/foo",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/%20foo",
+ "base": "about:blank",
+ "href": "http://example.com/%20foo",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/%20foo",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo%",
+ "base": "about:blank",
+ "href": "http://example.com/foo%",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo%",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo%2",
+ "base": "about:blank",
+ "href": "http://example.com/foo%2",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo%2",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo%2zbar",
+ "base": "about:blank",
+ "href": "http://example.com/foo%2zbar",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo%2zbar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo%2©zbar",
+ "base": "about:blank",
+ "href": "http://example.com/foo%2%C3%82%C2%A9zbar",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo%2%C3%82%C2%A9zbar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo%41%7a",
+ "base": "about:blank",
+ "href": "http://example.com/foo%41%7a",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo%41%7a",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo\t\u0091%91",
+ "base": "about:blank",
+ "href": "http://example.com/foo%C2%91%91",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo%C2%91%91",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo%00%51",
+ "base": "about:blank",
+ "href": "http://example.com/foo%00%51",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo%00%51",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/(%28:%3A%29)",
+ "base": "about:blank",
+ "href": "http://example.com/(%28:%3A%29)",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/(%28:%3A%29)",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/%3A%3a%3C%3c",
+ "base": "about:blank",
+ "href": "http://example.com/%3A%3a%3C%3c",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/%3A%3a%3C%3c",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo\tbar",
+ "base": "about:blank",
+ "href": "http://example.com/foobar",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foobar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com\\\\foo\\\\bar",
+ "base": "about:blank",
+ "href": "http://example.com//foo//bar",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "//foo//bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/%7Ffp3%3Eju%3Dduvgw%3Dd",
+ "base": "about:blank",
+ "href": "http://example.com/%7Ffp3%3Eju%3Dduvgw%3Dd",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/%7Ffp3%3Eju%3Dduvgw%3Dd",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/@asdf%40",
+ "base": "about:blank",
+ "href": "http://example.com/@asdf%40",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/@asdf%40",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/你好你好",
+ "base": "about:blank",
+ "href": "http://example.com/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/‥/foo",
+ "base": "about:blank",
+ "href": "http://example.com/%E2%80%A5/foo",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/%E2%80%A5/foo",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com//foo",
+ "base": "about:blank",
+ "href": "http://example.com/%EF%BB%BF/foo",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/%EF%BB%BF/foo",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/‮/foo/‭/bar",
+ "base": "about:blank",
+ "href": "http://example.com/%E2%80%AE/foo/%E2%80%AD/bar",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/%E2%80%AE/foo/%E2%80%AD/bar",
+ "search": "",
+ "hash": ""
+ },
+ "# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/script-tests/relative.js",
+ {
+ "input": "http://www.google.com/foo?bar=baz#",
+ "base": "about:blank",
+ "href": "http://www.google.com/foo?bar=baz#",
+ "origin": "http://www.google.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.google.com",
+ "hostname": "www.google.com",
+ "port": "",
+ "pathname": "/foo",
+ "search": "?bar=baz",
+ "hash": ""
+ },
+ {
+ "input": "http://www.google.com/foo?bar=baz# »",
+ "base": "about:blank",
+ "href": "http://www.google.com/foo?bar=baz# %C2%BB",
+ "origin": "http://www.google.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.google.com",
+ "hostname": "www.google.com",
+ "port": "",
+ "pathname": "/foo",
+ "search": "?bar=baz",
+ "hash": "# %C2%BB"
+ },
+ {
+ "input": "data:test# »",
+ "base": "about:blank",
+ "href": "data:test# %C2%BB",
+ "origin": "null",
+ "protocol": "data:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "test",
+ "search": "",
+ "hash": "# %C2%BB"
+ },
+ {
+ "input": "http://www.google.com",
+ "base": "about:blank",
+ "href": "http://www.google.com/",
+ "origin": "http://www.google.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.google.com",
+ "hostname": "www.google.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://192.0x00A80001",
+ "base": "about:blank",
+ "href": "http://192.168.0.1/",
+ "origin": "http://192.168.0.1",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "192.168.0.1",
+ "hostname": "192.168.0.1",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://www/foo%2Ehtml",
+ "base": "about:blank",
+ "href": "http://www/foo%2Ehtml",
+ "origin": "http://www",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www",
+ "hostname": "www",
+ "port": "",
+ "pathname": "/foo%2Ehtml",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://www/foo/%2E/html",
+ "base": "about:blank",
+ "href": "http://www/foo/html",
+ "origin": "http://www",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www",
+ "hostname": "www",
+ "port": "",
+ "pathname": "/foo/html",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://user:pass@/",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "http://%25DOMAIN:foobar@foodomain.com/",
+ "base": "about:blank",
+ "href": "http://%25DOMAIN:foobar@foodomain.com/",
+ "origin": "http://foodomain.com",
+ "protocol": "http:",
+ "username": "%25DOMAIN",
+ "password": "foobar",
+ "host": "foodomain.com",
+ "hostname": "foodomain.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:\\\\www.google.com\\foo",
+ "base": "about:blank",
+ "href": "http://www.google.com/foo",
+ "origin": "http://www.google.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.google.com",
+ "hostname": "www.google.com",
+ "port": "",
+ "pathname": "/foo",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://foo:80/",
+ "base": "about:blank",
+ "href": "http://foo/",
+ "origin": "http://foo",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "foo",
+ "hostname": "foo",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://foo:81/",
+ "base": "about:blank",
+ "href": "http://foo:81/",
+ "origin": "http://foo:81",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "foo:81",
+ "hostname": "foo",
+ "port": "81",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "httpa://foo:80/",
+ "base": "about:blank",
+ "href": "httpa://foo:80/",
+ "origin": "null",
+ "protocol": "httpa:",
+ "username": "",
+ "password": "",
+ "host": "foo:80",
+ "hostname": "foo",
+ "port": "80",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://foo:-80/",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "https://foo:443/",
+ "base": "about:blank",
+ "href": "https://foo/",
+ "origin": "https://foo",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "foo",
+ "hostname": "foo",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "https://foo:80/",
+ "base": "about:blank",
+ "href": "https://foo:80/",
+ "origin": "https://foo:80",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "foo:80",
+ "hostname": "foo",
+ "port": "80",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ftp://foo:21/",
+ "base": "about:blank",
+ "href": "ftp://foo/",
+ "origin": "ftp://foo",
+ "protocol": "ftp:",
+ "username": "",
+ "password": "",
+ "host": "foo",
+ "hostname": "foo",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ftp://foo:80/",
+ "base": "about:blank",
+ "href": "ftp://foo:80/",
+ "origin": "ftp://foo:80",
+ "protocol": "ftp:",
+ "username": "",
+ "password": "",
+ "host": "foo:80",
+ "hostname": "foo",
+ "port": "80",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "gopher://foo:70/",
+ "base": "about:blank",
+ "href": "gopher://foo/",
+ "origin": "gopher://foo",
+ "protocol": "gopher:",
+ "username": "",
+ "password": "",
+ "host": "foo",
+ "hostname": "foo",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "gopher://foo:443/",
+ "base": "about:blank",
+ "href": "gopher://foo:443/",
+ "origin": "gopher://foo:443",
+ "protocol": "gopher:",
+ "username": "",
+ "password": "",
+ "host": "foo:443",
+ "hostname": "foo",
+ "port": "443",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ws://foo:80/",
+ "base": "about:blank",
+ "href": "ws://foo/",
+ "origin": "ws://foo",
+ "protocol": "ws:",
+ "username": "",
+ "password": "",
+ "host": "foo",
+ "hostname": "foo",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ws://foo:81/",
+ "base": "about:blank",
+ "href": "ws://foo:81/",
+ "origin": "ws://foo:81",
+ "protocol": "ws:",
+ "username": "",
+ "password": "",
+ "host": "foo:81",
+ "hostname": "foo",
+ "port": "81",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ws://foo:443/",
+ "base": "about:blank",
+ "href": "ws://foo:443/",
+ "origin": "ws://foo:443",
+ "protocol": "ws:",
+ "username": "",
+ "password": "",
+ "host": "foo:443",
+ "hostname": "foo",
+ "port": "443",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ws://foo:815/",
+ "base": "about:blank",
+ "href": "ws://foo:815/",
+ "origin": "ws://foo:815",
+ "protocol": "ws:",
+ "username": "",
+ "password": "",
+ "host": "foo:815",
+ "hostname": "foo",
+ "port": "815",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "wss://foo:80/",
+ "base": "about:blank",
+ "href": "wss://foo:80/",
+ "origin": "wss://foo:80",
+ "protocol": "wss:",
+ "username": "",
+ "password": "",
+ "host": "foo:80",
+ "hostname": "foo",
+ "port": "80",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "wss://foo:81/",
+ "base": "about:blank",
+ "href": "wss://foo:81/",
+ "origin": "wss://foo:81",
+ "protocol": "wss:",
+ "username": "",
+ "password": "",
+ "host": "foo:81",
+ "hostname": "foo",
+ "port": "81",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "wss://foo:443/",
+ "base": "about:blank",
+ "href": "wss://foo/",
+ "origin": "wss://foo",
+ "protocol": "wss:",
+ "username": "",
+ "password": "",
+ "host": "foo",
+ "hostname": "foo",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "wss://foo:815/",
+ "base": "about:blank",
+ "href": "wss://foo:815/",
+ "origin": "wss://foo:815",
+ "protocol": "wss:",
+ "username": "",
+ "password": "",
+ "host": "foo:815",
+ "hostname": "foo",
+ "port": "815",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:/example.com/",
+ "base": "about:blank",
+ "href": "http://example.com/",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ftp:/example.com/",
+ "base": "about:blank",
+ "href": "ftp://example.com/",
+ "origin": "ftp://example.com",
+ "protocol": "ftp:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "https:/example.com/",
+ "base": "about:blank",
+ "href": "https://example.com/",
+ "origin": "https://example.com",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "madeupscheme:/example.com/",
+ "base": "about:blank",
+ "href": "madeupscheme:/example.com/",
+ "origin": "null",
+ "protocol": "madeupscheme:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:/example.com/",
+ "base": "about:blank",
+ "href": "file:///example.com/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ftps:/example.com/",
+ "base": "about:blank",
+ "href": "ftps:/example.com/",
+ "origin": "null",
+ "protocol": "ftps:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "gopher:/example.com/",
+ "base": "about:blank",
+ "href": "gopher://example.com/",
+ "origin": "gopher://example.com",
+ "protocol": "gopher:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ws:/example.com/",
+ "base": "about:blank",
+ "href": "ws://example.com/",
+ "origin": "ws://example.com",
+ "protocol": "ws:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "wss:/example.com/",
+ "base": "about:blank",
+ "href": "wss://example.com/",
+ "origin": "wss://example.com",
+ "protocol": "wss:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "data:/example.com/",
+ "base": "about:blank",
+ "href": "data:/example.com/",
+ "origin": "null",
+ "protocol": "data:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "javascript:/example.com/",
+ "base": "about:blank",
+ "href": "javascript:/example.com/",
+ "origin": "null",
+ "protocol": "javascript:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "mailto:/example.com/",
+ "base": "about:blank",
+ "href": "mailto:/example.com/",
+ "origin": "null",
+ "protocol": "mailto:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:example.com/",
+ "base": "about:blank",
+ "href": "http://example.com/",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ftp:example.com/",
+ "base": "about:blank",
+ "href": "ftp://example.com/",
+ "origin": "ftp://example.com",
+ "protocol": "ftp:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "https:example.com/",
+ "base": "about:blank",
+ "href": "https://example.com/",
+ "origin": "https://example.com",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "madeupscheme:example.com/",
+ "base": "about:blank",
+ "href": "madeupscheme:example.com/",
+ "origin": "null",
+ "protocol": "madeupscheme:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ftps:example.com/",
+ "base": "about:blank",
+ "href": "ftps:example.com/",
+ "origin": "null",
+ "protocol": "ftps:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "gopher:example.com/",
+ "base": "about:blank",
+ "href": "gopher://example.com/",
+ "origin": "gopher://example.com",
+ "protocol": "gopher:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ws:example.com/",
+ "base": "about:blank",
+ "href": "ws://example.com/",
+ "origin": "ws://example.com",
+ "protocol": "ws:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "wss:example.com/",
+ "base": "about:blank",
+ "href": "wss://example.com/",
+ "origin": "wss://example.com",
+ "protocol": "wss:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "data:example.com/",
+ "base": "about:blank",
+ "href": "data:example.com/",
+ "origin": "null",
+ "protocol": "data:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "javascript:example.com/",
+ "base": "about:blank",
+ "href": "javascript:example.com/",
+ "origin": "null",
+ "protocol": "javascript:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "mailto:example.com/",
+ "base": "about:blank",
+ "href": "mailto:example.com/",
+ "origin": "null",
+ "protocol": "mailto:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "example.com/",
+ "search": "",
+ "hash": ""
+ },
+ "# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/segments-userinfo-vs-host.html",
+ {
+ "input": "http:@www.example.com",
+ "base": "about:blank",
+ "href": "http://www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:/@www.example.com",
+ "base": "about:blank",
+ "href": "http://www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://@www.example.com",
+ "base": "about:blank",
+ "href": "http://www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:a:b@www.example.com",
+ "base": "about:blank",
+ "href": "http://a:b@www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "a",
+ "password": "b",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:/a:b@www.example.com",
+ "base": "about:blank",
+ "href": "http://a:b@www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "a",
+ "password": "b",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://a:b@www.example.com",
+ "base": "about:blank",
+ "href": "http://a:b@www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "a",
+ "password": "b",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://@pple.com",
+ "base": "about:blank",
+ "href": "http://pple.com/",
+ "origin": "http://pple.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "pple.com",
+ "hostname": "pple.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http::b@www.example.com",
+ "base": "about:blank",
+ "href": "http://:b@www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "b",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:/:b@www.example.com",
+ "base": "about:blank",
+ "href": "http://:b@www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "b",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://:b@www.example.com",
+ "base": "about:blank",
+ "href": "http://:b@www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "b",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:/:@/www.example.com",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "http://user@/www.example.com",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "http:@/www.example.com",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "http:/@/www.example.com",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "http://@/www.example.com",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "https:@/www.example.com",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "http:a:b@/www.example.com",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "http:/a:b@/www.example.com",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "http://a:b@/www.example.com",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "http::@/www.example.com",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "http:a:@www.example.com",
+ "base": "about:blank",
+ "href": "http://a@www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "a",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:/a:@www.example.com",
+ "base": "about:blank",
+ "href": "http://a@www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "a",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://a:@www.example.com",
+ "base": "about:blank",
+ "href": "http://a@www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "a",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://www.@pple.com",
+ "base": "about:blank",
+ "href": "http://www.@pple.com/",
+ "origin": "http://pple.com",
+ "protocol": "http:",
+ "username": "www.",
+ "password": "",
+ "host": "pple.com",
+ "hostname": "pple.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:@:www.example.com",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "http:/@:www.example.com",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "http://@:www.example.com",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "http://:@www.example.com",
+ "base": "about:blank",
+ "href": "http://www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "# Others",
+ {
+ "input": "/",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/test.txt",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example.com/test.txt",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/test.txt",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": ".",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "..",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "test.txt",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example.com/test.txt",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/test.txt",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "./test.txt",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example.com/test.txt",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/test.txt",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "../test.txt",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example.com/test.txt",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/test.txt",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "../aaa/test.txt",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example.com/aaa/test.txt",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/aaa/test.txt",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "../../test.txt",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example.com/test.txt",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/test.txt",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "中/test.txt",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example.com/%E4%B8%AD/test.txt",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/%E4%B8%AD/test.txt",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://www.example2.com",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example2.com/",
+ "origin": "http://www.example2.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example2.com",
+ "hostname": "www.example2.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "//www.example2.com",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example2.com/",
+ "origin": "http://www.example2.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example2.com",
+ "hostname": "www.example2.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:...",
+ "base": "http://www.example.com/test",
+ "href": "file:///...",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/...",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:..",
+ "base": "http://www.example.com/test",
+ "href": "file:///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:a",
+ "base": "http://www.example.com/test",
+ "href": "file:///a",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/a",
+ "search": "",
+ "hash": ""
+ },
+ "# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/host.html",
+ "Basic canonicalization, uppercase should be converted to lowercase",
+ {
+ "input": "http://ExAmPlE.CoM",
+ "base": "http://other.com/",
+ "href": "http://example.com/",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example example.com",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://Goo%20 goo%7C|.com",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://[]",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://[:]",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ "U+3000 is mapped to U+0020 (space) which is disallowed",
+ {
+ "input": "http://GOO\u00a0\u3000goo.com",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ "Other types of space (no-break, zero-width, zero-width-no-break) are name-prepped away to nothing. U+200B, U+2060, and U+FEFF, are ignored",
+ {
+ "input": "http://GOO\u200b\u2060\ufeffgoo.com",
+ "base": "http://other.com/",
+ "href": "http://googoo.com/",
+ "origin": "http://googoo.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "googoo.com",
+ "hostname": "googoo.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "Leading and trailing C0 control or space",
+ {
+ "input": "\u0000\u001b\u0004\u0012 http://example.com/\u001f \u000d ",
+ "base": "about:blank",
+ "href": "http://example.com/",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "Ideographic full stop (full-width period for Chinese, etc.) should be treated as a dot. U+3002 is mapped to U+002E (dot)",
+ {
+ "input": "http://www.foo。bar.com",
+ "base": "http://other.com/",
+ "href": "http://www.foo.bar.com/",
+ "origin": "http://www.foo.bar.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.foo.bar.com",
+ "hostname": "www.foo.bar.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "Invalid unicode characters should fail... U+FDD0 is disallowed; %ef%b7%90 is U+FDD0",
+ {
+ "input": "http://\ufdd0zyx.com",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ "This is the same as previous but escaped",
+ {
+ "input": "http://%ef%b7%90zyx.com",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ "U+FFFD",
+ {
+ "input": "https://\ufffd",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "https://%EF%BF%BD",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "https://x/\ufffd?\ufffd#\ufffd",
+ "base": "about:blank",
+ "href": "https://x/%EF%BF%BD?%EF%BF%BD#%EF%BF%BD",
+ "origin": "https://x",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "x",
+ "hostname": "x",
+ "port": "",
+ "pathname": "/%EF%BF%BD",
+ "search": "?%EF%BF%BD",
+ "hash": "#%EF%BF%BD"
+ },
+ "Test name prepping, fullwidth input should be converted to ASCII and NOT IDN-ized. This is 'Go' in fullwidth UTF-8/UTF-16.",
+ {
+ "input": "http://Go.com",
+ "base": "http://other.com/",
+ "href": "http://go.com/",
+ "origin": "http://go.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "go.com",
+ "hostname": "go.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "URL spec forbids the following. https://www.w3.org/Bugs/Public/show_bug.cgi?id=24257",
+ {
+ "input": "http://%41.com",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://%ef%bc%85%ef%bc%94%ef%bc%91.com",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ "...%00 in fullwidth should fail (also as escaped UTF-8 input)",
+ {
+ "input": "http://%00.com",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://%ef%bc%85%ef%bc%90%ef%bc%90.com",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ "Basic IDN support, UTF-8 and UTF-16 input should be converted to IDN",
+ {
+ "input": "http://你好你好",
+ "base": "http://other.com/",
+ "href": "http://xn--6qqa088eba/",
+ "origin": "http://xn--6qqa088eba",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "xn--6qqa088eba",
+ "hostname": "xn--6qqa088eba",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "https://faß.ExAmPlE/",
+ "base": "about:blank",
+ "href": "https://xn--fa-hia.example/",
+ "origin": "https://xn--fa-hia.example",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "xn--fa-hia.example",
+ "hostname": "xn--fa-hia.example",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "sc://faß.ExAmPlE/",
+ "base": "about:blank",
+ "href": "sc://fa%C3%9F.ExAmPlE/",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "fa%C3%9F.ExAmPlE",
+ "hostname": "fa%C3%9F.ExAmPlE",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "Invalid escaped characters should fail and the percents should be escaped. https://www.w3.org/Bugs/Public/show_bug.cgi?id=24191",
+ {
+ "input": "http://%zz%66%a.com",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ "If we get an invalid character that has been escaped.",
+ {
+ "input": "http://%25",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://hello%00",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ "Escaped numbers should be treated like IP addresses if they are.",
+ {
+ "input": "http://%30%78%63%30%2e%30%32%35%30.01",
+ "base": "http://other.com/",
+ "href": "http://192.168.0.1/",
+ "origin": "http://192.168.0.1",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "192.168.0.1",
+ "hostname": "192.168.0.1",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://%30%78%63%30%2e%30%32%35%30.01%2e",
+ "base": "http://other.com/",
+ "href": "http://192.168.0.1/",
+ "origin": "http://192.168.0.1",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "192.168.0.1",
+ "hostname": "192.168.0.1",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://192.168.0.257",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ "Invalid escaping in hosts causes failure",
+ {
+ "input": "http://%3g%78%63%30%2e%30%32%35%30%2E.01",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ "A space in a host causes failure",
+ {
+ "input": "http://192.168.0.1 hello",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "https://x x:12",
+ "base": "about:blank",
+ "failure": true
+ },
+ "Fullwidth and escaped UTF-8 fullwidth should still be treated as IP",
+ {
+ "input": "http://0Xc0.0250.01",
+ "base": "http://other.com/",
+ "href": "http://192.168.0.1/",
+ "origin": "http://192.168.0.1",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "192.168.0.1",
+ "hostname": "192.168.0.1",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "Domains with empty labels",
+ {
+ "input": "http://./",
+ "base": "about:blank",
+ "href": "http://./",
+ "origin": "http://.",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": ".",
+ "hostname": ".",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://../",
+ "base": "about:blank",
+ "href": "http://../",
+ "origin": "http://..",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "..",
+ "hostname": "..",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://0..0x300/",
+ "base": "about:blank",
+ "href": "http://0..0x300/",
+ "origin": "http://0..0x300",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "0..0x300",
+ "hostname": "0..0x300",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "Broken IPv6",
+ {
+ "input": "http://[www.google.com]/",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "http://[google.com]",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://[::1.2.3.4x]",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://[::1.2.3.]",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://[::1.2.]",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://[::1.]",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ "Misc Unicode",
+ {
+ "input": "http://foo:💩@example.com/bar",
+ "base": "http://other.com/",
+ "href": "http://foo:%F0%9F%92%A9@example.com/bar",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "foo",
+ "password": "%F0%9F%92%A9",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/bar",
+ "search": "",
+ "hash": ""
+ },
+ "# resolving a fragment against any scheme succeeds",
+ {
+ "input": "#",
+ "base": "test:test",
+ "href": "test:test#",
+ "origin": "null",
+ "protocol": "test:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "test",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "#x",
+ "base": "mailto:x@x.com",
+ "href": "mailto:x@x.com#x",
+ "origin": "null",
+ "protocol": "mailto:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "x@x.com",
+ "search": "",
+ "hash": "#x"
+ },
+ {
+ "input": "#x",
+ "base": "data:,",
+ "href": "data:,#x",
+ "origin": "null",
+ "protocol": "data:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": ",",
+ "search": "",
+ "hash": "#x"
+ },
+ {
+ "input": "#x",
+ "base": "about:blank",
+ "href": "about:blank#x",
+ "origin": "null",
+ "protocol": "about:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "blank",
+ "search": "",
+ "hash": "#x"
+ },
+ {
+ "input": "#",
+ "base": "test:test?test",
+ "href": "test:test?test#",
+ "origin": "null",
+ "protocol": "test:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "test",
+ "search": "?test",
+ "hash": ""
+ },
+ "# multiple @ in authority state",
+ {
+ "input": "https://@test@test@example:800/",
+ "base": "http://doesnotmatter/",
+ "href": "https://%40test%40test@example:800/",
+ "origin": "https://example:800",
+ "protocol": "https:",
+ "username": "%40test%40test",
+ "password": "",
+ "host": "example:800",
+ "hostname": "example",
+ "port": "800",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "https://@@@example",
+ "base": "http://doesnotmatter/",
+ "href": "https://%40%40@example/",
+ "origin": "https://example",
+ "protocol": "https:",
+ "username": "%40%40",
+ "password": "",
+ "host": "example",
+ "hostname": "example",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "non-az-09 characters",
+ {
+ "input": "http://`{}:`{}@h/`{}?`{}",
+ "base": "http://doesnotmatter/",
+ "href": "http://%60%7B%7D:%60%7B%7D@h/%60%7B%7D?`{}",
+ "origin": "http://h",
+ "protocol": "http:",
+ "username": "%60%7B%7D",
+ "password": "%60%7B%7D",
+ "host": "h",
+ "hostname": "h",
+ "port": "",
+ "pathname": "/%60%7B%7D",
+ "search": "?`{}",
+ "hash": ""
+ },
+ "# Credentials in base",
+ {
+ "input": "/some/path",
+ "base": "http://user@example.org/smth",
+ "href": "http://user@example.org/some/path",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "user",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/some/path",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "",
+ "base": "http://user:pass@example.org:21/smth",
+ "href": "http://user:pass@example.org:21/smth",
+ "origin": "http://example.org:21",
+ "protocol": "http:",
+ "username": "user",
+ "password": "pass",
+ "host": "example.org:21",
+ "hostname": "example.org",
+ "port": "21",
+ "pathname": "/smth",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/some/path",
+ "base": "http://user:pass@example.org:21/smth",
+ "href": "http://user:pass@example.org:21/some/path",
+ "origin": "http://example.org:21",
+ "protocol": "http:",
+ "username": "user",
+ "password": "pass",
+ "host": "example.org:21",
+ "hostname": "example.org",
+ "port": "21",
+ "pathname": "/some/path",
+ "search": "",
+ "hash": ""
+ },
+ "# a set of tests designed by zcorpan for relative URLs with unknown schemes",
+ {
+ "input": "i",
+ "base": "sc:sd",
+ "failure": true
+ },
+ {
+ "input": "i",
+ "base": "sc:sd/sd",
+ "failure": true
+ },
+ {
+ "input": "i",
+ "base": "sc:/pa/pa",
+ "href": "sc:/pa/i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/pa/i",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "i",
+ "base": "sc://ho/pa",
+ "href": "sc://ho/i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "ho",
+ "hostname": "ho",
+ "port": "",
+ "pathname": "/i",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "i",
+ "base": "sc:///pa/pa",
+ "href": "sc:///pa/i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/pa/i",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "../i",
+ "base": "sc:sd",
+ "failure": true
+ },
+ {
+ "input": "../i",
+ "base": "sc:sd/sd",
+ "failure": true
+ },
+ {
+ "input": "../i",
+ "base": "sc:/pa/pa",
+ "href": "sc:/i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/i",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "../i",
+ "base": "sc://ho/pa",
+ "href": "sc://ho/i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "ho",
+ "hostname": "ho",
+ "port": "",
+ "pathname": "/i",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "../i",
+ "base": "sc:///pa/pa",
+ "href": "sc:///i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/i",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/i",
+ "base": "sc:sd",
+ "failure": true
+ },
+ {
+ "input": "/i",
+ "base": "sc:sd/sd",
+ "failure": true
+ },
+ {
+ "input": "/i",
+ "base": "sc:/pa/pa",
+ "href": "sc:/i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/i",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/i",
+ "base": "sc://ho/pa",
+ "href": "sc://ho/i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "ho",
+ "hostname": "ho",
+ "port": "",
+ "pathname": "/i",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/i",
+ "base": "sc:///pa/pa",
+ "href": "sc:///i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/i",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "?i",
+ "base": "sc:sd",
+ "failure": true
+ },
+ {
+ "input": "?i",
+ "base": "sc:sd/sd",
+ "failure": true
+ },
+ {
+ "input": "?i",
+ "base": "sc:/pa/pa",
+ "href": "sc:/pa/pa?i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/pa/pa",
+ "search": "?i",
+ "hash": ""
+ },
+ {
+ "input": "?i",
+ "base": "sc://ho/pa",
+ "href": "sc://ho/pa?i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "ho",
+ "hostname": "ho",
+ "port": "",
+ "pathname": "/pa",
+ "search": "?i",
+ "hash": ""
+ },
+ {
+ "input": "?i",
+ "base": "sc:///pa/pa",
+ "href": "sc:///pa/pa?i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/pa/pa",
+ "search": "?i",
+ "hash": ""
+ },
+ {
+ "input": "#i",
+ "base": "sc:sd",
+ "href": "sc:sd#i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "sd",
+ "search": "",
+ "hash": "#i"
+ },
+ {
+ "input": "#i",
+ "base": "sc:sd/sd",
+ "href": "sc:sd/sd#i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "sd/sd",
+ "search": "",
+ "hash": "#i"
+ },
+ {
+ "input": "#i",
+ "base": "sc:/pa/pa",
+ "href": "sc:/pa/pa#i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/pa/pa",
+ "search": "",
+ "hash": "#i"
+ },
+ {
+ "input": "#i",
+ "base": "sc://ho/pa",
+ "href": "sc://ho/pa#i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "ho",
+ "hostname": "ho",
+ "port": "",
+ "pathname": "/pa",
+ "search": "",
+ "hash": "#i"
+ },
+ {
+ "input": "#i",
+ "base": "sc:///pa/pa",
+ "href": "sc:///pa/pa#i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/pa/pa",
+ "search": "",
+ "hash": "#i"
+ },
+ "# make sure that relative URL logic works on known typically non-relative schemes too",
+ {
+ "input": "about:/../",
+ "base": "about:blank",
+ "href": "about:/",
+ "origin": "null",
+ "protocol": "about:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "data:/../",
+ "base": "about:blank",
+ "href": "data:/",
+ "origin": "null",
+ "protocol": "data:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "javascript:/../",
+ "base": "about:blank",
+ "href": "javascript:/",
+ "origin": "null",
+ "protocol": "javascript:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "mailto:/../",
+ "base": "about:blank",
+ "href": "mailto:/",
+ "origin": "null",
+ "protocol": "mailto:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "# unknown schemes and their hosts",
+ {
+ "input": "sc://ñ.test/",
+ "base": "about:blank",
+ "href": "sc://%C3%B1.test/",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "%C3%B1.test",
+ "hostname": "%C3%B1.test",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "sc://\u001F!\"$&'()*+,-.;<=>^_`{|}~/",
+ "base": "about:blank",
+ "href": "sc://%1F!\"$&'()*+,-.;<=>^_`{|}~/",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "%1F!\"$&'()*+,-.;<=>^_`{|}~",
+ "hostname": "%1F!\"$&'()*+,-.;<=>^_`{|}~",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "sc://\u0000/",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "sc:// /",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "sc://%/",
+ "base": "about:blank",
+ "href": "sc://%/",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "%",
+ "hostname": "%",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "sc://[/",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "sc://\\/",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "sc://]/",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "x",
+ "base": "sc://ñ",
+ "href": "sc://%C3%B1/x",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "%C3%B1",
+ "hostname": "%C3%B1",
+ "port": "",
+ "pathname": "/x",
+ "search": "",
+ "hash": ""
+ },
+ "# unknown schemes and backslashes",
+ {
+ "input": "sc:\\../",
+ "base": "about:blank",
+ "href": "sc:\\../",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "\\../",
+ "search": "",
+ "hash": ""
+ },
+ "# unknown scheme with path looking like a password",
+ {
+ "input": "sc::a@example.net",
+ "base": "about:blank",
+ "href": "sc::a@example.net",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": ":a@example.net",
+ "search": "",
+ "hash": ""
+ },
+ "# unknown scheme with bogus percent-encoding",
+ {
+ "input": "wow:%NBD",
+ "base": "about:blank",
+ "href": "wow:%NBD",
+ "origin": "null",
+ "protocol": "wow:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "%NBD",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "wow:%1G",
+ "base": "about:blank",
+ "href": "wow:%1G",
+ "origin": "null",
+ "protocol": "wow:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "%1G",
+ "search": "",
+ "hash": ""
+ },
+ "# Hosts and percent-encoding",
+ {
+ "input": "ftp://example.com%80/",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "ftp://example.com%A0/",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "https://example.com%80/",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "https://example.com%A0/",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "ftp://%e2%98%83",
+ "base": "about:blank",
+ "href": "ftp://xn--n3h/",
+ "origin": "ftp://xn--n3h",
+ "protocol": "ftp:",
+ "username": "",
+ "password": "",
+ "host": "xn--n3h",
+ "hostname": "xn--n3h",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "https://%e2%98%83",
+ "base": "about:blank",
+ "href": "https://xn--n3h/",
+ "origin": "https://xn--n3h",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "xn--n3h",
+ "hostname": "xn--n3h",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "# tests from jsdom/whatwg-url designed for code coverage",
+ {
+ "input": "http://127.0.0.1:10100/relative_import.html",
+ "base": "about:blank",
+ "href": "http://127.0.0.1:10100/relative_import.html",
+ "origin": "http://127.0.0.1:10100",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "127.0.0.1:10100",
+ "hostname": "127.0.0.1",
+ "port": "10100",
+ "pathname": "/relative_import.html",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://facebook.com/?foo=%7B%22abc%22",
+ "base": "about:blank",
+ "href": "http://facebook.com/?foo=%7B%22abc%22",
+ "origin": "http://facebook.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "facebook.com",
+ "hostname": "facebook.com",
+ "port": "",
+ "pathname": "/",
+ "search": "?foo=%7B%22abc%22",
+ "hash": ""
+ },
+ {
+ "input": "https://localhost:3000/jqueryui@1.2.3",
+ "base": "about:blank",
+ "href": "https://localhost:3000/jqueryui@1.2.3",
+ "origin": "https://localhost:3000",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "localhost:3000",
+ "hostname": "localhost",
+ "port": "3000",
+ "pathname": "/jqueryui@1.2.3",
+ "search": "",
+ "hash": ""
+ },
+ "# tab/LF/CR",
+ {
+ "input": "h\tt\nt\rp://h\to\ns\rt:9\t0\n0\r0/p\ta\nt\rh?q\tu\ne\rry#f\tr\na\rg",
+ "base": "about:blank",
+ "href": "http://host:9000/path?query#frag",
+ "origin": "http://host:9000",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "host:9000",
+ "hostname": "host",
+ "port": "9000",
+ "pathname": "/path",
+ "search": "?query",
+ "hash": "#frag"
+ },
+ "# Stringification of URL.searchParams",
+ {
+ "input": "?a=b&c=d",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/bar?a=b&c=d",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "?a=b&c=d",
+ "searchParams": "a=b&c=d",
+ "hash": ""
+ },
+ {
+ "input": "??a=b&c=d",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/bar??a=b&c=d",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "??a=b&c=d",
+ "searchParams": "%3Fa=b&c=d",
+ "hash": ""
+ },
+ "# Scheme only",
+ {
+ "input": "http:",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/bar",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "",
+ "searchParams": "",
+ "hash": ""
+ },
+ {
+ "input": "http:",
+ "base": "https://example.org/foo/bar",
+ "failure": true
+ },
+ {
+ "input": "sc:",
+ "base": "https://example.org/foo/bar",
+ "href": "sc:",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "",
+ "search": "",
+ "searchParams": "",
+ "hash": ""
+ },
+ "# Percent encoding of fragments",
+ {
+ "input": "http://foo.bar/baz?qux#foo\bbar",
+ "base": "about:blank",
+ "href": "http://foo.bar/baz?qux#foo%08bar",
+ "origin": "http://foo.bar",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "foo.bar",
+ "hostname": "foo.bar",
+ "port": "",
+ "pathname": "/baz",
+ "search": "?qux",
+ "searchParams": "qux=",
+ "hash": "#foo%08bar"
+ },
+ "# IPv4 parsing (via https://github.com/nodejs/node/pull/10317)",
+ {
+ "input": "http://192.168.257",
+ "base": "http://other.com/",
+ "href": "http://192.168.1.1/",
+ "origin": "http://192.168.1.1",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "192.168.1.1",
+ "hostname": "192.168.1.1",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://192.168.257.com",
+ "base": "http://other.com/",
+ "href": "http://192.168.257.com/",
+ "origin": "http://192.168.257.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "192.168.257.com",
+ "hostname": "192.168.257.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://256",
+ "base": "http://other.com/",
+ "href": "http://0.0.1.0/",
+ "origin": "http://0.0.1.0",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "0.0.1.0",
+ "hostname": "0.0.1.0",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://256.com",
+ "base": "http://other.com/",
+ "href": "http://256.com/",
+ "origin": "http://256.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "256.com",
+ "hostname": "256.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://999999999",
+ "base": "http://other.com/",
+ "href": "http://59.154.201.255/",
+ "origin": "http://59.154.201.255",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "59.154.201.255",
+ "hostname": "59.154.201.255",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://999999999.com",
+ "base": "http://other.com/",
+ "href": "http://999999999.com/",
+ "origin": "http://999999999.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "999999999.com",
+ "hostname": "999999999.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://10000000000",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://10000000000.com",
+ "base": "http://other.com/",
+ "href": "http://10000000000.com/",
+ "origin": "http://10000000000.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "10000000000.com",
+ "hostname": "10000000000.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://4294967295",
+ "base": "http://other.com/",
+ "href": "http://255.255.255.255/",
+ "origin": "http://255.255.255.255",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "255.255.255.255",
+ "hostname": "255.255.255.255",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://4294967296",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://0xffffffff",
+ "base": "http://other.com/",
+ "href": "http://255.255.255.255/",
+ "origin": "http://255.255.255.255",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "255.255.255.255",
+ "hostname": "255.255.255.255",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://0xffffffff1",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://256.256.256.256",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://256.256.256.256.256",
+ "base": "http://other.com/",
+ "href": "http://256.256.256.256.256/",
+ "origin": "http://256.256.256.256.256",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "256.256.256.256.256",
+ "hostname": "256.256.256.256.256",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "https://0x.0x.0",
+ "base": "about:blank",
+ "href": "https://0.0.0.0/",
+ "origin": "https://0.0.0.0",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "0.0.0.0",
+ "hostname": "0.0.0.0",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "More IPv4 parsing (via https://github.com/jsdom/whatwg-url/issues/92)",
+ {
+ "input": "https://256.0.0.1/test",
+ "base": "about:blank",
+ "failure": true
+ },
+ "# file URLs containing percent-encoded Windows drive letters (shouldn't work)",
+ {
+ "input": "file:///C%3A/",
+ "base": "about:blank",
+ "href": "file:///C%3A/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C%3A/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:///C%7C/",
+ "base": "about:blank",
+ "href": "file:///C%7C/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C%7C/",
+ "search": "",
+ "hash": ""
+ },
+ "# file URLs relative to other file URLs (via https://github.com/jsdom/whatwg-url/pull/60)",
+ {
+ "input": "pix/submit.gif",
+ "base": "file:///C:/Users/Domenic/Dropbox/GitHub/tmpvar/jsdom/test/level2/html/files/anchor.html",
+ "href": "file:///C:/Users/Domenic/Dropbox/GitHub/tmpvar/jsdom/test/level2/html/files/pix/submit.gif",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/Users/Domenic/Dropbox/GitHub/tmpvar/jsdom/test/level2/html/files/pix/submit.gif",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "..",
+ "base": "file:///C:/",
+ "href": "file:///C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "..",
+ "base": "file:///",
+ "href": "file:///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "# More file URL tests by zcorpan and annevk",
+ {
+ "input": "/",
+ "base": "file:///C:/a/b",
+ "href": "file:///C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "//d:",
+ "base": "file:///C:/a/b",
+ "href": "file:///d:",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/d:",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "//d:/..",
+ "base": "file:///C:/a/b",
+ "href": "file:///d:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/d:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "..",
+ "base": "file:///ab:/",
+ "href": "file:///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "..",
+ "base": "file:///1:/",
+ "href": "file:///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "",
+ "base": "file:///test?test#test",
+ "href": "file:///test?test",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "?test",
+ "hash": ""
+ },
+ {
+ "input": "file:",
+ "base": "file:///test?test#test",
+ "href": "file:///test?test",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "?test",
+ "hash": ""
+ },
+ {
+ "input": "?x",
+ "base": "file:///test?test#test",
+ "href": "file:///test?x",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "?x",
+ "hash": ""
+ },
+ {
+ "input": "file:?x",
+ "base": "file:///test?test#test",
+ "href": "file:///test?x",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "?x",
+ "hash": ""
+ },
+ {
+ "input": "#x",
+ "base": "file:///test?test#test",
+ "href": "file:///test?test#x",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "?test",
+ "hash": "#x"
+ },
+ {
+ "input": "file:#x",
+ "base": "file:///test?test#test",
+ "href": "file:///test?test#x",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "?test",
+ "hash": "#x"
+ },
+ "# File URLs and many (back)slashes",
+ {
+ "input": "file:///localhost//cat",
+ "base": "about:blank",
+ "href": "file:///localhost//cat",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/localhost//cat",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "\\//pig",
+ "base": "file://lion/",
+ "href": "file:///pig",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/pig",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://",
+ "base": "file://ape/",
+ "href": "file:///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "# Windows drive letter handling with the 'file:' base URL",
+ {
+ "input": "C|#",
+ "base": "file://host/dir/file",
+ "href": "file:///C:#",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "C|?",
+ "base": "file://host/dir/file",
+ "href": "file:///C:?",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "C|/",
+ "base": "file://host/dir/file",
+ "href": "file:///C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "C|\n/",
+ "base": "file://host/dir/file",
+ "href": "file:///C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "C|\\",
+ "base": "file://host/dir/file",
+ "href": "file:///C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "C",
+ "base": "file://host/dir/file",
+ "href": "file://host/dir/C",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "host",
+ "hostname": "host",
+ "port": "",
+ "pathname": "/dir/C",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "C|a",
+ "base": "file://host/dir/file",
+ "href": "file://host/dir/C|a",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "host",
+ "hostname": "host",
+ "port": "",
+ "pathname": "/dir/C|a",
+ "search": "",
+ "hash": ""
+ },
+ "# Windows drive letter quirk in the file slash state",
+ {
+ "input": "/c:/foo/bar",
+ "base": "file://host/path",
+ "href": "file:///c:/foo/bar",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/c:/foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ "# Windows drive letter quirk (no host)",
+ {
+ "input": "file:/C|/",
+ "base": "about:blank",
+ "href": "file:///C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://C|/",
+ "base": "about:blank",
+ "href": "file:///C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ "# Windows drive letter quirk with not empty host",
+ {
+ "input": "file://example.net/C:/",
+ "base": "about:blank",
+ "href": "file:///C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://1.2.3.4/C:/",
+ "base": "about:blank",
+ "href": "file:///C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://[1::8]/C:/",
+ "base": "about:blank",
+ "href": "file:///C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ "# file URLs without base URL by Rimas Misevičius",
+ {
+ "input": "file:",
+ "base": "about:blank",
+ "href": "file:///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:?q=v",
+ "base": "about:blank",
+ "href": "file:///?q=v",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "?q=v",
+ "hash": ""
+ },
+ {
+ "input": "file:#frag",
+ "base": "about:blank",
+ "href": "file:///#frag",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": "#frag"
+ },
+ "# IPv6 tests",
+ {
+ "input": "http://[1:0::]",
+ "base": "http://example.net/",
+ "href": "http://[1::]/",
+ "origin": "http://[1::]",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "[1::]",
+ "hostname": "[1::]",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://[0:1:2:3:4:5:6:7:8]",
+ "base": "http://example.net/",
+ "failure": true
+ },
+ {
+ "input": "https://[0::0::0]",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "https://[0:.0]",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "https://[0:0:]",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "https://[0:1:2:3:4:5:6:7.0.0.0.1]",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "https://[0:1.00.0.0.0]",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "https://[0:1.290.0.0.0]",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "https://[0:1.23.23]",
+ "base": "about:blank",
+ "failure": true
+ },
+ "# Empty host",
+ {
+ "input": "http://?",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "http://#",
+ "base": "about:blank",
+ "failure": true
+ },
+ "Port overflow (2^32 + 81)",
+ {
+ "input": "http://f:4294967377/c",
+ "base": "http://example.org/",
+ "failure": true
+ },
+ "Port overflow (2^64 + 81)",
+ {
+ "input": "http://f:18446744073709551697/c",
+ "base": "http://example.org/",
+ "failure": true
+ },
+ "Port overflow (2^128 + 81)",
+ {
+ "input": "http://f:340282366920938463463374607431768211537/c",
+ "base": "http://example.org/",
+ "failure": true
+ },
+ "# Non-special-URL path tests",
+ {
+ "input": "///",
+ "base": "sc://x/",
+ "href": "sc:///",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "tftp://foobar.com/someconfig;mode=netascii",
+ "base": "about:blank",
+ "href": "tftp://foobar.com/someconfig;mode=netascii",
+ "origin": "null",
+ "protocol": "tftp:",
+ "username": "",
+ "password": "",
+ "host": "foobar.com",
+ "hostname": "foobar.com",
+ "port": "",
+ "pathname": "/someconfig;mode=netascii",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "telnet://user:pass@foobar.com:23/",
+ "base": "about:blank",
+ "href": "telnet://user:pass@foobar.com:23/",
+ "origin": "null",
+ "protocol": "telnet:",
+ "username": "user",
+ "password": "pass",
+ "host": "foobar.com:23",
+ "hostname": "foobar.com",
+ "port": "23",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ut2004://10.10.10.10:7777/Index.ut2",
+ "base": "about:blank",
+ "href": "ut2004://10.10.10.10:7777/Index.ut2",
+ "origin": "null",
+ "protocol": "ut2004:",
+ "username": "",
+ "password": "",
+ "host": "10.10.10.10:7777",
+ "hostname": "10.10.10.10",
+ "port": "7777",
+ "pathname": "/Index.ut2",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "redis://foo:bar@somehost:6379/0?baz=bam&qux=baz",
+ "base": "about:blank",
+ "href": "redis://foo:bar@somehost:6379/0?baz=bam&qux=baz",
+ "origin": "null",
+ "protocol": "redis:",
+ "username": "foo",
+ "password": "bar",
+ "host": "somehost:6379",
+ "hostname": "somehost",
+ "port": "6379",
+ "pathname": "/0",
+ "search": "?baz=bam&qux=baz",
+ "hash": ""
+ },
+ {
+ "input": "rsync://foo@host:911/sup",
+ "base": "about:blank",
+ "href": "rsync://foo@host:911/sup",
+ "origin": "null",
+ "protocol": "rsync:",
+ "username": "foo",
+ "password": "",
+ "host": "host:911",
+ "hostname": "host",
+ "port": "911",
+ "pathname": "/sup",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "git://github.com/foo/bar.git",
+ "base": "about:blank",
+ "href": "git://github.com/foo/bar.git",
+ "origin": "null",
+ "protocol": "git:",
+ "username": "",
+ "password": "",
+ "host": "github.com",
+ "hostname": "github.com",
+ "port": "",
+ "pathname": "/foo/bar.git",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "irc://myserver.com:6999/channel?passwd",
+ "base": "about:blank",
+ "href": "irc://myserver.com:6999/channel?passwd",
+ "origin": "null",
+ "protocol": "irc:",
+ "username": "",
+ "password": "",
+ "host": "myserver.com:6999",
+ "hostname": "myserver.com",
+ "port": "6999",
+ "pathname": "/channel",
+ "search": "?passwd",
+ "hash": ""
+ },
+ {
+ "input": "dns://fw.example.org:9999/foo.bar.org?type=TXT",
+ "base": "about:blank",
+ "href": "dns://fw.example.org:9999/foo.bar.org?type=TXT",
+ "origin": "null",
+ "protocol": "dns:",
+ "username": "",
+ "password": "",
+ "host": "fw.example.org:9999",
+ "hostname": "fw.example.org",
+ "port": "9999",
+ "pathname": "/foo.bar.org",
+ "search": "?type=TXT",
+ "hash": ""
+ },
+ {
+ "input": "ldap://localhost:389/ou=People,o=JNDITutorial",
+ "base": "about:blank",
+ "href": "ldap://localhost:389/ou=People,o=JNDITutorial",
+ "origin": "null",
+ "protocol": "ldap:",
+ "username": "",
+ "password": "",
+ "host": "localhost:389",
+ "hostname": "localhost",
+ "port": "389",
+ "pathname": "/ou=People,o=JNDITutorial",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "git+https://github.com/foo/bar",
+ "base": "about:blank",
+ "href": "git+https://github.com/foo/bar",
+ "origin": "null",
+ "protocol": "git+https:",
+ "username": "",
+ "password": "",
+ "host": "github.com",
+ "hostname": "github.com",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "urn:ietf:rfc:2648",
+ "base": "about:blank",
+ "href": "urn:ietf:rfc:2648",
+ "origin": "null",
+ "protocol": "urn:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "ietf:rfc:2648",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "tag:joe@example.org,2001:foo/bar",
+ "base": "about:blank",
+ "href": "tag:joe@example.org,2001:foo/bar",
+ "origin": "null",
+ "protocol": "tag:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "joe@example.org,2001:foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ "# percent encoded hosts in non-special-URLs",
+ {
+ "input": "non-special://%E2%80%A0/",
+ "base": "about:blank",
+ "href": "non-special://%E2%80%A0/",
+ "protocol": "non-special:",
+ "username": "",
+ "password": "",
+ "host": "%E2%80%A0",
+ "hostname": "%E2%80%A0",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "non-special://H%4fSt/path",
+ "base": "about:blank",
+ "href": "non-special://H%4fSt/path",
+ "protocol": "non-special:",
+ "username": "",
+ "password": "",
+ "host": "H%4fSt",
+ "hostname": "H%4fSt",
+ "port": "",
+ "pathname": "/path",
+ "search": "",
+ "hash": ""
+ },
+ "# IPv6 in non-special-URLs",
+ {
+ "input": "non-special://[1:2:0:0:5:0:0:0]/",
+ "base": "about:blank",
+ "href": "non-special://[1:2:0:0:5::]/",
+ "protocol": "non-special:",
+ "username": "",
+ "password": "",
+ "host": "[1:2:0:0:5::]",
+ "hostname": "[1:2:0:0:5::]",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "non-special://[1:2:0:0:0:0:0:3]/",
+ "base": "about:blank",
+ "href": "non-special://[1:2::3]/",
+ "protocol": "non-special:",
+ "username": "",
+ "password": "",
+ "host": "[1:2::3]",
+ "hostname": "[1:2::3]",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "non-special://[1:2::3]:80/",
+ "base": "about:blank",
+ "href": "non-special://[1:2::3]:80/",
+ "protocol": "non-special:",
+ "username": "",
+ "password": "",
+ "host": "[1:2::3]:80",
+ "hostname": "[1:2::3]",
+ "port": "80",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "non-special://[:80/",
+ "base": "about:blank",
+ "failure": true
+ },
+ {
+ "input": "blob:https://example.com:443/",
+ "base": "about:blank",
+ "href": "blob:https://example.com:443/",
+ "protocol": "blob:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "https://example.com:443/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "blob:d3958f5c-0777-0845-9dcf-2cb28783acaf",
+ "base": "about:blank",
+ "href": "blob:d3958f5c-0777-0845-9dcf-2cb28783acaf",
+ "protocol": "blob:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "d3958f5c-0777-0845-9dcf-2cb28783acaf",
+ "search": "",
+ "hash": ""
+ },
+ "Invalid IPv4 radix digits",
+ {
+ "input": "http://0177.0.0.0189",
+ "base": "about:blank",
+ "href": "http://0177.0.0.0189/",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "0177.0.0.0189",
+ "hostname": "0177.0.0.0189",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://0x7f.0.0.0x7g",
+ "base": "about:blank",
+ "href": "http://0x7f.0.0.0x7g/",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "0x7f.0.0.0x7g",
+ "hostname": "0x7f.0.0.0x7g",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://0X7F.0.0.0X7G",
+ "base": "about:blank",
+ "href": "http://0x7f.0.0.0x7g/",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "0x7f.0.0.0x7g",
+ "hostname": "0x7f.0.0.0x7g",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "Invalid IPv4 portion of IPv6 address",
+ {
+ "input": "http://[::127.0.0.0.1]",
+ "base": "about:blank",
+ "failure": true
+ },
+ "Uncompressed IPv6 addresses with 0",
+ {
+ "input": "http://[0:1:0:1:0:1:0:1]",
+ "base": "about:blank",
+ "href": "http://[0:1:0:1:0:1:0:1]/",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "[0:1:0:1:0:1:0:1]",
+ "hostname": "[0:1:0:1:0:1:0:1]",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://[1:0:1:0:1:0:1:0]",
+ "base": "about:blank",
+ "href": "http://[1:0:1:0:1:0:1:0]/",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "[1:0:1:0:1:0:1:0]",
+ "hostname": "[1:0:1:0:1:0:1:0]",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "Percent-encoded query and fragment",
+ {
+ "input": "http://example.org/test?\u0022",
+ "base": "about:blank",
+ "href": "http://example.org/test?%22",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/test",
+ "search": "?%22",
+ "hash": ""
+ },
+ {
+ "input": "http://example.org/test?\u0023",
+ "base": "about:blank",
+ "href": "http://example.org/test?#",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/test",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.org/test?\u003C",
+ "base": "about:blank",
+ "href": "http://example.org/test?%3C",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/test",
+ "search": "?%3C",
+ "hash": ""
+ },
+ {
+ "input": "http://example.org/test?\u003E",
+ "base": "about:blank",
+ "href": "http://example.org/test?%3E",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/test",
+ "search": "?%3E",
+ "hash": ""
+ },
+ {
+ "input": "http://example.org/test?\u2323",
+ "base": "about:blank",
+ "href": "http://example.org/test?%E2%8C%A3",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/test",
+ "search": "?%E2%8C%A3",
+ "hash": ""
+ },
+ {
+ "input": "http://example.org/test?%23%23",
+ "base": "about:blank",
+ "href": "http://example.org/test?%23%23",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/test",
+ "search": "?%23%23",
+ "hash": ""
+ },
+ {
+ "input": "http://example.org/test?%GH",
+ "base": "about:blank",
+ "href": "http://example.org/test?%GH",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/test",
+ "search": "?%GH",
+ "hash": ""
+ },
+ {
+ "input": "http://example.org/test?a#%EF",
+ "base": "about:blank",
+ "href": "http://example.org/test?a#%EF",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/test",
+ "search": "?a",
+ "hash": "#%EF"
+ },
+ {
+ "input": "http://example.org/test?a#%GH",
+ "base": "about:blank",
+ "href": "http://example.org/test?a#%GH",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/test",
+ "search": "?a",
+ "hash": "#%GH"
+ },
+ "Bad bases",
+ {
+ "input": "test-a.html",
+ "base": "a",
+ "failure": true
+ },
+ {
+ "input": "test-a-slash.html",
+ "base": "a/",
+ "failure": true
+ },
+ {
+ "input": "test-a-slash-slash.html",
+ "base": "a//",
+ "failure": true
+ },
+ {
+ "input": "test-a-colon.html",
+ "base": "a:",
+ "failure": true
+ },
+ {
+ "input": "test-a-colon-slash.html",
+ "base": "a:/",
+ "href": "a:/test-a-colon-slash.html",
+ "protocol": "a:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test-a-colon-slash.html",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "test-a-colon-slash-slash.html",
+ "base": "a://",
+ "href": "a:///test-a-colon-slash-slash.html",
+ "protocol": "a:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test-a-colon-slash-slash.html",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "test-a-colon-b.html",
+ "base": "a:b",
+ "failure": true
+ },
+ {
+ "input": "test-a-colon-slash-b.html",
+ "base": "a:/b",
+ "href": "a:/test-a-colon-slash-b.html",
+ "protocol": "a:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test-a-colon-slash-b.html",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "test-a-colon-slash-slash-b.html",
+ "base": "a://b",
+ "href": "a://b/test-a-colon-slash-slash-b.html",
+ "protocol": "a:",
+ "username": "",
+ "password": "",
+ "host": "b",
+ "hostname": "b",
+ "port": "",
+ "pathname": "/test-a-colon-slash-slash-b.html",
+ "search": "",
+ "hash": ""
+ },
+ "Null code point in fragment",
+ {
+ "input": "http://example.org/test?a#b\u0000c",
+ "base": "about:blank",
+ "href": "http://example.org/test?a#bc",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/test",
+ "search": "?a",
+ "hash": "#bc"
+ }
+]
diff --git a/netwerk/test/gtest/urltestdata.json b/netwerk/test/gtest/urltestdata.json
new file mode 100644
index 0000000000..41cf6725b3
--- /dev/null
+++ b/netwerk/test/gtest/urltestdata.json
@@ -0,0 +1,9050 @@
+[
+ "This file is based on testing/web-platform/tests/url/resources/urltestdata.json (with failing tests removed)",
+ {
+ "input": "http://example\t.\norg",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://user:pass@foo:21/bar;par?b#c",
+ "base": "http://example.org/foo/bar",
+ "href": "http://user:pass@foo:21/bar;par?b#c",
+ "origin": "http://foo:21",
+ "protocol": "http:",
+ "username": "user",
+ "password": "pass",
+ "host": "foo:21",
+ "hostname": "foo",
+ "port": "21",
+ "pathname": "/bar;par",
+ "search": "?b",
+ "hash": "#c"
+ },
+ {
+ "input": "https://test:@test",
+ "base": null,
+ "href": "https://test@test/",
+ "origin": "https://test",
+ "protocol": "https:",
+ "username": "test",
+ "password": "",
+ "host": "test",
+ "hostname": "test",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "https://:@test",
+ "base": null,
+ "href": "https://test/",
+ "origin": "https://test",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "test",
+ "hostname": "test",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "non-special://test:@test/x",
+ "base": null,
+ "href": "non-special://test@test/x",
+ "origin": "null",
+ "protocol": "non-special:",
+ "username": "test",
+ "password": "",
+ "host": "test",
+ "hostname": "test",
+ "port": "",
+ "pathname": "/x",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "non-special://:@test/x",
+ "base": null,
+ "href": "non-special://test/x",
+ "origin": "null",
+ "protocol": "non-special:",
+ "username": "",
+ "password": "",
+ "host": "test",
+ "hostname": "test",
+ "port": "",
+ "pathname": "/x",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:foo.com",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/foo.com",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/foo.com",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "\t :foo.com \n",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/:foo.com",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/:foo.com",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": " foo.com ",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/foo.com",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/foo.com",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "a:\t foo.com",
+ "base": "http://example.org/foo/bar",
+ "href": "a: foo.com",
+ "origin": "null",
+ "protocol": "a:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": " foo.com",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://f:21/ b ? d # e ",
+ "base": "http://example.org/foo/bar",
+ "href": "http://f:21/%20b%20?%20d%20#%20e",
+ "origin": "http://f:21",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "f:21",
+ "hostname": "f",
+ "port": "21",
+ "pathname": "/%20b%20",
+ "search": "?%20d%20",
+ "hash": "#%20e"
+ },
+ {
+ "input": "lolscheme:x x#x x",
+ "base": null,
+ "href": "lolscheme:x x#x%20x",
+ "protocol": "lolscheme:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "x x",
+ "search": "",
+ "hash": "#x%20x"
+ },
+ {
+ "input": "http://f:/c",
+ "base": "http://example.org/foo/bar",
+ "href": "http://f/c",
+ "origin": "http://f",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "f",
+ "hostname": "f",
+ "port": "",
+ "pathname": "/c",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://f:0/c",
+ "base": "http://example.org/foo/bar",
+ "href": "http://f:0/c",
+ "origin": "http://f:0",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "f:0",
+ "hostname": "f",
+ "port": "0",
+ "pathname": "/c",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://f:00000000000000/c",
+ "base": "http://example.org/foo/bar",
+ "href": "http://f:0/c",
+ "origin": "http://f:0",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "f:0",
+ "hostname": "f",
+ "port": "0",
+ "pathname": "/c",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://f:00000000000000000000080/c",
+ "base": "http://example.org/foo/bar",
+ "href": "http://f/c",
+ "origin": "http://f",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "f",
+ "hostname": "f",
+ "port": "",
+ "pathname": "/c",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://f:b/c",
+ "base": "http://example.org/foo/bar",
+ "failure": true
+ },
+ {
+ "input": "http://f: /c",
+ "base": "http://example.org/foo/bar",
+ "failure": true
+ },
+ {
+ "input": "http://f:\n/c",
+ "base": "http://example.org/foo/bar",
+ "href": "http://f/c",
+ "origin": "http://f",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "f",
+ "hostname": "f",
+ "port": "",
+ "pathname": "/c",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://f:fifty-two/c",
+ "base": "http://example.org/foo/bar",
+ "failure": true
+ },
+ {
+ "input": "http://f:999999/c",
+ "base": "http://example.org/foo/bar",
+ "failure": true
+ },
+ {
+ "input": "non-special://f:999999/c",
+ "base": "http://example.org/foo/bar",
+ "failure": true
+ },
+ {
+ "input": "http://f: 21 / b ? d # e ",
+ "base": "http://example.org/foo/bar",
+ "failure": true
+ },
+ {
+ "input": "",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/bar",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": " \t",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/bar",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": ":foo.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/:foo.com/",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/:foo.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": ":foo.com\\",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/:foo.com/",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/:foo.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": ":",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/:",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/:",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": ":a",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/:a",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/:a",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": ":/",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/:/",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": ":\\",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/:/",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": ":#",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/:#",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/:",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "#",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/bar#",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "#/",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/bar#/",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "",
+ "hash": "#/"
+ },
+ {
+ "input": "#\\",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/bar#\\",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "",
+ "hash": "#\\"
+ },
+ {
+ "input": "#;?",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/bar#;?",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "",
+ "hash": "#;?"
+ },
+ {
+ "input": "?",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/bar?",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": ":23",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/:23",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/:23",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/:23",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/:23",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/:23",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "\\x",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/x",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/x",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "\\\\x\\hello",
+ "base": "http://example.org/foo/bar",
+ "href": "http://x/hello",
+ "origin": "http://x",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "x",
+ "hostname": "x",
+ "port": "",
+ "pathname": "/hello",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "::",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/::",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/::",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "::23",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/::23",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/::23",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "foo://",
+ "base": "http://example.org/foo/bar",
+ "href": "foo://",
+ "origin": "null",
+ "protocol": "foo:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://a:b@c:29/d",
+ "base": "http://example.org/foo/bar",
+ "href": "http://a:b@c:29/d",
+ "origin": "http://c:29",
+ "protocol": "http:",
+ "username": "a",
+ "password": "b",
+ "host": "c:29",
+ "hostname": "c",
+ "port": "29",
+ "pathname": "/d",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http::@c:29",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/:@c:29",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/:@c:29",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://&a:foo(b]c@d:2/",
+ "base": "http://example.org/foo/bar",
+ "href": "http://&a:foo(b%5Dc@d:2/",
+ "origin": "http://d:2",
+ "protocol": "http:",
+ "username": "&a",
+ "password": "foo(b%5Dc",
+ "host": "d:2",
+ "hostname": "d",
+ "port": "2",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://::@c@d:2",
+ "base": "http://example.org/foo/bar",
+ "href": "http://:%3A%40c@d:2/",
+ "origin": "http://d:2",
+ "protocol": "http:",
+ "username": "",
+ "password": "%3A%40c",
+ "host": "d:2",
+ "hostname": "d",
+ "port": "2",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://foo.com:b@d/",
+ "base": "http://example.org/foo/bar",
+ "href": "http://foo.com:b@d/",
+ "origin": "http://d",
+ "protocol": "http:",
+ "username": "foo.com",
+ "password": "b",
+ "host": "d",
+ "hostname": "d",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://foo.com/\\@",
+ "base": "http://example.org/foo/bar",
+ "href": "http://foo.com//@",
+ "origin": "http://foo.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "foo.com",
+ "hostname": "foo.com",
+ "port": "",
+ "pathname": "//@",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:\\\\foo.com\\",
+ "base": "http://example.org/foo/bar",
+ "href": "http://foo.com/",
+ "origin": "http://foo.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "foo.com",
+ "hostname": "foo.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:\\\\a\\b:c\\d@foo.com\\",
+ "base": "http://example.org/foo/bar",
+ "href": "http://a/b:c/d@foo.com/",
+ "origin": "http://a",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "a",
+ "hostname": "a",
+ "port": "",
+ "pathname": "/b:c/d@foo.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "foo:/",
+ "base": "http://example.org/foo/bar",
+ "href": "foo:/",
+ "origin": "null",
+ "protocol": "foo:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "foo:/bar.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "foo:/bar.com/",
+ "origin": "null",
+ "protocol": "foo:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/bar.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "foo://///////",
+ "base": "http://example.org/foo/bar",
+ "href": "foo://///////",
+ "origin": "null",
+ "protocol": "foo:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "///////",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "foo://///////bar.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "foo://///////bar.com/",
+ "origin": "null",
+ "protocol": "foo:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "///////bar.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "foo:////://///",
+ "base": "http://example.org/foo/bar",
+ "href": "foo:////://///",
+ "origin": "null",
+ "protocol": "foo:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//://///",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "c:/foo",
+ "base": "http://example.org/foo/bar",
+ "href": "c:/foo",
+ "origin": "null",
+ "protocol": "c:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/foo",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "//foo/bar",
+ "base": "http://example.org/foo/bar",
+ "href": "http://foo/bar",
+ "origin": "http://foo",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "foo",
+ "hostname": "foo",
+ "port": "",
+ "pathname": "/bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://foo/path;a??e#f#g",
+ "base": "http://example.org/foo/bar",
+ "href": "http://foo/path;a??e#f#g",
+ "origin": "http://foo",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "foo",
+ "hostname": "foo",
+ "port": "",
+ "pathname": "/path;a",
+ "search": "??e",
+ "hash": "#f#g"
+ },
+ {
+ "input": "http://foo/abcd?efgh?ijkl",
+ "base": "http://example.org/foo/bar",
+ "href": "http://foo/abcd?efgh?ijkl",
+ "origin": "http://foo",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "foo",
+ "hostname": "foo",
+ "port": "",
+ "pathname": "/abcd",
+ "search": "?efgh?ijkl",
+ "hash": ""
+ },
+ {
+ "input": "http://foo/abcd#foo?bar",
+ "base": "http://example.org/foo/bar",
+ "href": "http://foo/abcd#foo?bar",
+ "origin": "http://foo",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "foo",
+ "hostname": "foo",
+ "port": "",
+ "pathname": "/abcd",
+ "search": "",
+ "hash": "#foo?bar"
+ },
+ {
+ "input": "[61:24:74]:98",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/[61:24:74]:98",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/[61:24:74]:98",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:[61:27]/:foo",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/[61:27]/:foo",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/[61:27]/:foo",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://[1::2]:3:4",
+ "base": "http://example.org/foo/bar",
+ "failure": true
+ },
+ {
+ "input": "http://2001::1",
+ "base": "http://example.org/foo/bar",
+ "failure": true
+ },
+ {
+ "input": "http://2001::1]",
+ "base": "http://example.org/foo/bar",
+ "failure": true
+ },
+ {
+ "input": "http://2001::1]:80",
+ "base": "http://example.org/foo/bar",
+ "failure": true
+ },
+ {
+ "input": "http://[2001::1]",
+ "base": "http://example.org/foo/bar",
+ "href": "http://[2001::1]/",
+ "origin": "http://[2001::1]",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "[2001::1]",
+ "hostname": "[2001::1]",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://[::127.0.0.1]",
+ "base": "http://example.org/foo/bar",
+ "href": "http://[::7f00:1]/",
+ "origin": "http://[::7f00:1]",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "[::7f00:1]",
+ "hostname": "[::7f00:1]",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://[::127.0.0.1.]",
+ "base": "http://example.org/foo/bar",
+ "failure": true
+ },
+ {
+ "input": "http://[0:0:0:0:0:0:13.1.68.3]",
+ "base": "http://example.org/foo/bar",
+ "href": "http://[::d01:4403]/",
+ "origin": "http://[::d01:4403]",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "[::d01:4403]",
+ "hostname": "[::d01:4403]",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://[2001::1]:80",
+ "base": "http://example.org/foo/bar",
+ "href": "http://[2001::1]/",
+ "origin": "http://[2001::1]",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "[2001::1]",
+ "hostname": "[2001::1]",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/example.com/",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ftp:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "ftp://example.com/",
+ "origin": "ftp://example.com",
+ "protocol": "ftp:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "https:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "https://example.com/",
+ "origin": "https://example.com",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "madeupscheme:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "madeupscheme:/example.com/",
+ "origin": "null",
+ "protocol": "madeupscheme:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "file:///example.com/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://example:1/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "file://example:test/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "file://example%/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "file://[example]/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "ftps:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "ftps:/example.com/",
+ "origin": "null",
+ "protocol": "ftps:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "gopher:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "gopher:/example.com/",
+ "origin": "null",
+ "protocol": "gopher:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ws:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "ws://example.com/",
+ "origin": "ws://example.com",
+ "protocol": "ws:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "wss:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "wss://example.com/",
+ "origin": "wss://example.com",
+ "protocol": "wss:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "data:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "data:/example.com/",
+ "origin": "null",
+ "protocol": "data:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "javascript:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "javascript:/example.com/",
+ "origin": "null",
+ "protocol": "javascript:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "mailto:/example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "mailto:/example.com/",
+ "origin": "null",
+ "protocol": "mailto:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/example.com/",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ftp:example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "ftp://example.com/",
+ "origin": "ftp://example.com",
+ "protocol": "ftp:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "https:example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "https://example.com/",
+ "origin": "https://example.com",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "madeupscheme:example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "madeupscheme:example.com/",
+ "origin": "null",
+ "protocol": "madeupscheme:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ftps:example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "ftps:example.com/",
+ "origin": "null",
+ "protocol": "ftps:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "gopher:example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "gopher:example.com/",
+ "origin": "null",
+ "protocol": "gopher:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ws:example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "ws://example.com/",
+ "origin": "ws://example.com",
+ "protocol": "ws:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "wss:example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "wss://example.com/",
+ "origin": "wss://example.com",
+ "protocol": "wss:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "data:example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "data:example.com/",
+ "origin": "null",
+ "protocol": "data:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "javascript:example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "javascript:example.com/",
+ "origin": "null",
+ "protocol": "javascript:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "mailto:example.com/",
+ "base": "http://example.org/foo/bar",
+ "href": "mailto:example.com/",
+ "origin": "null",
+ "protocol": "mailto:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/a/b/c",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/a/b/c",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/a/b/c",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/a/ /c",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/a/%20/c",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/a/%20/c",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/a%2fc",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/a%2fc",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/a%2fc",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/a/%2f/c",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/a/%2f/c",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/a/%2f/c",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "#β",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/bar#%CE%B2",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "",
+ "hash": "#%CE%B2"
+ },
+ {
+ "input": "data:text/html,test#test",
+ "base": "http://example.org/foo/bar",
+ "href": "data:text/html,test#test",
+ "origin": "null",
+ "protocol": "data:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "text/html,test",
+ "search": "",
+ "hash": "#test"
+ },
+ {
+ "input": "tel:1234567890",
+ "base": "http://example.org/foo/bar",
+ "href": "tel:1234567890",
+ "origin": "null",
+ "protocol": "tel:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "1234567890",
+ "search": "",
+ "hash": ""
+ },
+ "# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/file.html",
+ {
+ "input": "file:c:\\foo\\bar.html",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///c:/foo/bar.html",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/c:/foo/bar.html",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": " File:c|////foo\\bar.html",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///c:////foo/bar.html",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/c:////foo/bar.html",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "C|/foo/bar",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///C:/foo/bar",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/C|\\foo\\bar",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///C:/foo/bar",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "//C|/foo/bar",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///C:/foo/bar",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "//server/file",
+ "base": "file:///tmp/mock/path",
+ "href": "file://server/file",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "server",
+ "hostname": "server",
+ "port": "",
+ "pathname": "/file",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "\\\\server\\file",
+ "base": "file:///tmp/mock/path",
+ "href": "file://server/file",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "server",
+ "hostname": "server",
+ "port": "",
+ "pathname": "/file",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/\\server/file",
+ "base": "file:///tmp/mock/path",
+ "href": "file://server/file",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "server",
+ "hostname": "server",
+ "port": "",
+ "pathname": "/file",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:///foo/bar.txt",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///foo/bar.txt",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/foo/bar.txt",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:///home/me",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///home/me",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/home/me",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "//",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "///",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "///test",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///test",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://test",
+ "base": "file:///tmp/mock/path",
+ "href": "file://test/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "test",
+ "hostname": "test",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://localhost",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://localhost/",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://localhost/test",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///test",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "test",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///tmp/mock/test",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/tmp/mock/test",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:test",
+ "base": "file:///tmp/mock/path",
+ "href": "file:///tmp/mock/test",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/tmp/mock/test",
+ "search": "",
+ "hash": ""
+ },
+ "# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/script-tests/path.js",
+ {
+ "input": "http://example.com/././foo",
+ "base": null,
+ "href": "http://example.com/foo",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/./.foo",
+ "base": null,
+ "href": "http://example.com/.foo",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/.foo",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/.",
+ "base": null,
+ "href": "http://example.com/foo/",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/./",
+ "base": null,
+ "href": "http://example.com/foo/",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/bar/..",
+ "base": null,
+ "href": "http://example.com/foo/",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/bar/../",
+ "base": null,
+ "href": "http://example.com/foo/",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/..bar",
+ "base": null,
+ "href": "http://example.com/foo/..bar",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo/..bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/bar/../ton",
+ "base": null,
+ "href": "http://example.com/foo/ton",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo/ton",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/bar/../ton/../../a",
+ "base": null,
+ "href": "http://example.com/a",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/a",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/../../..",
+ "base": null,
+ "href": "http://example.com/",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/../../../ton",
+ "base": null,
+ "href": "http://example.com/ton",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/ton",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/%2e",
+ "base": null,
+ "href": "http://example.com/foo/",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/%2e%2",
+ "base": null,
+ "href": "http://example.com/foo/%2e%2",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo/%2e%2",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/%2e./%2e%2e/.%2e/%2e.bar",
+ "base": null,
+ "href": "http://example.com/%2e.bar",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/%2e.bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com////../..",
+ "base": null,
+ "href": "http://example.com//",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "//",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/bar//../..",
+ "base": null,
+ "href": "http://example.com/foo/",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo/bar//..",
+ "base": null,
+ "href": "http://example.com/foo/bar/",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo/bar/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo",
+ "base": null,
+ "href": "http://example.com/foo",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/%20foo",
+ "base": null,
+ "href": "http://example.com/%20foo",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/%20foo",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo%",
+ "base": null,
+ "href": "http://example.com/foo%",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo%",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo%2",
+ "base": null,
+ "href": "http://example.com/foo%2",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo%2",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo%2zbar",
+ "base": null,
+ "href": "http://example.com/foo%2zbar",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo%2zbar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo%2©zbar",
+ "base": null,
+ "href": "http://example.com/foo%2%C3%82%C2%A9zbar",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo%2%C3%82%C2%A9zbar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo%41%7a",
+ "base": null,
+ "href": "http://example.com/foo%41%7a",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo%41%7a",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo\t\u0091%91",
+ "base": null,
+ "href": "http://example.com/foo%C2%91%91",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo%C2%91%91",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo%00%51",
+ "base": null,
+ "href": "http://example.com/foo%00%51",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foo%00%51",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/(%28:%3A%29)",
+ "base": null,
+ "href": "http://example.com/(%28:%3A%29)",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/(%28:%3A%29)",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/%3A%3a%3C%3c",
+ "base": null,
+ "href": "http://example.com/%3A%3a%3C%3c",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/%3A%3a%3C%3c",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/foo\tbar",
+ "base": null,
+ "href": "http://example.com/foobar",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/foobar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com\\\\foo\\\\bar",
+ "base": null,
+ "href": "http://example.com//foo//bar",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "//foo//bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/%7Ffp3%3Eju%3Dduvgw%3Dd",
+ "base": null,
+ "href": "http://example.com/%7Ffp3%3Eju%3Dduvgw%3Dd",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/%7Ffp3%3Eju%3Dduvgw%3Dd",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/@asdf%40",
+ "base": null,
+ "href": "http://example.com/@asdf%40",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/@asdf%40",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/你好你好",
+ "base": null,
+ "href": "http://example.com/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/‥/foo",
+ "base": null,
+ "href": "http://example.com/%E2%80%A5/foo",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/%E2%80%A5/foo",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com//foo",
+ "base": null,
+ "href": "http://example.com/%EF%BB%BF/foo",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/%EF%BB%BF/foo",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.com/‮/foo/‭/bar",
+ "base": null,
+ "href": "http://example.com/%E2%80%AE/foo/%E2%80%AD/bar",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/%E2%80%AE/foo/%E2%80%AD/bar",
+ "search": "",
+ "hash": ""
+ },
+ "# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/script-tests/relative.js",
+ {
+ "input": "http://www.google.com/foo?bar=baz#",
+ "base": null,
+ "href": "http://www.google.com/foo?bar=baz#",
+ "origin": "http://www.google.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.google.com",
+ "hostname": "www.google.com",
+ "port": "",
+ "pathname": "/foo",
+ "search": "?bar=baz",
+ "hash": ""
+ },
+ {
+ "input": "http://www.google.com/foo?bar=baz# »",
+ "base": null,
+ "href": "http://www.google.com/foo?bar=baz#%20%C2%BB",
+ "origin": "http://www.google.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.google.com",
+ "hostname": "www.google.com",
+ "port": "",
+ "pathname": "/foo",
+ "search": "?bar=baz",
+ "hash": "#%20%C2%BB"
+ },
+ {
+ "input": "data:test# »",
+ "base": null,
+ "href": "data:test#%20%C2%BB",
+ "origin": "null",
+ "protocol": "data:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "test",
+ "search": "",
+ "hash": "#%20%C2%BB"
+ },
+ {
+ "input": "http://www.google.com",
+ "base": null,
+ "href": "http://www.google.com/",
+ "origin": "http://www.google.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.google.com",
+ "hostname": "www.google.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://192.0x00A80001",
+ "base": null,
+ "href": "http://192.168.0.1/",
+ "origin": "http://192.168.0.1",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "192.168.0.1",
+ "hostname": "192.168.0.1",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://www/foo%2Ehtml",
+ "base": null,
+ "href": "http://www/foo%2Ehtml",
+ "origin": "http://www",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www",
+ "hostname": "www",
+ "port": "",
+ "pathname": "/foo%2Ehtml",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://www/foo/%2E/html",
+ "base": null,
+ "href": "http://www/foo/html",
+ "origin": "http://www",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www",
+ "hostname": "www",
+ "port": "",
+ "pathname": "/foo/html",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://user:pass@/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://%25DOMAIN:foobar@foodomain.com/",
+ "base": null,
+ "href": "http://%25DOMAIN:foobar@foodomain.com/",
+ "origin": "http://foodomain.com",
+ "protocol": "http:",
+ "username": "%25DOMAIN",
+ "password": "foobar",
+ "host": "foodomain.com",
+ "hostname": "foodomain.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:\\\\www.google.com\\foo",
+ "base": null,
+ "href": "http://www.google.com/foo",
+ "origin": "http://www.google.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.google.com",
+ "hostname": "www.google.com",
+ "port": "",
+ "pathname": "/foo",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://foo:80/",
+ "base": null,
+ "href": "http://foo/",
+ "origin": "http://foo",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "foo",
+ "hostname": "foo",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://foo:81/",
+ "base": null,
+ "href": "http://foo:81/",
+ "origin": "http://foo:81",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "foo:81",
+ "hostname": "foo",
+ "port": "81",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "httpa://foo:80/",
+ "base": null,
+ "href": "httpa://foo:80/",
+ "origin": "null",
+ "protocol": "httpa:",
+ "username": "",
+ "password": "",
+ "host": "foo:80",
+ "hostname": "foo",
+ "port": "80",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://foo:-80/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "https://foo:443/",
+ "base": null,
+ "href": "https://foo/",
+ "origin": "https://foo",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "foo",
+ "hostname": "foo",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "https://foo:80/",
+ "base": null,
+ "href": "https://foo:80/",
+ "origin": "https://foo:80",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "foo:80",
+ "hostname": "foo",
+ "port": "80",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ftp://foo:21/",
+ "base": null,
+ "href": "ftp://foo/",
+ "origin": "ftp://foo",
+ "protocol": "ftp:",
+ "username": "",
+ "password": "",
+ "host": "foo",
+ "hostname": "foo",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ftp://foo:80/",
+ "base": null,
+ "href": "ftp://foo:80/",
+ "origin": "ftp://foo:80",
+ "protocol": "ftp:",
+ "username": "",
+ "password": "",
+ "host": "foo:80",
+ "hostname": "foo",
+ "port": "80",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "gopher://foo:70/",
+ "base": null,
+ "href": "gopher://foo:70/",
+ "origin": "null",
+ "protocol": "gopher:",
+ "username": "",
+ "password": "",
+ "host": "foo:70",
+ "hostname": "foo",
+ "port": "70",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "gopher://foo:443/",
+ "base": null,
+ "href": "gopher://foo:443/",
+ "origin": "null",
+ "protocol": "gopher:",
+ "username": "",
+ "password": "",
+ "host": "foo:443",
+ "hostname": "foo",
+ "port": "443",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ws://foo:80/",
+ "base": null,
+ "href": "ws://foo/",
+ "origin": "ws://foo",
+ "protocol": "ws:",
+ "username": "",
+ "password": "",
+ "host": "foo",
+ "hostname": "foo",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ws://foo:81/",
+ "base": null,
+ "href": "ws://foo:81/",
+ "origin": "ws://foo:81",
+ "protocol": "ws:",
+ "username": "",
+ "password": "",
+ "host": "foo:81",
+ "hostname": "foo",
+ "port": "81",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ws://foo:443/",
+ "base": null,
+ "href": "ws://foo:443/",
+ "origin": "ws://foo:443",
+ "protocol": "ws:",
+ "username": "",
+ "password": "",
+ "host": "foo:443",
+ "hostname": "foo",
+ "port": "443",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ws://foo:815/",
+ "base": null,
+ "href": "ws://foo:815/",
+ "origin": "ws://foo:815",
+ "protocol": "ws:",
+ "username": "",
+ "password": "",
+ "host": "foo:815",
+ "hostname": "foo",
+ "port": "815",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "wss://foo:80/",
+ "base": null,
+ "href": "wss://foo:80/",
+ "origin": "wss://foo:80",
+ "protocol": "wss:",
+ "username": "",
+ "password": "",
+ "host": "foo:80",
+ "hostname": "foo",
+ "port": "80",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "wss://foo:81/",
+ "base": null,
+ "href": "wss://foo:81/",
+ "origin": "wss://foo:81",
+ "protocol": "wss:",
+ "username": "",
+ "password": "",
+ "host": "foo:81",
+ "hostname": "foo",
+ "port": "81",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "wss://foo:443/",
+ "base": null,
+ "href": "wss://foo/",
+ "origin": "wss://foo",
+ "protocol": "wss:",
+ "username": "",
+ "password": "",
+ "host": "foo",
+ "hostname": "foo",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "wss://foo:815/",
+ "base": null,
+ "href": "wss://foo:815/",
+ "origin": "wss://foo:815",
+ "protocol": "wss:",
+ "username": "",
+ "password": "",
+ "host": "foo:815",
+ "hostname": "foo",
+ "port": "815",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ftp:/example.com/",
+ "base": null,
+ "href": "ftp://example.com/",
+ "origin": "ftp://example.com",
+ "protocol": "ftp:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "https:/example.com/",
+ "base": null,
+ "href": "https://example.com/",
+ "origin": "https://example.com",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "madeupscheme:/example.com/",
+ "base": null,
+ "href": "madeupscheme:/example.com/",
+ "origin": "null",
+ "protocol": "madeupscheme:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:/example.com/",
+ "base": null,
+ "href": "file:///example.com/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ftps:/example.com/",
+ "base": null,
+ "href": "ftps:/example.com/",
+ "origin": "null",
+ "protocol": "ftps:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "gopher:/example.com/",
+ "base": null,
+ "href": "gopher:/example.com/",
+ "origin": "null",
+ "protocol": "gopher:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ws:/example.com/",
+ "base": null,
+ "href": "ws://example.com/",
+ "origin": "ws://example.com",
+ "protocol": "ws:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "wss:/example.com/",
+ "base": null,
+ "href": "wss://example.com/",
+ "origin": "wss://example.com",
+ "protocol": "wss:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "data:/example.com/",
+ "base": null,
+ "href": "data:/example.com/",
+ "origin": "null",
+ "protocol": "data:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "javascript:/example.com/",
+ "base": null,
+ "href": "javascript:/example.com/",
+ "origin": "null",
+ "protocol": "javascript:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "mailto:/example.com/",
+ "base": null,
+ "href": "mailto:/example.com/",
+ "origin": "null",
+ "protocol": "mailto:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ftp:example.com/",
+ "base": null,
+ "href": "ftp://example.com/",
+ "origin": "ftp://example.com",
+ "protocol": "ftp:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "https:example.com/",
+ "base": null,
+ "href": "https://example.com/",
+ "origin": "https://example.com",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "madeupscheme:example.com/",
+ "base": null,
+ "href": "madeupscheme:example.com/",
+ "origin": "null",
+ "protocol": "madeupscheme:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ftps:example.com/",
+ "base": null,
+ "href": "ftps:example.com/",
+ "origin": "null",
+ "protocol": "ftps:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "gopher:example.com/",
+ "base": null,
+ "href": "gopher:example.com/",
+ "origin": "null",
+ "protocol": "gopher:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ws:example.com/",
+ "base": null,
+ "href": "ws://example.com/",
+ "origin": "ws://example.com",
+ "protocol": "ws:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "wss:example.com/",
+ "base": null,
+ "href": "wss://example.com/",
+ "origin": "wss://example.com",
+ "protocol": "wss:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "data:example.com/",
+ "base": null,
+ "href": "data:example.com/",
+ "origin": "null",
+ "protocol": "data:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "javascript:example.com/",
+ "base": null,
+ "href": "javascript:example.com/",
+ "origin": "null",
+ "protocol": "javascript:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "mailto:example.com/",
+ "base": null,
+ "href": "mailto:example.com/",
+ "origin": "null",
+ "protocol": "mailto:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "example.com/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://@www.example.com",
+ "base": null,
+ "href": "http://www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://a:b@www.example.com",
+ "base": null,
+ "href": "http://a:b@www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "a",
+ "password": "b",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://@pple.com",
+ "base": null,
+ "href": "http://pple.com/",
+ "origin": "http://pple.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "pple.com",
+ "hostname": "pple.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://:b@www.example.com",
+ "base": null,
+ "href": "http://:b@www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "b",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:/:@/www.example.com",
+ "base": null,
+ "failure": true,
+ "relativeTo": "non-opaque-path-base"
+ },
+ {
+ "input": "http://user@/www.example.com",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http:@/www.example.com",
+ "base": null,
+ "failure": true,
+ "relativeTo": "non-opaque-path-base"
+ },
+ {
+ "input": "http:/@/www.example.com",
+ "base": null,
+ "failure": true,
+ "relativeTo": "non-opaque-path-base"
+ },
+ {
+ "input": "http://@/www.example.com",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "https:@/www.example.com",
+ "base": null,
+ "failure": true,
+ "relativeTo": "non-opaque-path-base"
+ },
+ {
+ "input": "http:a:b@/www.example.com",
+ "base": null,
+ "failure": true,
+ "relativeTo": "non-opaque-path-base"
+ },
+ {
+ "input": "http:/a:b@/www.example.com",
+ "base": null,
+ "failure": true,
+ "relativeTo": "non-opaque-path-base"
+ },
+ {
+ "input": "http://a:b@/www.example.com",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http::@/www.example.com",
+ "base": null,
+ "failure": true,
+ "relativeTo": "non-opaque-path-base"
+ },
+ {
+ "input": "http://a:@www.example.com",
+ "base": null,
+ "href": "http://a@www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "a",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://www.@pple.com",
+ "base": null,
+ "href": "http://www.@pple.com/",
+ "origin": "http://pple.com",
+ "protocol": "http:",
+ "username": "www.",
+ "password": "",
+ "host": "pple.com",
+ "hostname": "pple.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http:@:www.example.com",
+ "base": null,
+ "failure": true,
+ "relativeTo": "non-opaque-path-base"
+ },
+ {
+ "input": "http:/@:www.example.com",
+ "base": null,
+ "failure": true,
+ "relativeTo": "non-opaque-path-base"
+ },
+ {
+ "input": "http://@:www.example.com",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://:@www.example.com",
+ "base": null,
+ "href": "http://www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "# Others",
+ {
+ "input": "/",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/test.txt",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example.com/test.txt",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/test.txt",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": ".",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "..",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example.com/",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "test.txt",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example.com/test.txt",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/test.txt",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "./test.txt",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example.com/test.txt",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/test.txt",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "../test.txt",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example.com/test.txt",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/test.txt",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "../aaa/test.txt",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example.com/aaa/test.txt",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/aaa/test.txt",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "../../test.txt",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example.com/test.txt",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/test.txt",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "中/test.txt",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example.com/%E4%B8%AD/test.txt",
+ "origin": "http://www.example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "port": "",
+ "pathname": "/%E4%B8%AD/test.txt",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://www.example2.com",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example2.com/",
+ "origin": "http://www.example2.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example2.com",
+ "hostname": "www.example2.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "//www.example2.com",
+ "base": "http://www.example.com/test",
+ "href": "http://www.example2.com/",
+ "origin": "http://www.example2.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.example2.com",
+ "hostname": "www.example2.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:...",
+ "base": "http://www.example.com/test",
+ "href": "file:///...",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/...",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:..",
+ "base": "http://www.example.com/test",
+ "href": "file:///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:a",
+ "base": "http://www.example.com/test",
+ "href": "file:///a",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/a",
+ "search": "",
+ "hash": ""
+ },
+ "# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/host.html",
+ "Basic canonicalization, uppercase should be converted to lowercase",
+ {
+ "input": "http://ExAmPlE.CoM",
+ "base": "http://other.com/",
+ "href": "http://example.com/",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example example.com",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://Goo%20 goo%7C|.com",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://[]",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://[:]",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ "U+3000 is mapped to U+0020 (space) which is disallowed",
+ {
+ "input": "http://GOO\u00a0\u3000goo.com",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ "Other types of space (no-break, zero-width, zero-width-no-break) are name-prepped away to nothing. U+200B, U+2060, and U+FEFF, are ignored",
+ {
+ "input": "http://GOO\u200b\u2060\ufeffgoo.com",
+ "base": "http://other.com/",
+ "href": "http://googoo.com/",
+ "origin": "http://googoo.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "googoo.com",
+ "hostname": "googoo.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "Leading and trailing C0 control or space",
+ {
+ "input": "\u0000\u001b\u0004\u0012 http://example.com/\u001f \u000d ",
+ "base": null,
+ "href": "http://example.com/",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "Ideographic full stop (full-width period for Chinese, etc.) should be treated as a dot. U+3002 is mapped to U+002E (dot)",
+ {
+ "input": "http://www.foo。bar.com",
+ "base": "http://other.com/",
+ "href": "http://www.foo.bar.com/",
+ "origin": "http://www.foo.bar.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "www.foo.bar.com",
+ "hostname": "www.foo.bar.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "Invalid unicode characters should fail... U+FDD0 is disallowed; %ef%b7%90 is U+FDD0",
+ {
+ "input": "http://\ufdd0zyx.com",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ "This is the same as previous but escaped",
+ {
+ "input": "http://%ef%b7%90zyx.com",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ "U+FFFD",
+ {
+ "input": "https://\ufffd",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "https://%EF%BF%BD",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "https://x/\ufffd?\ufffd#\ufffd",
+ "base": null,
+ "href": "https://x/%EF%BF%BD?%EF%BF%BD#%EF%BF%BD",
+ "origin": "https://x",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "x",
+ "hostname": "x",
+ "port": "",
+ "pathname": "/%EF%BF%BD",
+ "search": "?%EF%BF%BD",
+ "hash": "#%EF%BF%BD"
+ },
+ "Domain is ASCII, but a label is invalid IDNA",
+ {
+ "input": "http://a.b.c.xn--pokxncvks",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://10.0.0.xn--pokxncvks",
+ "base": null,
+ "failure": true
+ },
+ "IDNA labels should be matched case-insensitively",
+ {
+ "input": "http://a.b.c.XN--pokxncvks",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a.b.c.Xn--pokxncvks",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://10.0.0.XN--pokxncvks",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://10.0.0.xN--pokxncvks",
+ "base": null,
+ "failure": true
+ },
+ "Test name prepping, fullwidth input should be converted to ASCII and NOT IDN-ized. This is 'Go' in fullwidth UTF-8/UTF-16.",
+ {
+ "input": "http://Go.com",
+ "base": "http://other.com/",
+ "href": "http://go.com/",
+ "origin": "http://go.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "go.com",
+ "hostname": "go.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "URL spec forbids the following. https://www.w3.org/Bugs/Public/show_bug.cgi?id=24257",
+ {
+ "input": "http://%41.com",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://%ef%bc%85%ef%bc%94%ef%bc%91.com",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ "...%00 in fullwidth should fail (also as escaped UTF-8 input)",
+ {
+ "input": "http://%00.com",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://%ef%bc%85%ef%bc%90%ef%bc%90.com",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ "Basic IDN support, UTF-8 and UTF-16 input should be converted to IDN",
+ {
+ "input": "http://你好你好",
+ "base": "http://other.com/",
+ "href": "http://xn--6qqa088eba/",
+ "origin": "http://xn--6qqa088eba",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "xn--6qqa088eba",
+ "hostname": "xn--6qqa088eba",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "https://faß.ExAmPlE/",
+ "base": null,
+ "href": "https://xn--fa-hia.example/",
+ "origin": "https://xn--fa-hia.example",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "xn--fa-hia.example",
+ "hostname": "xn--fa-hia.example",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "sc://faß.ExAmPlE/",
+ "base": null,
+ "href": "sc://fa%C3%9F.ExAmPlE/",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "fa%C3%9F.ExAmPlE",
+ "hostname": "fa%C3%9F.ExAmPlE",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "Invalid escaped characters should fail and the percents should be escaped. https://www.w3.org/Bugs/Public/show_bug.cgi?id=24191",
+ {
+ "input": "http://%zz%66%a.com",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ "If we get an invalid character that has been escaped.",
+ {
+ "input": "http://%25",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://hello%00",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ "Escaped numbers should be treated like IP addresses if they are.",
+ {
+ "input": "http://%30%78%63%30%2e%30%32%35%30.01",
+ "base": "http://other.com/",
+ "href": "http://192.168.0.1/",
+ "origin": "http://192.168.0.1",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "192.168.0.1",
+ "hostname": "192.168.0.1",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://%30%78%63%30%2e%30%32%35%30.01%2e",
+ "base": "http://other.com/",
+ "href": "http://192.168.0.1/",
+ "origin": "http://192.168.0.1",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "192.168.0.1",
+ "hostname": "192.168.0.1",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://192.168.0.257",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ "Invalid escaping in hosts causes failure",
+ {
+ "input": "http://%3g%78%63%30%2e%30%32%35%30%2E.01",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ "A space in a host causes failure",
+ {
+ "input": "http://192.168.0.1 hello",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "https://x x:12",
+ "base": null,
+ "failure": true
+ },
+ "Fullwidth and escaped UTF-8 fullwidth should still be treated as IP",
+ {
+ "input": "http://0Xc0.0250.01",
+ "base": "http://other.com/",
+ "href": "http://192.168.0.1/",
+ "origin": "http://192.168.0.1",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "192.168.0.1",
+ "hostname": "192.168.0.1",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "Domains with empty labels",
+ {
+ "input": "http://./",
+ "base": null,
+ "href": "http://./",
+ "origin": "http://.",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": ".",
+ "hostname": ".",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://../",
+ "base": null,
+ "href": "http://../",
+ "origin": "http://..",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "..",
+ "hostname": "..",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "Non-special domains with empty labels",
+ {
+ "input": "h://.",
+ "base": null,
+ "href": "h://.",
+ "origin": "null",
+ "protocol": "h:",
+ "username": "",
+ "password": "",
+ "host": ".",
+ "hostname": ".",
+ "port": "",
+ "pathname": "",
+ "search": "",
+ "hash": ""
+ },
+ "Broken IPv6",
+ {
+ "input": "http://[www.google.com]/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://[google.com]",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://[::1.2.3.4x]",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://[::1.2.3.]",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://[::1.2.]",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://[::.1.2]",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://[::1.]",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://[::.1]",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://[::%31]",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://%5B::1]",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ "Misc Unicode",
+ {
+ "input": "http://foo:💩@example.com/bar",
+ "base": "http://other.com/",
+ "href": "http://foo:%F0%9F%92%A9@example.com/bar",
+ "origin": "http://example.com",
+ "protocol": "http:",
+ "username": "foo",
+ "password": "%F0%9F%92%A9",
+ "host": "example.com",
+ "hostname": "example.com",
+ "port": "",
+ "pathname": "/bar",
+ "search": "",
+ "hash": ""
+ },
+ "# resolving a fragment against any scheme succeeds",
+ {
+ "input": "#",
+ "base": "test:test",
+ "href": "test:test#",
+ "origin": "null",
+ "protocol": "test:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "test",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "#x",
+ "base": "mailto:x@x.com",
+ "href": "mailto:x@x.com#x",
+ "origin": "null",
+ "protocol": "mailto:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "x@x.com",
+ "search": "",
+ "hash": "#x"
+ },
+ {
+ "input": "#x",
+ "base": "data:,",
+ "href": "data:,#x",
+ "origin": "null",
+ "protocol": "data:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": ",",
+ "search": "",
+ "hash": "#x"
+ },
+ {
+ "input": "#x",
+ "base": "about:blank",
+ "href": "about:blank#x",
+ "origin": "null",
+ "protocol": "about:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "blank",
+ "search": "",
+ "hash": "#x"
+ },
+ {
+ "input": "#x:y",
+ "base": "about:blank",
+ "href": "about:blank#x:y",
+ "origin": "null",
+ "protocol": "about:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "blank",
+ "search": "",
+ "hash": "#x:y"
+ },
+ {
+ "input": "#",
+ "base": "test:test?test",
+ "href": "test:test?test#",
+ "origin": "null",
+ "protocol": "test:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "test",
+ "search": "?test",
+ "hash": ""
+ },
+ "# multiple @ in authority state",
+ {
+ "input": "https://@test@test@example:800/",
+ "base": "http://doesnotmatter/",
+ "href": "https://%40test%40test@example:800/",
+ "origin": "https://example:800",
+ "protocol": "https:",
+ "username": "%40test%40test",
+ "password": "",
+ "host": "example:800",
+ "hostname": "example",
+ "port": "800",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "https://@@@example",
+ "base": "http://doesnotmatter/",
+ "href": "https://%40%40@example/",
+ "origin": "https://example",
+ "protocol": "https:",
+ "username": "%40%40",
+ "password": "",
+ "host": "example",
+ "hostname": "example",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "non-az-09 characters",
+ {
+ "input": "http://`{}:`{}@h/`{}?`{}",
+ "base": "http://doesnotmatter/",
+ "href": "http://%60%7B%7D:%60%7B%7D@h/%60%7B%7D?`{}",
+ "origin": "http://h",
+ "protocol": "http:",
+ "username": "%60%7B%7D",
+ "password": "%60%7B%7D",
+ "host": "h",
+ "hostname": "h",
+ "port": "",
+ "pathname": "/%60%7B%7D",
+ "search": "?`{}",
+ "hash": ""
+ },
+ "byte is ' and url is special",
+ {
+ "input": "http://host/?'",
+ "base": null,
+ "href": "http://host/?%27",
+ "origin": "http://host",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "host",
+ "hostname": "host",
+ "port": "",
+ "pathname": "/",
+ "search": "?%27",
+ "hash": ""
+ },
+ {
+ "input": "notspecial://host/?'",
+ "base": null,
+ "href": "notspecial://host/?'",
+ "origin": "null",
+ "protocol": "notspecial:",
+ "username": "",
+ "password": "",
+ "host": "host",
+ "hostname": "host",
+ "port": "",
+ "pathname": "/",
+ "search": "?'",
+ "hash": ""
+ },
+ "# Credentials in base",
+ {
+ "input": "/some/path",
+ "base": "http://user@example.org/smth",
+ "href": "http://user@example.org/some/path",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "user",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/some/path",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "",
+ "base": "http://user:pass@example.org:21/smth",
+ "href": "http://user:pass@example.org:21/smth",
+ "origin": "http://example.org:21",
+ "protocol": "http:",
+ "username": "user",
+ "password": "pass",
+ "host": "example.org:21",
+ "hostname": "example.org",
+ "port": "21",
+ "pathname": "/smth",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/some/path",
+ "base": "http://user:pass@example.org:21/smth",
+ "href": "http://user:pass@example.org:21/some/path",
+ "origin": "http://example.org:21",
+ "protocol": "http:",
+ "username": "user",
+ "password": "pass",
+ "host": "example.org:21",
+ "hostname": "example.org",
+ "port": "21",
+ "pathname": "/some/path",
+ "search": "",
+ "hash": ""
+ },
+ "# a set of tests designed by zcorpan for relative URLs with unknown schemes",
+ {
+ "input": "i",
+ "base": "sc:sd",
+ "failure": true
+ },
+ {
+ "input": "i",
+ "base": "sc:sd/sd",
+ "failure": true
+ },
+ {
+ "input": "i",
+ "base": "sc:/pa/pa",
+ "href": "sc:/pa/i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/pa/i",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "i",
+ "base": "sc://ho/pa",
+ "href": "sc://ho/i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "ho",
+ "hostname": "ho",
+ "port": "",
+ "pathname": "/i",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "i",
+ "base": "sc:///pa/pa",
+ "href": "sc:///pa/i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/pa/i",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "../i",
+ "base": "sc:sd",
+ "failure": true
+ },
+ {
+ "input": "../i",
+ "base": "sc:sd/sd",
+ "failure": true
+ },
+ {
+ "input": "../i",
+ "base": "sc:/pa/pa",
+ "href": "sc:/i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/i",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "../i",
+ "base": "sc://ho/pa",
+ "href": "sc://ho/i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "ho",
+ "hostname": "ho",
+ "port": "",
+ "pathname": "/i",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "../i",
+ "base": "sc:///pa/pa",
+ "href": "sc:///i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/i",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/i",
+ "base": "sc:sd",
+ "failure": true
+ },
+ {
+ "input": "/i",
+ "base": "sc:sd/sd",
+ "failure": true
+ },
+ {
+ "input": "/i",
+ "base": "sc:/pa/pa",
+ "href": "sc:/i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/i",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/i",
+ "base": "sc://ho/pa",
+ "href": "sc://ho/i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "ho",
+ "hostname": "ho",
+ "port": "",
+ "pathname": "/i",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/i",
+ "base": "sc:///pa/pa",
+ "href": "sc:///i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/i",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "?i",
+ "base": "sc:sd",
+ "failure": true
+ },
+ {
+ "input": "?i",
+ "base": "sc:sd/sd",
+ "failure": true
+ },
+ {
+ "input": "?i",
+ "base": "sc:/pa/pa",
+ "href": "sc:/pa/pa?i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/pa/pa",
+ "search": "?i",
+ "hash": ""
+ },
+ {
+ "input": "?i",
+ "base": "sc://ho/pa",
+ "href": "sc://ho/pa?i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "ho",
+ "hostname": "ho",
+ "port": "",
+ "pathname": "/pa",
+ "search": "?i",
+ "hash": ""
+ },
+ {
+ "input": "?i",
+ "base": "sc:///pa/pa",
+ "href": "sc:///pa/pa?i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/pa/pa",
+ "search": "?i",
+ "hash": ""
+ },
+ {
+ "input": "#i",
+ "base": "sc:sd",
+ "href": "sc:sd#i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "sd",
+ "search": "",
+ "hash": "#i"
+ },
+ {
+ "input": "#i",
+ "base": "sc:sd/sd",
+ "href": "sc:sd/sd#i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "sd/sd",
+ "search": "",
+ "hash": "#i"
+ },
+ {
+ "input": "#i",
+ "base": "sc:/pa/pa",
+ "href": "sc:/pa/pa#i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/pa/pa",
+ "search": "",
+ "hash": "#i"
+ },
+ {
+ "input": "#i",
+ "base": "sc://ho/pa",
+ "href": "sc://ho/pa#i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "ho",
+ "hostname": "ho",
+ "port": "",
+ "pathname": "/pa",
+ "search": "",
+ "hash": "#i"
+ },
+ {
+ "input": "#i",
+ "base": "sc:///pa/pa",
+ "href": "sc:///pa/pa#i",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/pa/pa",
+ "search": "",
+ "hash": "#i"
+ },
+ "# make sure that relative URL logic works on known typically non-relative schemes too",
+ {
+ "input": "data:/../",
+ "base": null,
+ "href": "data:/",
+ "origin": "null",
+ "protocol": "data:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "javascript:/../",
+ "base": null,
+ "href": "javascript:/",
+ "origin": "null",
+ "protocol": "javascript:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "mailto:/../",
+ "base": null,
+ "href": "mailto:/",
+ "origin": "null",
+ "protocol": "mailto:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "# unknown schemes and their hosts",
+ {
+ "input": "sc://ñ.test/",
+ "base": null,
+ "href": "sc://%C3%B1.test/",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "%C3%B1.test",
+ "hostname": "%C3%B1.test",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "sc://%/",
+ "base": null,
+ "href": "sc://%/",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "%",
+ "hostname": "%",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "sc://@/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "sc://te@s:t@/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "sc://:/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "sc://:12/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "x",
+ "base": "sc://ñ",
+ "href": "sc://%C3%B1/x",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "%C3%B1",
+ "hostname": "%C3%B1",
+ "port": "",
+ "pathname": "/x",
+ "search": "",
+ "hash": ""
+ },
+ "# unknown schemes and backslashes",
+ {
+ "input": "sc:\\../",
+ "base": null,
+ "href": "sc:\\../",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "\\../",
+ "search": "",
+ "hash": ""
+ },
+ "# unknown scheme with path looking like a password",
+ {
+ "input": "sc::a@example.net",
+ "base": null,
+ "href": "sc::a@example.net",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": ":a@example.net",
+ "search": "",
+ "hash": ""
+ },
+ "# unknown scheme with bogus percent-encoding",
+ {
+ "input": "wow:%NBD",
+ "base": null,
+ "href": "wow:%NBD",
+ "origin": "null",
+ "protocol": "wow:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "%NBD",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "wow:%1G",
+ "base": null,
+ "href": "wow:%1G",
+ "origin": "null",
+ "protocol": "wow:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "%1G",
+ "search": "",
+ "hash": ""
+ },
+ "# unknown scheme with non-URL characters",
+ {
+ "input": "wow:\uFFFF",
+ "base": null,
+ "href": "wow:%EF%BF%BF",
+ "origin": "null",
+ "protocol": "wow:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "%EF%BF%BF",
+ "search": "",
+ "hash": ""
+ },
+ "Forbidden host code points",
+ {
+ "input": "sc://a\u0000b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "sc://a b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "sc://a<b",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "sc://a>b",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "sc://a[b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "sc://a\\b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "sc://a]b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "sc://a^b",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "sc://a|b/",
+ "base": null,
+ "failure": true
+ },
+ "Forbidden host codepoints: tabs and newlines are removed during preprocessing",
+ {
+ "input": "foo://ho\u0009st/",
+ "base": null,
+ "hash": "",
+ "host": "host",
+ "hostname": "host",
+ "href": "foo://host/",
+ "password": "",
+ "pathname": "/",
+ "port": "",
+ "protocol": "foo:",
+ "search": "",
+ "username": ""
+ },
+ {
+ "input": "foo://ho\u000Ast/",
+ "base": null,
+ "hash": "",
+ "host": "host",
+ "hostname": "host",
+ "href": "foo://host/",
+ "password": "",
+ "pathname": "/",
+ "port": "",
+ "protocol": "foo:",
+ "search": "",
+ "username": ""
+ },
+ {
+ "input": "foo://ho\u000Dst/",
+ "base": null,
+ "hash": "",
+ "host": "host",
+ "hostname": "host",
+ "href": "foo://host/",
+ "password": "",
+ "pathname": "/",
+ "port": "",
+ "protocol": "foo:",
+ "search": "",
+ "username": ""
+ },
+ "Forbidden domain code-points",
+ {
+ "input": "http://a\u0000b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u0001b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u0002b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u0003b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u0004b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u0005b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u0006b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u0007b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u0008b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u000Bb/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u000Cb/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u000Eb/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u000Fb/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u0010b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u0011b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u0012b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u0013b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u0014b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u0015b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u0016b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u0017b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u0018b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u0019b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u001Ab/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u001Bb/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u001Cb/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u001Db/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u001Eb/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u001Fb/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a%b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a<b",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a>b",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a[b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a]b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a^b",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a|b/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://a\u007Fb/",
+ "base": null,
+ "failure": true
+ },
+ "Forbidden domain codepoints: tabs and newlines are removed during preprocessing",
+ {
+ "input": "http://ho\u0009st/",
+ "base": null,
+ "hash": "",
+ "host": "host",
+ "hostname": "host",
+ "href": "http://host/",
+ "password": "",
+ "pathname": "/",
+ "port": "",
+ "protocol": "http:",
+ "search": "",
+ "username": ""
+ },
+ {
+ "input": "http://ho\u000Ast/",
+ "base": null,
+ "hash": "",
+ "host": "host",
+ "hostname": "host",
+ "href": "http://host/",
+ "password": "",
+ "pathname": "/",
+ "port": "",
+ "protocol": "http:",
+ "search": "",
+ "username": ""
+ },
+ {
+ "input": "http://ho\u000Dst/",
+ "base": null,
+ "hash": "",
+ "host": "host",
+ "hostname": "host",
+ "href": "http://host/",
+ "password": "",
+ "pathname": "/",
+ "port": "",
+ "protocol": "http:",
+ "search": "",
+ "username": ""
+ },
+ "Encoded forbidden domain codepoints in special URLs",
+ {
+ "input": "http://ho%00st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%01st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%02st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%03st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%04st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%05st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%06st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%07st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%08st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%09st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%0Ast/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%0Bst/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%0Cst/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%0Dst/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%0Est/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%0Fst/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%10st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%11st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%12st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%13st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%14st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%15st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%16st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%17st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%18st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%19st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%1Ast/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%1Bst/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%1Cst/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%1Dst/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%1Est/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%1Fst/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%20st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%23st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%25st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%2Fst/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%3Ast/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%3Cst/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%3Est/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%3Fst/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%40st/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%5Bst/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%5Cst/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%5Dst/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%7Cst/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://ho%7Fst/",
+ "base": null,
+ "failure": true
+ },
+ "Allowed host/domain code points",
+ {
+ "input": "sc://\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u000B\u000C\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F\u007F!\"$%&'()*+,-.;=_`{}~/",
+ "base": null,
+ "href": "sc://%01%02%03%04%05%06%07%08%0B%0C%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%7F!\"$%&'()*+,-.;=_`{}~/",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "%01%02%03%04%05%06%07%08%0B%0C%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%7F!\"$%&'()*+,-.;=_`{}~",
+ "hostname": "%01%02%03%04%05%06%07%08%0B%0C%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%7F!\"$%&'()*+,-.;=_`{}~",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "# Hosts and percent-encoding",
+ {
+ "input": "ftp://example.com%80/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "ftp://example.com%A0/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "https://example.com%80/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "https://example.com%A0/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "ftp://%e2%98%83",
+ "base": null,
+ "href": "ftp://xn--n3h/",
+ "origin": "ftp://xn--n3h",
+ "protocol": "ftp:",
+ "username": "",
+ "password": "",
+ "host": "xn--n3h",
+ "hostname": "xn--n3h",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "https://%e2%98%83",
+ "base": null,
+ "href": "https://xn--n3h/",
+ "origin": "https://xn--n3h",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "xn--n3h",
+ "hostname": "xn--n3h",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "# tests from jsdom/whatwg-url designed for code coverage",
+ {
+ "input": "http://127.0.0.1:10100/relative_import.html",
+ "base": null,
+ "href": "http://127.0.0.1:10100/relative_import.html",
+ "origin": "http://127.0.0.1:10100",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "127.0.0.1:10100",
+ "hostname": "127.0.0.1",
+ "port": "10100",
+ "pathname": "/relative_import.html",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://facebook.com/?foo=%7B%22abc%22",
+ "base": null,
+ "href": "http://facebook.com/?foo=%7B%22abc%22",
+ "origin": "http://facebook.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "facebook.com",
+ "hostname": "facebook.com",
+ "port": "",
+ "pathname": "/",
+ "search": "?foo=%7B%22abc%22",
+ "hash": ""
+ },
+ {
+ "input": "https://localhost:3000/jqueryui@1.2.3",
+ "base": null,
+ "href": "https://localhost:3000/jqueryui@1.2.3",
+ "origin": "https://localhost:3000",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "localhost:3000",
+ "hostname": "localhost",
+ "port": "3000",
+ "pathname": "/jqueryui@1.2.3",
+ "search": "",
+ "hash": ""
+ },
+ "# tab/LF/CR",
+ {
+ "input": "h\tt\nt\rp://h\to\ns\rt:9\t0\n0\r0/p\ta\nt\rh?q\tu\ne\rry#f\tr\na\rg",
+ "base": null,
+ "href": "http://host:9000/path?query#frag",
+ "origin": "http://host:9000",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "host:9000",
+ "hostname": "host",
+ "port": "9000",
+ "pathname": "/path",
+ "search": "?query",
+ "hash": "#frag"
+ },
+ "# Stringification of URL.searchParams",
+ {
+ "input": "?a=b&c=d",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/bar?a=b&c=d",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "?a=b&c=d",
+ "searchParams": "a=b&c=d",
+ "hash": ""
+ },
+ {
+ "input": "??a=b&c=d",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/bar??a=b&c=d",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "??a=b&c=d",
+ "searchParams": "%3Fa=b&c=d",
+ "hash": ""
+ },
+ "# Scheme only",
+ {
+ "input": "http:",
+ "base": "http://example.org/foo/bar",
+ "href": "http://example.org/foo/bar",
+ "origin": "http://example.org",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "",
+ "searchParams": "",
+ "hash": ""
+ },
+ {
+ "input": "http:",
+ "base": "https://example.org/foo/bar",
+ "failure": true
+ },
+ {
+ "input": "sc:",
+ "base": "https://example.org/foo/bar",
+ "href": "sc:",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "",
+ "search": "",
+ "searchParams": "",
+ "hash": ""
+ },
+ "# Percent encoding of fragments",
+ {
+ "input": "http://foo.bar/baz?qux#foo\bbar",
+ "base": null,
+ "href": "http://foo.bar/baz?qux#foo%08bar",
+ "origin": "http://foo.bar",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "foo.bar",
+ "hostname": "foo.bar",
+ "port": "",
+ "pathname": "/baz",
+ "search": "?qux",
+ "searchParams": "qux=",
+ "hash": "#foo%08bar"
+ },
+ {
+ "input": "http://foo.bar/baz?qux#foo\"bar",
+ "base": null,
+ "href": "http://foo.bar/baz?qux#foo%22bar",
+ "origin": "http://foo.bar",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "foo.bar",
+ "hostname": "foo.bar",
+ "port": "",
+ "pathname": "/baz",
+ "search": "?qux",
+ "searchParams": "qux=",
+ "hash": "#foo%22bar"
+ },
+ {
+ "input": "http://foo.bar/baz?qux#foo<bar",
+ "base": null,
+ "href": "http://foo.bar/baz?qux#foo%3Cbar",
+ "origin": "http://foo.bar",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "foo.bar",
+ "hostname": "foo.bar",
+ "port": "",
+ "pathname": "/baz",
+ "search": "?qux",
+ "searchParams": "qux=",
+ "hash": "#foo%3Cbar"
+ },
+ {
+ "input": "http://foo.bar/baz?qux#foo>bar",
+ "base": null,
+ "href": "http://foo.bar/baz?qux#foo%3Ebar",
+ "origin": "http://foo.bar",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "foo.bar",
+ "hostname": "foo.bar",
+ "port": "",
+ "pathname": "/baz",
+ "search": "?qux",
+ "searchParams": "qux=",
+ "hash": "#foo%3Ebar"
+ },
+ {
+ "input": "http://foo.bar/baz?qux#foo`bar",
+ "base": null,
+ "href": "http://foo.bar/baz?qux#foo%60bar",
+ "origin": "http://foo.bar",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "foo.bar",
+ "hostname": "foo.bar",
+ "port": "",
+ "pathname": "/baz",
+ "search": "?qux",
+ "searchParams": "qux=",
+ "hash": "#foo%60bar"
+ },
+ "# IPv4 parsing (via https://github.com/nodejs/node/pull/10317)",
+ {
+ "input": "http://1.2.3.4/",
+ "base": "http://other.com/",
+ "href": "http://1.2.3.4/",
+ "origin": "http://1.2.3.4",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "1.2.3.4",
+ "hostname": "1.2.3.4",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://1.2.3.4./",
+ "base": "http://other.com/",
+ "href": "http://1.2.3.4/",
+ "origin": "http://1.2.3.4",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "1.2.3.4",
+ "hostname": "1.2.3.4",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://192.168.257",
+ "base": "http://other.com/",
+ "href": "http://192.168.1.1/",
+ "origin": "http://192.168.1.1",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "192.168.1.1",
+ "hostname": "192.168.1.1",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://192.168.257.",
+ "base": "http://other.com/",
+ "href": "http://192.168.1.1/",
+ "origin": "http://192.168.1.1",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "192.168.1.1",
+ "hostname": "192.168.1.1",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://192.168.257.com",
+ "base": "http://other.com/",
+ "href": "http://192.168.257.com/",
+ "origin": "http://192.168.257.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "192.168.257.com",
+ "hostname": "192.168.257.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://256",
+ "base": "http://other.com/",
+ "href": "http://0.0.1.0/",
+ "origin": "http://0.0.1.0",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "0.0.1.0",
+ "hostname": "0.0.1.0",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://256.com",
+ "base": "http://other.com/",
+ "href": "http://256.com/",
+ "origin": "http://256.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "256.com",
+ "hostname": "256.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://999999999",
+ "base": "http://other.com/",
+ "href": "http://59.154.201.255/",
+ "origin": "http://59.154.201.255",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "59.154.201.255",
+ "hostname": "59.154.201.255",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://999999999.",
+ "base": "http://other.com/",
+ "href": "http://59.154.201.255/",
+ "origin": "http://59.154.201.255",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "59.154.201.255",
+ "hostname": "59.154.201.255",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": "",
+ "failure": true
+ },
+ {
+ "input": "http://999999999.com",
+ "base": "http://other.com/",
+ "href": "http://999999999.com/",
+ "origin": "http://999999999.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "999999999.com",
+ "hostname": "999999999.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://10000000000",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://10000000000.com",
+ "base": "http://other.com/",
+ "href": "http://10000000000.com/",
+ "origin": "http://10000000000.com",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "10000000000.com",
+ "hostname": "10000000000.com",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://4294967295",
+ "base": "http://other.com/",
+ "href": "http://255.255.255.255/",
+ "origin": "http://255.255.255.255",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "255.255.255.255",
+ "hostname": "255.255.255.255",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://4294967296",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://0xffffffff",
+ "base": "http://other.com/",
+ "href": "http://255.255.255.255/",
+ "origin": "http://255.255.255.255",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "255.255.255.255",
+ "hostname": "255.255.255.255",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://0xffffffff1",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://256.256.256.256",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ "More IPv4 parsing (via https://github.com/jsdom/whatwg-url/issues/92)",
+ {
+ "input": "https://0x100000000/test",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "https://256.0.0.1/test",
+ "base": null,
+ "failure": true
+ },
+ "# file URLs containing percent-encoded Windows drive letters (shouldn't work)",
+ {
+ "input": "file:///C%3A/",
+ "base": null,
+ "href": "file:///C%3A/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C%3A/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:///C%7C/",
+ "base": null,
+ "href": "file:///C%7C/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C%7C/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://%43%3A",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "file://%43%7C",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "file://%43|",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "file://C%7C",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "file://%43%7C/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "https://%43%7C/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "asdf://%43|/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "asdf://%43%7C/",
+ "base": null,
+ "href": "asdf://%43%7C/",
+ "origin": "null",
+ "protocol": "asdf:",
+ "username": "",
+ "password": "",
+ "host": "%43%7C",
+ "hostname": "%43%7C",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "# file URLs relative to other file URLs (via https://github.com/jsdom/whatwg-url/pull/60)",
+ {
+ "input": "pix/submit.gif",
+ "base": "file:///C:/Users/Domenic/Dropbox/GitHub/tmpvar/jsdom/test/level2/html/files/anchor.html",
+ "href": "file:///C:/Users/Domenic/Dropbox/GitHub/tmpvar/jsdom/test/level2/html/files/pix/submit.gif",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/Users/Domenic/Dropbox/GitHub/tmpvar/jsdom/test/level2/html/files/pix/submit.gif",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "..",
+ "base": "file:///C:/",
+ "href": "file:///C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "..",
+ "base": "file:///",
+ "href": "file:///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "# More file URL tests by zcorpan and annevk",
+ {
+ "input": "/",
+ "base": "file:///C:/a/b",
+ "href": "file:///C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/",
+ "base": "file://h/C:/a/b",
+ "href": "file://h/C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "h",
+ "hostname": "h",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/",
+ "base": "file://h/a/b",
+ "href": "file://h/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "h",
+ "hostname": "h",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "//d:",
+ "base": "file:///C:/a/b",
+ "href": "file:///d:",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/d:",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "//d:/..",
+ "base": "file:///C:/a/b",
+ "href": "file:///d:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/d:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "..",
+ "base": "file:///ab:/",
+ "href": "file:///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "..",
+ "base": "file:///1:/",
+ "href": "file:///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "",
+ "base": "file:///test?test#test",
+ "href": "file:///test?test",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "?test",
+ "hash": ""
+ },
+ {
+ "input": "file:",
+ "base": "file:///test?test#test",
+ "href": "file:///test?test",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "?test",
+ "hash": ""
+ },
+ {
+ "input": "?x",
+ "base": "file:///test?test#test",
+ "href": "file:///test?x",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "?x",
+ "hash": ""
+ },
+ {
+ "input": "file:?x",
+ "base": "file:///test?test#test",
+ "href": "file:///test?x",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "?x",
+ "hash": ""
+ },
+ {
+ "input": "#x",
+ "base": "file:///test?test#test",
+ "href": "file:///test?test#x",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "?test",
+ "hash": "#x"
+ },
+ {
+ "input": "file:#x",
+ "base": "file:///test?test#test",
+ "href": "file:///test?test#x",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "?test",
+ "hash": "#x"
+ },
+ "# File URLs and many (back)slashes",
+ {
+ "input": "file:\\\\//",
+ "base": null,
+ "href": "file:////",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:\\\\\\\\",
+ "base": null,
+ "href": "file:////",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:\\\\\\\\?fox",
+ "base": null,
+ "href": "file:////?fox",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//",
+ "search": "?fox",
+ "hash": ""
+ },
+ {
+ "input": "file:\\\\\\\\#guppy",
+ "base": null,
+ "href": "file:////#guppy",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//",
+ "search": "",
+ "hash": "#guppy"
+ },
+ {
+ "input": "file://spider///",
+ "base": null,
+ "href": "file://spider///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "spider",
+ "hostname": "spider",
+ "port": "",
+ "pathname": "///",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:\\\\localhost//",
+ "base": null,
+ "href": "file:////",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:///localhost//cat",
+ "base": null,
+ "href": "file:///localhost//cat",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/localhost//cat",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://\\/localhost//cat",
+ "base": null,
+ "href": "file:////localhost//cat",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//localhost//cat",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://localhost//a//../..//",
+ "base": null,
+ "href": "file://///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "///",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/////mouse",
+ "base": "file:///elephant",
+ "href": "file://///mouse",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "///mouse",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "\\//pig",
+ "base": "file://lion/",
+ "href": "file:///pig",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/pig",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "\\/localhost//pig",
+ "base": "file://lion/",
+ "href": "file:////pig",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//pig",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "//localhost//pig",
+ "base": "file://lion/",
+ "href": "file:////pig",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//pig",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/..//localhost//pig",
+ "base": "file://lion/",
+ "href": "file://lion//localhost//pig",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "lion",
+ "hostname": "lion",
+ "port": "",
+ "pathname": "//localhost//pig",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://",
+ "base": "file://ape/",
+ "href": "file:///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "# File URLs with non-empty hosts",
+ {
+ "input": "/rooibos",
+ "base": "file://tea/",
+ "href": "file://tea/rooibos",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "tea",
+ "hostname": "tea",
+ "port": "",
+ "pathname": "/rooibos",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/?chai",
+ "base": "file://tea/",
+ "href": "file://tea/?chai",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "tea",
+ "hostname": "tea",
+ "port": "",
+ "pathname": "/",
+ "search": "?chai",
+ "hash": ""
+ },
+ "# Windows drive letter handling with the 'file:' base URL",
+ {
+ "input": "C|",
+ "base": "file://host/dir/file",
+ "href": "file://host/C:",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "host",
+ "hostname": "host",
+ "port": "",
+ "pathname": "/C:",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "C|",
+ "base": "file://host/D:/dir1/dir2/file",
+ "href": "file://host/C:",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "host",
+ "hostname": "host",
+ "port": "",
+ "pathname": "/C:",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "C|#",
+ "base": "file://host/dir/file",
+ "href": "file://host/C:#",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "host",
+ "hostname": "host",
+ "port": "",
+ "pathname": "/C:",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "C|?",
+ "base": "file://host/dir/file",
+ "href": "file://host/C:?",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "host",
+ "hostname": "host",
+ "port": "",
+ "pathname": "/C:",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "C|/",
+ "base": "file://host/dir/file",
+ "href": "file://host/C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "host",
+ "hostname": "host",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "C|\n/",
+ "base": "file://host/dir/file",
+ "href": "file://host/C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "host",
+ "hostname": "host",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "C|\\",
+ "base": "file://host/dir/file",
+ "href": "file://host/C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "host",
+ "hostname": "host",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "C",
+ "base": "file://host/dir/file",
+ "href": "file://host/dir/C",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "host",
+ "hostname": "host",
+ "port": "",
+ "pathname": "/dir/C",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "C|a",
+ "base": "file://host/dir/file",
+ "href": "file://host/dir/C|a",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "host",
+ "hostname": "host",
+ "port": "",
+ "pathname": "/dir/C|a",
+ "search": "",
+ "hash": ""
+ },
+ "# Windows drive letter quirk in the file slash state",
+ {
+ "input": "/c:/foo/bar",
+ "base": "file:///c:/baz/qux",
+ "href": "file:///c:/foo/bar",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/c:/foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/c|/foo/bar",
+ "base": "file:///c:/baz/qux",
+ "href": "file:///c:/foo/bar",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/c:/foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:\\c:\\foo\\bar",
+ "base": "file:///c:/baz/qux",
+ "href": "file:///c:/foo/bar",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/c:/foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/c:/foo/bar",
+ "base": "file://host/path",
+ "href": "file://host/c:/foo/bar",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "host",
+ "hostname": "host",
+ "port": "",
+ "pathname": "/c:/foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ "# Do not drop the host in the presence of a drive letter",
+ {
+ "input": "file://example.net/C:/",
+ "base": null,
+ "href": "file://example.net/C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "example.net",
+ "hostname": "example.net",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://1.2.3.4/C:/",
+ "base": null,
+ "href": "file://1.2.3.4/C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "1.2.3.4",
+ "hostname": "1.2.3.4",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://[1::8]/C:/",
+ "base": null,
+ "href": "file://[1::8]/C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "[1::8]",
+ "hostname": "[1::8]",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ "# Copy the host from the base URL in the following cases",
+ {
+ "input": "C|/",
+ "base": "file://host/",
+ "href": "file://host/C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "host",
+ "hostname": "host",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/C:/",
+ "base": "file://host/",
+ "href": "file://host/C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "host",
+ "hostname": "host",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:C:/",
+ "base": "file://host/",
+ "href": "file://host/C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "host",
+ "hostname": "host",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:/C:/",
+ "base": "file://host/",
+ "href": "file://host/C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "host",
+ "hostname": "host",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ "# Copy the empty host from the input in the following cases",
+ {
+ "input": "//C:/",
+ "base": "file://host/",
+ "href": "file:///C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://C:/",
+ "base": "file://host/",
+ "href": "file:///C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "///C:/",
+ "base": "file://host/",
+ "href": "file:///C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:///C:/",
+ "base": "file://host/",
+ "href": "file:///C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ "# Windows drive letter quirk (no host)",
+ {
+ "input": "file:/C|/",
+ "base": null,
+ "href": "file:///C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://C|/",
+ "base": null,
+ "href": "file:///C:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/C:/",
+ "search": "",
+ "hash": ""
+ },
+ "# file URLs without base URL by Rimas Misevičius",
+ {
+ "input": "file:",
+ "base": null,
+ "href": "file:///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:?q=v",
+ "base": null,
+ "href": "file:///?q=v",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "?q=v",
+ "hash": ""
+ },
+ {
+ "input": "file:#frag",
+ "base": null,
+ "href": "file:///#frag",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": "#frag"
+ },
+ "# file: drive letter cases from https://crbug.com/1078698",
+ {
+ "input": "file:///Y:",
+ "base": null,
+ "href": "file:///Y:",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/Y:",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:///Y:/",
+ "base": null,
+ "href": "file:///Y:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/Y:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:///./Y",
+ "base": null,
+ "href": "file:///Y",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/Y",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:///./Y:",
+ "base": null,
+ "href": "file:///Y:",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/Y:",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "\\\\\\.\\Y:",
+ "base": null,
+ "failure": true,
+ "relativeTo": "non-opaque-path-base"
+ },
+ "# file: drive letter cases from https://crbug.com/1078698 but lowercased",
+ {
+ "input": "file:///y:",
+ "base": null,
+ "href": "file:///y:",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/y:",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:///y:/",
+ "base": null,
+ "href": "file:///y:/",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/y:/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:///./y",
+ "base": null,
+ "href": "file:///y",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/y",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:///./y:",
+ "base": null,
+ "href": "file:///y:",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/y:",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "\\\\\\.\\y:",
+ "base": null,
+ "failure": true,
+ "relativeTo": "non-opaque-path-base"
+ },
+ "# Additional file URL tests for (https://github.com/whatwg/url/issues/405)",
+ {
+ "input": "file://localhost//a//../..//foo",
+ "base": null,
+ "href": "file://///foo",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "///foo",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://localhost////foo",
+ "base": null,
+ "href": "file://////foo",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "////foo",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:////foo",
+ "base": null,
+ "href": "file:////foo",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//foo",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:///one/two",
+ "base": "file:///",
+ "href": "file:///one/two",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/one/two",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:////one/two",
+ "base": "file:///",
+ "href": "file:////one/two",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//one/two",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "//one/two",
+ "base": "file:///",
+ "href": "file://one/two",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "one",
+ "hostname": "one",
+ "port": "",
+ "pathname": "/two",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "///one/two",
+ "base": "file:///",
+ "href": "file:///one/two",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/one/two",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "////one/two",
+ "base": "file:///",
+ "href": "file:////one/two",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//one/two",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:///.//",
+ "base": "file:////",
+ "href": "file:////",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//",
+ "search": "",
+ "hash": ""
+ },
+ "File URL tests for https://github.com/whatwg/url/issues/549",
+ {
+ "input": "file:.//p",
+ "base": null,
+ "href": "file:////p",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//p",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:/.//p",
+ "base": null,
+ "href": "file:////p",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//p",
+ "search": "",
+ "hash": ""
+ },
+ "# IPv6 tests",
+ {
+ "input": "http://[1:0::]",
+ "base": "http://example.net/",
+ "href": "http://[1::]/",
+ "origin": "http://[1::]",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "[1::]",
+ "hostname": "[1::]",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://[0:1:2:3:4:5:6:7:8]",
+ "base": "http://example.net/",
+ "failure": true
+ },
+ {
+ "input": "https://[0::0::0]",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "https://[0:.0]",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "https://[0:0:]",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "https://[0:1:2:3:4:5:6:7.0.0.0.1]",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "https://[0:1.00.0.0.0]",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "https://[0:1.290.0.0.0]",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "https://[0:1.23.23]",
+ "base": null,
+ "failure": true
+ },
+ "# Empty host",
+ {
+ "input": "http://?",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://#",
+ "base": null,
+ "failure": true
+ },
+ "Port overflow (2^32 + 81)",
+ {
+ "input": "http://f:4294967377/c",
+ "base": "http://example.org/",
+ "failure": true
+ },
+ "Port overflow (2^64 + 81)",
+ {
+ "input": "http://f:18446744073709551697/c",
+ "base": "http://example.org/",
+ "failure": true
+ },
+ "Port overflow (2^128 + 81)",
+ {
+ "input": "http://f:340282366920938463463374607431768211537/c",
+ "base": "http://example.org/",
+ "failure": true
+ },
+ "# Non-special-URL path tests",
+ {
+ "input": "sc://ñ",
+ "base": null,
+ "href": "sc://%C3%B1",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "%C3%B1",
+ "hostname": "%C3%B1",
+ "port": "",
+ "pathname": "",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "sc://ñ?x",
+ "base": null,
+ "href": "sc://%C3%B1?x",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "%C3%B1",
+ "hostname": "%C3%B1",
+ "port": "",
+ "pathname": "",
+ "search": "?x",
+ "hash": ""
+ },
+ {
+ "input": "sc://ñ#x",
+ "base": null,
+ "href": "sc://%C3%B1#x",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "%C3%B1",
+ "hostname": "%C3%B1",
+ "port": "",
+ "pathname": "",
+ "search": "",
+ "hash": "#x"
+ },
+ {
+ "input": "#x",
+ "base": "sc://ñ",
+ "href": "sc://%C3%B1#x",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "%C3%B1",
+ "hostname": "%C3%B1",
+ "port": "",
+ "pathname": "",
+ "search": "",
+ "hash": "#x"
+ },
+ {
+ "input": "?x",
+ "base": "sc://ñ",
+ "href": "sc://%C3%B1?x",
+ "origin": "null",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "%C3%B1",
+ "hostname": "%C3%B1",
+ "port": "",
+ "pathname": "",
+ "search": "?x",
+ "hash": ""
+ },
+ {
+ "input": "sc://?",
+ "base": null,
+ "href": "sc://?",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "sc://#",
+ "base": null,
+ "href": "sc://#",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "///",
+ "base": "sc://x/",
+ "href": "sc:///",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "////",
+ "base": "sc://x/",
+ "href": "sc:////",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "////x/",
+ "base": "sc://x/",
+ "href": "sc:////x/",
+ "protocol": "sc:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//x/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "tftp://foobar.com/someconfig;mode=netascii",
+ "base": null,
+ "href": "tftp://foobar.com/someconfig;mode=netascii",
+ "origin": "null",
+ "protocol": "tftp:",
+ "username": "",
+ "password": "",
+ "host": "foobar.com",
+ "hostname": "foobar.com",
+ "port": "",
+ "pathname": "/someconfig;mode=netascii",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "telnet://user:pass@foobar.com:23/",
+ "base": null,
+ "href": "telnet://user:pass@foobar.com:23/",
+ "origin": "null",
+ "protocol": "telnet:",
+ "username": "user",
+ "password": "pass",
+ "host": "foobar.com:23",
+ "hostname": "foobar.com",
+ "port": "23",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "ut2004://10.10.10.10:7777/Index.ut2",
+ "base": null,
+ "href": "ut2004://10.10.10.10:7777/Index.ut2",
+ "origin": "null",
+ "protocol": "ut2004:",
+ "username": "",
+ "password": "",
+ "host": "10.10.10.10:7777",
+ "hostname": "10.10.10.10",
+ "port": "7777",
+ "pathname": "/Index.ut2",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "redis://foo:bar@somehost:6379/0?baz=bam&qux=baz",
+ "base": null,
+ "href": "redis://foo:bar@somehost:6379/0?baz=bam&qux=baz",
+ "origin": "null",
+ "protocol": "redis:",
+ "username": "foo",
+ "password": "bar",
+ "host": "somehost:6379",
+ "hostname": "somehost",
+ "port": "6379",
+ "pathname": "/0",
+ "search": "?baz=bam&qux=baz",
+ "hash": ""
+ },
+ {
+ "input": "rsync://foo@host:911/sup",
+ "base": null,
+ "href": "rsync://foo@host:911/sup",
+ "origin": "null",
+ "protocol": "rsync:",
+ "username": "foo",
+ "password": "",
+ "host": "host:911",
+ "hostname": "host",
+ "port": "911",
+ "pathname": "/sup",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "git://github.com/foo/bar.git",
+ "base": null,
+ "href": "git://github.com/foo/bar.git",
+ "origin": "null",
+ "protocol": "git:",
+ "username": "",
+ "password": "",
+ "host": "github.com",
+ "hostname": "github.com",
+ "port": "",
+ "pathname": "/foo/bar.git",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "irc://myserver.com:6999/channel?passwd",
+ "base": null,
+ "href": "irc://myserver.com:6999/channel?passwd",
+ "origin": "null",
+ "protocol": "irc:",
+ "username": "",
+ "password": "",
+ "host": "myserver.com:6999",
+ "hostname": "myserver.com",
+ "port": "6999",
+ "pathname": "/channel",
+ "search": "?passwd",
+ "hash": ""
+ },
+ {
+ "input": "dns://fw.example.org:9999/foo.bar.org?type=TXT",
+ "base": null,
+ "href": "dns://fw.example.org:9999/foo.bar.org?type=TXT",
+ "origin": "null",
+ "protocol": "dns:",
+ "username": "",
+ "password": "",
+ "host": "fw.example.org:9999",
+ "hostname": "fw.example.org",
+ "port": "9999",
+ "pathname": "/foo.bar.org",
+ "search": "?type=TXT",
+ "hash": ""
+ },
+ {
+ "input": "ldap://localhost:389/ou=People,o=JNDITutorial",
+ "base": null,
+ "href": "ldap://localhost:389/ou=People,o=JNDITutorial",
+ "origin": "null",
+ "protocol": "ldap:",
+ "username": "",
+ "password": "",
+ "host": "localhost:389",
+ "hostname": "localhost",
+ "port": "389",
+ "pathname": "/ou=People,o=JNDITutorial",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "git+https://github.com/foo/bar",
+ "base": null,
+ "href": "git+https://github.com/foo/bar",
+ "origin": "null",
+ "protocol": "git+https:",
+ "username": "",
+ "password": "",
+ "host": "github.com",
+ "hostname": "github.com",
+ "port": "",
+ "pathname": "/foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "urn:ietf:rfc:2648",
+ "base": null,
+ "href": "urn:ietf:rfc:2648",
+ "origin": "null",
+ "protocol": "urn:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "ietf:rfc:2648",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "tag:joe@example.org,2001:foo/bar",
+ "base": null,
+ "href": "tag:joe@example.org,2001:foo/bar",
+ "origin": "null",
+ "protocol": "tag:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "joe@example.org,2001:foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ "Serialize /. in path",
+ {
+ "input": "non-spec:/.//",
+ "base": null,
+ "href": "non-spec:/.//",
+ "protocol": "non-spec:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "non-spec:/..//",
+ "base": null,
+ "href": "non-spec:/.//",
+ "protocol": "non-spec:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "non-spec:/a/..//",
+ "base": null,
+ "href": "non-spec:/.//",
+ "protocol": "non-spec:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "non-spec:/.//path",
+ "base": null,
+ "href": "non-spec:/.//path",
+ "protocol": "non-spec:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//path",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "non-spec:/..//path",
+ "base": null,
+ "href": "non-spec:/.//path",
+ "protocol": "non-spec:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//path",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "non-spec:/a/..//path",
+ "base": null,
+ "href": "non-spec:/.//path",
+ "protocol": "non-spec:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//path",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/.//path",
+ "base": "non-spec:/p",
+ "href": "non-spec:/.//path",
+ "protocol": "non-spec:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//path",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "/..//path",
+ "base": "non-spec:/p",
+ "href": "non-spec:/.//path",
+ "protocol": "non-spec:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//path",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "..//path",
+ "base": "non-spec:/p",
+ "href": "non-spec:/.//path",
+ "protocol": "non-spec:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//path",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "a/..//path",
+ "base": "non-spec:/p",
+ "href": "non-spec:/.//path",
+ "protocol": "non-spec:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//path",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "",
+ "base": "non-spec:/..//p",
+ "href": "non-spec:/.//p",
+ "protocol": "non-spec:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//p",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "path",
+ "base": "non-spec:/..//p",
+ "href": "non-spec:/.//path",
+ "protocol": "non-spec:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "//path",
+ "search": "",
+ "hash": ""
+ },
+ "Do not serialize /. in path",
+ {
+ "input": "../path",
+ "base": "non-spec:/.//p",
+ "href": "non-spec:/path",
+ "protocol": "non-spec:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/path",
+ "search": "",
+ "hash": ""
+ },
+ "# percent encoded hosts in non-special-URLs",
+ {
+ "input": "non-special://%E2%80%A0/",
+ "base": null,
+ "href": "non-special://%E2%80%A0/",
+ "protocol": "non-special:",
+ "username": "",
+ "password": "",
+ "host": "%E2%80%A0",
+ "hostname": "%E2%80%A0",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "non-special://H%4fSt/path",
+ "base": null,
+ "href": "non-special://H%4fSt/path",
+ "protocol": "non-special:",
+ "username": "",
+ "password": "",
+ "host": "H%4fSt",
+ "hostname": "H%4fSt",
+ "port": "",
+ "pathname": "/path",
+ "search": "",
+ "hash": ""
+ },
+ "# IPv6 in non-special-URLs",
+ {
+ "input": "non-special://[1:2:0:0:5:0:0:0]/",
+ "base": null,
+ "href": "non-special://[1:2:0:0:5::]/",
+ "protocol": "non-special:",
+ "username": "",
+ "password": "",
+ "host": "[1:2:0:0:5::]",
+ "hostname": "[1:2:0:0:5::]",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "non-special://[1:2:0:0:0:0:0:3]/",
+ "base": null,
+ "href": "non-special://[1:2::3]/",
+ "protocol": "non-special:",
+ "username": "",
+ "password": "",
+ "host": "[1:2::3]",
+ "hostname": "[1:2::3]",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "non-special://[1:2::3]:80/",
+ "base": null,
+ "href": "non-special://[1:2::3]:80/",
+ "protocol": "non-special:",
+ "username": "",
+ "password": "",
+ "host": "[1:2::3]:80",
+ "hostname": "[1:2::3]",
+ "port": "80",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "non-special://[:80/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "blob:d3958f5c-0777-0845-9dcf-2cb28783acaf",
+ "base": null,
+ "href": "blob:d3958f5c-0777-0845-9dcf-2cb28783acaf",
+ "origin": "null",
+ "protocol": "blob:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "d3958f5c-0777-0845-9dcf-2cb28783acaf",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "blob:",
+ "base": null,
+ "href": "blob:",
+ "origin": "null",
+ "protocol": "blob:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "",
+ "search": "",
+ "hash": ""
+ },
+ "Invalid IPv4 radix digits",
+ {
+ "input": "http://0x7f.0.0.0x7g",
+ "base": null,
+ "href": "http://0x7f.0.0.0x7g/",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "0x7f.0.0.0x7g",
+ "hostname": "0x7f.0.0.0x7g",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://0X7F.0.0.0X7G",
+ "base": null,
+ "href": "http://0x7f.0.0.0x7g/",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "0x7f.0.0.0x7g",
+ "hostname": "0x7f.0.0.0x7g",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "Invalid IPv4 portion of IPv6 address",
+ {
+ "input": "http://[::127.0.0.0.1]",
+ "base": null,
+ "failure": true
+ },
+ "Uncompressed IPv6 addresses with 0",
+ {
+ "input": "http://[0:1:0:1:0:1:0:1]",
+ "base": null,
+ "href": "http://[0:1:0:1:0:1:0:1]/",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "[0:1:0:1:0:1:0:1]",
+ "hostname": "[0:1:0:1:0:1:0:1]",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://[1:0:1:0:1:0:1:0]",
+ "base": null,
+ "href": "http://[1:0:1:0:1:0:1:0]/",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "[1:0:1:0:1:0:1:0]",
+ "hostname": "[1:0:1:0:1:0:1:0]",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ "Percent-encoded query and fragment",
+ {
+ "input": "http://example.org/test?\u0022",
+ "base": null,
+ "href": "http://example.org/test?%22",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/test",
+ "search": "?%22",
+ "hash": ""
+ },
+ {
+ "input": "http://example.org/test?\u0023",
+ "base": null,
+ "href": "http://example.org/test?#",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/test",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "http://example.org/test?\u003C",
+ "base": null,
+ "href": "http://example.org/test?%3C",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/test",
+ "search": "?%3C",
+ "hash": ""
+ },
+ {
+ "input": "http://example.org/test?\u003E",
+ "base": null,
+ "href": "http://example.org/test?%3E",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/test",
+ "search": "?%3E",
+ "hash": ""
+ },
+ {
+ "input": "http://example.org/test?\u2323",
+ "base": null,
+ "href": "http://example.org/test?%E2%8C%A3",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/test",
+ "search": "?%E2%8C%A3",
+ "hash": ""
+ },
+ {
+ "input": "http://example.org/test?%23%23",
+ "base": null,
+ "href": "http://example.org/test?%23%23",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/test",
+ "search": "?%23%23",
+ "hash": ""
+ },
+ {
+ "input": "http://example.org/test?%GH",
+ "base": null,
+ "href": "http://example.org/test?%GH",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/test",
+ "search": "?%GH",
+ "hash": ""
+ },
+ {
+ "input": "http://example.org/test?a#%EF",
+ "base": null,
+ "href": "http://example.org/test?a#%EF",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/test",
+ "search": "?a",
+ "hash": "#%EF"
+ },
+ {
+ "input": "http://example.org/test?a#%GH",
+ "base": null,
+ "href": "http://example.org/test?a#%GH",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/test",
+ "search": "?a",
+ "hash": "#%GH"
+ },
+ "URLs that require a non-about:blank base. (Also serve as invalid base tests.)",
+ {
+ "input": "a",
+ "base": null,
+ "failure": true,
+ "relativeTo": "non-opaque-path-base"
+ },
+ {
+ "input": "a/",
+ "base": null,
+ "failure": true,
+ "relativeTo": "non-opaque-path-base"
+ },
+ {
+ "input": "a//",
+ "base": null,
+ "failure": true,
+ "relativeTo": "non-opaque-path-base"
+ },
+ "Bases that don't fail to parse but fail to be bases",
+ {
+ "input": "test-a-colon.html",
+ "base": "a:",
+ "failure": true
+ },
+ {
+ "input": "test-a-colon-b.html",
+ "base": "a:b",
+ "failure": true
+ },
+ "Other base URL tests, that must succeed",
+ {
+ "input": "test-a-colon-slash.html",
+ "base": "a:/",
+ "href": "a:/test-a-colon-slash.html",
+ "protocol": "a:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test-a-colon-slash.html",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "test-a-colon-slash-slash.html",
+ "base": "a://",
+ "href": "a:///test-a-colon-slash-slash.html",
+ "protocol": "a:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test-a-colon-slash-slash.html",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "test-a-colon-slash-b.html",
+ "base": "a:/b",
+ "href": "a:/test-a-colon-slash-b.html",
+ "protocol": "a:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test-a-colon-slash-b.html",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "test-a-colon-slash-slash-b.html",
+ "base": "a://b",
+ "href": "a://b/test-a-colon-slash-slash-b.html",
+ "protocol": "a:",
+ "username": "",
+ "password": "",
+ "host": "b",
+ "hostname": "b",
+ "port": "",
+ "pathname": "/test-a-colon-slash-slash-b.html",
+ "search": "",
+ "hash": ""
+ },
+ "Null code point in fragment",
+ {
+ "input": "http://example.org/test?a#b\u0000c",
+ "base": null,
+ "href": "http://example.org/test?a#b%00c",
+ "protocol": "http:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/test",
+ "search": "?a",
+ "hash": "#b%00c"
+ },
+ {
+ "input": "non-spec://example.org/test?a#b\u0000c",
+ "base": null,
+ "href": "non-spec://example.org/test?a#b%00c",
+ "protocol": "non-spec:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/test",
+ "search": "?a",
+ "hash": "#b%00c"
+ },
+ {
+ "input": "non-spec:/test?a#b\u0000c",
+ "base": null,
+ "href": "non-spec:/test?a#b%00c",
+ "protocol": "non-spec:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "?a",
+ "hash": "#b%00c"
+ },
+ "First scheme char - not allowed: https://github.com/whatwg/url/issues/464",
+ {
+ "input": "10.0.0.7:8080/foo.html",
+ "base": "file:///some/dir/bar.html",
+ "href": "file:///some/dir/10.0.0.7:8080/foo.html",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/some/dir/10.0.0.7:8080/foo.html",
+ "search": "",
+ "hash": ""
+ },
+ "Subsequent scheme chars - not allowed",
+ {
+ "input": "a!@$*=/foo.html",
+ "base": "file:///some/dir/bar.html",
+ "href": "file:///some/dir/a!@$*=/foo.html",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/some/dir/a!@$*=/foo.html",
+ "search": "",
+ "hash": ""
+ },
+ "First and subsequent scheme chars - allowed",
+ {
+ "input": "a1234567890-+.:foo/bar",
+ "base": "http://example.com/dir/file",
+ "href": "a1234567890-+.:foo/bar",
+ "protocol": "a1234567890-+.:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "foo/bar",
+ "search": "",
+ "hash": ""
+ },
+ "IDNA ignored code points in file URLs hosts",
+ {
+ "input": "file://a\u00ADb/p",
+ "base": null,
+ "href": "file://ab/p",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "ab",
+ "hostname": "ab",
+ "port": "",
+ "pathname": "/p",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file://a%C2%ADb/p",
+ "base": null,
+ "href": "file://ab/p",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "ab",
+ "hostname": "ab",
+ "port": "",
+ "pathname": "/p",
+ "search": "",
+ "hash": ""
+ },
+ "IDNA hostnames which get mapped to 'localhost'",
+ {
+ "input": "file://loC𝐀𝐋𝐇𝐨𝐬𝐭/usr/bin",
+ "base": null,
+ "href": "file:///usr/bin",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/usr/bin",
+ "search": "",
+ "hash": ""
+ },
+ "Empty host after the domain to ASCII",
+ {
+ "input": "file://\u00ad/p",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "file://%C2%AD/p",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "file://xn--/p",
+ "base": null,
+ "failure": true
+ },
+ "https://bugzilla.mozilla.org/show_bug.cgi?id=1647058",
+ {
+ "input": "#link",
+ "base": "https://example.org/##link",
+ "href": "https://example.org/#link",
+ "protocol": "https:",
+ "username": "",
+ "password": "",
+ "host": "example.org",
+ "hostname": "example.org",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": "#link"
+ },
+ "UTF-8 percent-encode of C0 control percent-encode set and supersets",
+ {
+ "input": "non-special:cannot-be-a-base-url-\u0000\u0001\u001F\u001E\u007E\u007F\u0080",
+ "base": null,
+ "hash": "",
+ "host": "",
+ "hostname": "",
+ "href": "non-special:cannot-be-a-base-url-%00%01%1F%1E~%7F%C2%80",
+ "origin": "null",
+ "password": "",
+ "pathname": "cannot-be-a-base-url-%00%01%1F%1E~%7F%C2%80",
+ "port": "",
+ "protocol": "non-special:",
+ "search": "",
+ "username": ""
+ },
+ {
+ "input": "https://www.example.com/path{\u007Fpath.html?query'\u007F=query#fragment<\u007Ffragment",
+ "base": null,
+ "hash": "#fragment%3C%7Ffragment",
+ "host": "www.example.com",
+ "hostname": "www.example.com",
+ "href": "https://www.example.com/path%7B%7Fpath.html?query%27%7F=query#fragment%3C%7Ffragment",
+ "origin": "https://www.example.com",
+ "password": "",
+ "pathname": "/path%7B%7Fpath.html",
+ "port": "",
+ "protocol": "https:",
+ "search": "?query%27%7F=query",
+ "username": ""
+ },
+ {
+ "input": "https://user:pass[\u007F@foo/bar",
+ "base": "http://example.org",
+ "hash": "",
+ "host": "foo",
+ "hostname": "foo",
+ "href": "https://user:pass%5B%7F@foo/bar",
+ "origin": "https://foo",
+ "password": "pass%5B%7F",
+ "pathname": "/bar",
+ "port": "",
+ "protocol": "https:",
+ "search": "",
+ "username": "user"
+ },
+ "Tests for the distinct percent-encode sets",
+ {
+ "input": "foo:// !\"$%&'()*+,-.;<=>@[\\]^_`{|}~@host/",
+ "base": null,
+ "hash": "",
+ "host": "host",
+ "hostname": "host",
+ "href": "foo://%20!%22$%&'()*+,-.%3B%3C%3D%3E%40%5B%5C%5D%5E_%60%7B%7C%7D~@host/",
+ "origin": "null",
+ "password": "",
+ "pathname": "/",
+ "port": "",
+ "protocol": "foo:",
+ "search": "",
+ "username": "%20!%22$%&'()*+,-.%3B%3C%3D%3E%40%5B%5C%5D%5E_%60%7B%7C%7D~"
+ },
+ {
+ "input": "foo://joe: !\"$%&'()*+,-.:;<=>@[\\]^_`{|}~@host/",
+ "base": null,
+ "hash": "",
+ "host": "host",
+ "hostname": "host",
+ "href": "foo://joe:%20!%22$%&'()*+,-.%3A%3B%3C%3D%3E%40%5B%5C%5D%5E_%60%7B%7C%7D~@host/",
+ "origin": "null",
+ "password": "%20!%22$%&'()*+,-.%3A%3B%3C%3D%3E%40%5B%5C%5D%5E_%60%7B%7C%7D~",
+ "pathname": "/",
+ "port": "",
+ "protocol": "foo:",
+ "search": "",
+ "username": "joe"
+ },
+ {
+ "input": "foo://!\"$%&'()*+,-.;=_`{}~/",
+ "base": null,
+ "hash": "",
+ "host": "!\"$%&'()*+,-.;=_`{}~",
+ "hostname": "!\"$%&'()*+,-.;=_`{}~",
+ "href": "foo://!\"$%&'()*+,-.;=_`{}~/",
+ "origin": "null",
+ "password": "",
+ "pathname": "/",
+ "port": "",
+ "protocol": "foo:",
+ "search": "",
+ "username": ""
+ },
+ {
+ "input": "foo://host/ !\"$%&'()*+,-./:;<=>@[\\]^_`{|}~",
+ "base": null,
+ "hash": "",
+ "host": "host",
+ "hostname": "host",
+ "href": "foo://host/%20!%22$%&'()*+,-./:;%3C=%3E@[\\]^_%60%7B|%7D~",
+ "origin": "null",
+ "password": "",
+ "pathname": "/%20!%22$%&'()*+,-./:;%3C=%3E@[\\]^_%60%7B|%7D~",
+ "port": "",
+ "protocol": "foo:",
+ "search": "",
+ "username": ""
+ },
+ {
+ "input": "foo://host/dir/? !\"$%&'()*+,-./:;<=>?@[\\]^_`{|}~",
+ "base": null,
+ "hash": "",
+ "host": "host",
+ "hostname": "host",
+ "href": "foo://host/dir/?%20!%22$%&'()*+,-./:;%3C=%3E?@[\\]^_`{|}~",
+ "origin": "null",
+ "password": "",
+ "pathname": "/dir/",
+ "port": "",
+ "protocol": "foo:",
+ "search": "?%20!%22$%&'()*+,-./:;%3C=%3E?@[\\]^_`{|}~",
+ "username": ""
+ },
+ {
+ "input": "wss://host/dir/? !\"$%&'()*+,-./:;<=>?@[\\]^_`{|}~",
+ "base": null,
+ "hash": "",
+ "host": "host",
+ "hostname": "host",
+ "href": "wss://host/dir/?%20!%22$%&%27()*+,-./:;%3C=%3E?@[\\]^_`{|}~",
+ "origin": "wss://host",
+ "password": "",
+ "pathname": "/dir/",
+ "port": "",
+ "protocol": "wss:",
+ "search": "?%20!%22$%&%27()*+,-./:;%3C=%3E?@[\\]^_`{|}~",
+ "username": ""
+ },
+ {
+ "input": "foo://host/dir/# !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",
+ "base": null,
+ "hash": "#%20!%22#$%&'()*+,-./:;%3C=%3E?@[\\]^_%60{|}~",
+ "host": "host",
+ "hostname": "host",
+ "href": "foo://host/dir/#%20!%22#$%&'()*+,-./:;%3C=%3E?@[\\]^_%60{|}~",
+ "origin": "null",
+ "password": "",
+ "pathname": "/dir/",
+ "port": "",
+ "protocol": "foo:",
+ "search": "",
+ "username": ""
+ },
+ "Ensure that input schemes are not ignored when resolving non-special URLs",
+ {
+ "input": "abc:rootless",
+ "base": "abc://host/path",
+ "hash": "",
+ "host": "",
+ "hostname": "",
+ "href": "abc:rootless",
+ "password": "",
+ "pathname": "rootless",
+ "port": "",
+ "protocol": "abc:",
+ "search": "",
+ "username": ""
+ },
+ {
+ "input": "abc:rootless",
+ "base": "abc:/path",
+ "hash": "",
+ "host": "",
+ "hostname": "",
+ "href": "abc:rootless",
+ "password": "",
+ "pathname": "rootless",
+ "port": "",
+ "protocol": "abc:",
+ "search": "",
+ "username": ""
+ },
+ {
+ "input": "abc:rootless",
+ "base": "abc:path",
+ "hash": "",
+ "host": "",
+ "hostname": "",
+ "href": "abc:rootless",
+ "password": "",
+ "pathname": "rootless",
+ "port": "",
+ "protocol": "abc:",
+ "search": "",
+ "username": ""
+ },
+ {
+ "input": "abc:/rooted",
+ "base": "abc://host/path",
+ "hash": "",
+ "host": "",
+ "hostname": "",
+ "href": "abc:/rooted",
+ "password": "",
+ "pathname": "/rooted",
+ "port": "",
+ "protocol": "abc:",
+ "search": "",
+ "username": ""
+ },
+ "Empty query and fragment with blank should throw an error",
+ {
+ "input": "#",
+ "base": null,
+ "failure": true,
+ "relativeTo": "any-base"
+ },
+ {
+ "input": "?",
+ "base": null,
+ "failure": true,
+ "relativeTo": "non-opaque-path-base"
+ },
+ "Last component looks like a number, but not valid IPv4",
+ {
+ "input": "http://1.2.3.4.5",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://1.2.3.4.5.",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://0..0x300/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://0..0x300./",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://256.256.256.256.256",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://256.256.256.256.256.",
+ "base": "http://other.com/",
+ "failure": true
+ },
+ {
+ "input": "http://1.2.3.08",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://1.2.3.08.",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://1.2.3.09",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://09.2.3.4",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://09.2.3.4.",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://01.2.3.4.5",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://01.2.3.4.5.",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://0x100.2.3.4",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://0x100.2.3.4.",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://0x1.2.3.4.5",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://0x1.2.3.4.5.",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://foo.1.2.3.4",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://foo.1.2.3.4.",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://foo.2.3.4",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://foo.2.3.4.",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://foo.09",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://foo.09.",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://foo.0x4",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://foo.0x4.",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://foo.09..",
+ "base": null,
+ "hash": "",
+ "host": "foo.09..",
+ "hostname": "foo.09..",
+ "href": "http://foo.09../",
+ "password": "",
+ "pathname": "/",
+ "port": "",
+ "protocol": "http:",
+ "search": "",
+ "username": ""
+ },
+ {
+ "input": "http://0999999999999999999/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://foo.0x",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://foo.0XFfFfFfFfFfFfFfFfFfAcE123",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "http://💩.123/",
+ "base": null,
+ "failure": true
+ },
+ "U+0000 and U+FFFF in various places",
+ {
+ "input": "https://\u0000y",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "https://x/\u0000y",
+ "base": null,
+ "hash": "",
+ "host": "x",
+ "hostname": "x",
+ "href": "https://x/%00y",
+ "password": "",
+ "pathname": "/%00y",
+ "port": "",
+ "protocol": "https:",
+ "search": "",
+ "username": ""
+ },
+ {
+ "input": "https://x/?\u0000y",
+ "base": null,
+ "hash": "",
+ "host": "x",
+ "hostname": "x",
+ "href": "https://x/?%00y",
+ "password": "",
+ "pathname": "/",
+ "port": "",
+ "protocol": "https:",
+ "search": "?%00y",
+ "username": ""
+ },
+ {
+ "input": "https://x/?#\u0000y",
+ "base": null,
+ "hash": "#%00y",
+ "host": "x",
+ "hostname": "x",
+ "href": "https://x/?#%00y",
+ "password": "",
+ "pathname": "/",
+ "port": "",
+ "protocol": "https:",
+ "search": "",
+ "username": ""
+ },
+ {
+ "input": "https://\uFFFFy",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "https://x/\uFFFFy",
+ "base": null,
+ "hash": "",
+ "host": "x",
+ "hostname": "x",
+ "href": "https://x/%EF%BF%BFy",
+ "password": "",
+ "pathname": "/%EF%BF%BFy",
+ "port": "",
+ "protocol": "https:",
+ "search": "",
+ "username": ""
+ },
+ {
+ "input": "https://x/?\uFFFFy",
+ "base": null,
+ "hash": "",
+ "host": "x",
+ "hostname": "x",
+ "href": "https://x/?%EF%BF%BFy",
+ "password": "",
+ "pathname": "/",
+ "port": "",
+ "protocol": "https:",
+ "search": "?%EF%BF%BFy",
+ "username": ""
+ },
+ {
+ "input": "https://x/?#\uFFFFy",
+ "base": null,
+ "hash": "#%EF%BF%BFy",
+ "host": "x",
+ "hostname": "x",
+ "href": "https://x/?#%EF%BF%BFy",
+ "password": "",
+ "pathname": "/",
+ "port": "",
+ "protocol": "https:",
+ "search": "",
+ "username": ""
+ },
+ {
+ "input": "non-special:\u0000y",
+ "base": null,
+ "hash": "",
+ "host": "",
+ "hostname": "",
+ "href": "non-special:%00y",
+ "password": "",
+ "pathname": "%00y",
+ "port": "",
+ "protocol": "non-special:",
+ "search": "",
+ "username": ""
+ },
+ {
+ "input": "non-special:x/\u0000y",
+ "base": null,
+ "hash": "",
+ "host": "",
+ "hostname": "",
+ "href": "non-special:x/%00y",
+ "password": "",
+ "pathname": "x/%00y",
+ "port": "",
+ "protocol": "non-special:",
+ "search": "",
+ "username": ""
+ },
+ {
+ "input": "non-special:x/?\u0000y",
+ "base": null,
+ "hash": "",
+ "host": "",
+ "hostname": "",
+ "href": "non-special:x/?%00y",
+ "password": "",
+ "pathname": "x/",
+ "port": "",
+ "protocol": "non-special:",
+ "search": "?%00y",
+ "username": ""
+ },
+ {
+ "input": "non-special:x/?#\u0000y",
+ "base": null,
+ "hash": "#%00y",
+ "host": "",
+ "hostname": "",
+ "href": "non-special:x/?#%00y",
+ "password": "",
+ "pathname": "x/",
+ "port": "",
+ "protocol": "non-special:",
+ "search": "",
+ "username": ""
+ },
+ {
+ "input": "non-special:\uFFFFy",
+ "base": null,
+ "hash": "",
+ "host": "",
+ "hostname": "",
+ "href": "non-special:%EF%BF%BFy",
+ "password": "",
+ "pathname": "%EF%BF%BFy",
+ "port": "",
+ "protocol": "non-special:",
+ "search": "",
+ "username": ""
+ },
+ {
+ "input": "non-special:x/\uFFFFy",
+ "base": null,
+ "hash": "",
+ "host": "",
+ "hostname": "",
+ "href": "non-special:x/%EF%BF%BFy",
+ "password": "",
+ "pathname": "x/%EF%BF%BFy",
+ "port": "",
+ "protocol": "non-special:",
+ "search": "",
+ "username": ""
+ },
+ {
+ "input": "non-special:x/?\uFFFFy",
+ "base": null,
+ "hash": "",
+ "host": "",
+ "hostname": "",
+ "href": "non-special:x/?%EF%BF%BFy",
+ "password": "",
+ "pathname": "x/",
+ "port": "",
+ "protocol": "non-special:",
+ "search": "?%EF%BF%BFy",
+ "username": ""
+ },
+ {
+ "input": "non-special:x/?#\uFFFFy",
+ "base": null,
+ "hash": "#%EF%BF%BFy",
+ "host": "",
+ "hostname": "",
+ "href": "non-special:x/?#%EF%BF%BFy",
+ "password": "",
+ "pathname": "x/",
+ "port": "",
+ "protocol": "non-special:",
+ "search": "",
+ "username": ""
+ },
+ {
+ "input": "",
+ "base": null,
+ "failure": true,
+ "relativeTo": "non-opaque-path-base"
+ },
+ {
+ "input": "https://example.com/\"quoted\"",
+ "base": null,
+ "hash": "",
+ "host": "example.com",
+ "hostname": "example.com",
+ "href": "https://example.com/%22quoted%22",
+ "origin": "https://example.com",
+ "password": "",
+ "pathname": "/%22quoted%22",
+ "port": "",
+ "protocol": "https:",
+ "search": "",
+ "username": ""
+ },
+ {
+ "input": "https://a%C2%ADb/",
+ "base": null,
+ "hash": "",
+ "host": "ab",
+ "hostname": "ab",
+ "href": "https://ab/",
+ "origin": "https://ab",
+ "password": "",
+ "pathname": "/",
+ "port": "",
+ "protocol": "https:",
+ "search": "",
+ "username": ""
+ },
+ {
+ "comment": "Empty host after domain to ASCII",
+ "input": "https://\u00AD/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "https://%C2%AD/",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "https://xn--/",
+ "base": null,
+ "failure": true
+ },
+ "Non-special schemes that some implementations might incorrectly treat as special",
+ {
+ "input": "data://example.com:8080/pathname?search#hash",
+ "base": null,
+ "href": "data://example.com:8080/pathname?search#hash",
+ "origin": "null",
+ "protocol": "data:",
+ "username": "",
+ "password": "",
+ "host": "example.com:8080",
+ "hostname": "example.com",
+ "port": "8080",
+ "pathname": "/pathname",
+ "search": "?search",
+ "hash": "#hash"
+ },
+ {
+ "input": "data:///test",
+ "base": null,
+ "href": "data:///test",
+ "origin": "null",
+ "protocol": "data:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "data://test/a/../b",
+ "base": null,
+ "href": "data://test/b",
+ "origin": "null",
+ "protocol": "data:",
+ "username": "",
+ "password": "",
+ "host": "test",
+ "hostname": "test",
+ "port": "",
+ "pathname": "/b",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "data://:443",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "data://test:test",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "data://[:1]",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "javascript://example.com:8080/pathname?search#hash",
+ "base": null,
+ "href": "javascript://example.com:8080/pathname?search#hash",
+ "origin": "null",
+ "protocol": "javascript:",
+ "username": "",
+ "password": "",
+ "host": "example.com:8080",
+ "hostname": "example.com",
+ "port": "8080",
+ "pathname": "/pathname",
+ "search": "?search",
+ "hash": "#hash"
+ },
+ {
+ "input": "javascript:///test",
+ "base": null,
+ "href": "javascript:///test",
+ "origin": "null",
+ "protocol": "javascript:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "javascript://test/a/../b",
+ "base": null,
+ "href": "javascript://test/b",
+ "origin": "null",
+ "protocol": "javascript:",
+ "username": "",
+ "password": "",
+ "host": "test",
+ "hostname": "test",
+ "port": "",
+ "pathname": "/b",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "javascript://:443",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "javascript://test:test",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "javascript://[:1]",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "mailto://example.com:8080/pathname?search#hash",
+ "base": null,
+ "href": "mailto://example.com:8080/pathname?search#hash",
+ "origin": "null",
+ "protocol": "mailto:",
+ "username": "",
+ "password": "",
+ "host": "example.com:8080",
+ "hostname": "example.com",
+ "port": "8080",
+ "pathname": "/pathname",
+ "search": "?search",
+ "hash": "#hash"
+ },
+ {
+ "input": "mailto:///test",
+ "base": null,
+ "href": "mailto:///test",
+ "origin": "null",
+ "protocol": "mailto:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "mailto://test/a/../b",
+ "base": null,
+ "href": "mailto://test/b",
+ "origin": "null",
+ "protocol": "mailto:",
+ "username": "",
+ "password": "",
+ "host": "test",
+ "hostname": "test",
+ "port": "",
+ "pathname": "/b",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "mailto://:443",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "mailto://test:test",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "mailto://[:1]",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "intent://example.com:8080/pathname?search#hash",
+ "base": null,
+ "href": "intent://example.com:8080/pathname?search#hash",
+ "origin": "null",
+ "protocol": "intent:",
+ "username": "",
+ "password": "",
+ "host": "example.com:8080",
+ "hostname": "example.com",
+ "port": "8080",
+ "pathname": "/pathname",
+ "search": "?search",
+ "hash": "#hash"
+ },
+ {
+ "input": "intent:///test",
+ "base": null,
+ "href": "intent:///test",
+ "origin": "null",
+ "protocol": "intent:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "intent://test/a/../b",
+ "base": null,
+ "href": "intent://test/b",
+ "origin": "null",
+ "protocol": "intent:",
+ "username": "",
+ "password": "",
+ "host": "test",
+ "hostname": "test",
+ "port": "",
+ "pathname": "/b",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "intent://:443",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "intent://test:test",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "intent://[:1]",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "urn://example.com:8080/pathname?search#hash",
+ "base": null,
+ "href": "urn://example.com:8080/pathname?search#hash",
+ "origin": "null",
+ "protocol": "urn:",
+ "username": "",
+ "password": "",
+ "host": "example.com:8080",
+ "hostname": "example.com",
+ "port": "8080",
+ "pathname": "/pathname",
+ "search": "?search",
+ "hash": "#hash"
+ },
+ {
+ "input": "urn:///test",
+ "base": null,
+ "href": "urn:///test",
+ "origin": "null",
+ "protocol": "urn:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "urn://test/a/../b",
+ "base": null,
+ "href": "urn://test/b",
+ "origin": "null",
+ "protocol": "urn:",
+ "username": "",
+ "password": "",
+ "host": "test",
+ "hostname": "test",
+ "port": "",
+ "pathname": "/b",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "urn://:443",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "urn://test:test",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "urn://[:1]",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "turn://example.com:8080/pathname?search#hash",
+ "base": null,
+ "href": "turn://example.com:8080/pathname?search#hash",
+ "origin": "null",
+ "protocol": "turn:",
+ "username": "",
+ "password": "",
+ "host": "example.com:8080",
+ "hostname": "example.com",
+ "port": "8080",
+ "pathname": "/pathname",
+ "search": "?search",
+ "hash": "#hash"
+ },
+ {
+ "input": "turn:///test",
+ "base": null,
+ "href": "turn:///test",
+ "origin": "null",
+ "protocol": "turn:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "turn://test/a/../b",
+ "base": null,
+ "href": "turn://test/b",
+ "origin": "null",
+ "protocol": "turn:",
+ "username": "",
+ "password": "",
+ "host": "test",
+ "hostname": "test",
+ "port": "",
+ "pathname": "/b",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "turn://:443",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "turn://test:test",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "turn://[:1]",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "stun://example.com:8080/pathname?search#hash",
+ "base": null,
+ "href": "stun://example.com:8080/pathname?search#hash",
+ "origin": "null",
+ "protocol": "stun:",
+ "username": "",
+ "password": "",
+ "host": "example.com:8080",
+ "hostname": "example.com",
+ "port": "8080",
+ "pathname": "/pathname",
+ "search": "?search",
+ "hash": "#hash"
+ },
+ {
+ "input": "stun:///test",
+ "base": null,
+ "href": "stun:///test",
+ "origin": "null",
+ "protocol": "stun:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/test",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "stun://test/a/../b",
+ "base": null,
+ "href": "stun://test/b",
+ "origin": "null",
+ "protocol": "stun:",
+ "username": "",
+ "password": "",
+ "host": "test",
+ "hostname": "test",
+ "port": "",
+ "pathname": "/b",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "stun://:443",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "stun://test:test",
+ "base": null,
+ "failure": true
+ },
+ {
+ "input": "stun://[:1]",
+ "base": null,
+ "failure": true
+ }
+]