From 03b5eb203a18dcd97d6ddf5dd4dfc980eca8200e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=BCri=20Valdmann?= <juri.valdmann@qt.io>
Date: Wed, 10 Apr 2019 12:30:04 +0200
Subject: [PATCH] Fix resolving relative URLs with custom schemes

Pull in 3rdparty patch and add test.

Fixes: QTBUG-74864
Change-Id: I5440f58ff55297c2a51f896d43f479404ff6ca2f
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
---
 src/3rdparty                               |  2 +-
 tests/auto/widgets/origins/tst_origins.cpp | 49 ++++++++++++++++++++++
 2 files changed, 50 insertions(+), 1 deletion(-)

diff --git a/src/3rdparty b/src/3rdparty
index 4c7ecce30..5bbb0fff1 160000
--- a/src/3rdparty
+++ b/src/3rdparty
@@ -1 +1 @@
-Subproject commit 4c7ecce30045daf172dceaeeb86351f60cc91990
+Subproject commit 5bbb0fff1e645bb29cd740767cda698e4e6d23e7
diff --git a/tests/auto/widgets/origins/tst_origins.cpp b/tests/auto/widgets/origins/tst_origins.cpp
index 59cbdae13..21afead1d 100644
--- a/tests/auto/widgets/origins/tst_origins.cpp
+++ b/tests/auto/widgets/origins/tst_origins.cpp
@@ -172,6 +172,7 @@ private Q_SLOTS:
     void cleanupTestCase();
 
     void jsUrlCanon();
+    void jsUrlRelative();
     void jsUrlOrigin();
     void subdirWithAccess();
     void subdirWithoutAccess();
@@ -281,6 +282,54 @@ void tst_Origins::jsUrlCanon()
              QVariant(QSL("hostportanduserinformationsyntax://a:b@foo/bar")));
 }
 
+// Test relative URL resolution.
+void tst_Origins::jsUrlRelative()
+{
+    QVERIFY(load(QSL("about:blank")));
+
+    // Schemes with hosts, like http, work as expected.
+    QCOMPARE(eval(QSL("new URL('bar', 'http://foo').href")), QVariant(QSL("http://foo/bar")));
+    QCOMPARE(eval(QSL("new URL('baz', 'http://foo/bar').href")), QVariant(QSL("http://foo/baz")));
+    QCOMPARE(eval(QSL("new URL('baz', 'http://foo/bar/').href")), QVariant(QSL("http://foo/bar/baz")));
+    QCOMPARE(eval(QSL("new URL('/baz', 'http://foo/bar/').href")), QVariant(QSL("http://foo/baz")));
+    QCOMPARE(eval(QSL("new URL('./baz', 'http://foo/bar/').href")), QVariant(QSL("http://foo/bar/baz")));
+    QCOMPARE(eval(QSL("new URL('../baz', 'http://foo/bar/').href")), QVariant(QSL("http://foo/baz")));
+    QCOMPARE(eval(QSL("new URL('../../baz', 'http://foo/bar/').href")), QVariant(QSL("http://foo/baz")));
+    QCOMPARE(eval(QSL("new URL('//baz', 'http://foo/bar/').href")), QVariant(QSL("http://baz/")));
+
+    // In the case of schemes without hosts, relative URLs only work if the URL
+    // starts with a single slash -- and canonicalization does not guarantee
+    // this. The following cases all fail with TypeErrors.
+    QCOMPARE(eval(QSL("new URL('bar', 'tst:foo').href")), QVariant());
+    QCOMPARE(eval(QSL("new URL('baz', 'tst:foo/bar').href")), QVariant());
+    QCOMPARE(eval(QSL("new URL('bar', 'tst://foo').href")), QVariant());
+    QCOMPARE(eval(QSL("new URL('bar', 'tst:///foo').href")), QVariant());
+
+    // However, registered custom schemes have been patched to allow relative
+    // URLs even without an initial slash.
+    QCOMPARE(eval(QSL("new URL('bar', 'qrc:foo').href")), QVariant(QSL("qrc:bar")));
+    QCOMPARE(eval(QSL("new URL('baz', 'qrc:foo/bar').href")), QVariant(QSL("qrc:foo/baz")));
+    QCOMPARE(eval(QSL("new URL('bar', 'qrc://foo').href")), QVariant());
+    QCOMPARE(eval(QSL("new URL('bar', 'qrc:///foo').href")), QVariant());
+
+    // With a slash it works the same as http except 'foo' is part of the path and not the host.
+    QCOMPARE(eval(QSL("new URL('bar', 'qrc:/foo').href")), QVariant(QSL("qrc:/bar")));
+    QCOMPARE(eval(QSL("new URL('bar', 'qrc:/foo/').href")), QVariant(QSL("qrc:/foo/bar")));
+    QCOMPARE(eval(QSL("new URL('baz', 'qrc:/foo/bar').href")), QVariant(QSL("qrc:/foo/baz")));
+    QCOMPARE(eval(QSL("new URL('baz', 'qrc:/foo/bar/').href")), QVariant(QSL("qrc:/foo/bar/baz")));
+    QCOMPARE(eval(QSL("new URL('/baz', 'qrc:/foo/bar/').href")), QVariant(QSL("qrc:/baz")));
+    QCOMPARE(eval(QSL("new URL('./baz', 'qrc:/foo/bar/').href")), QVariant(QSL("qrc:/foo/bar/baz")));
+    QCOMPARE(eval(QSL("new URL('../baz', 'qrc:/foo/bar/').href")), QVariant(QSL("qrc:/foo/baz")));
+    QCOMPARE(eval(QSL("new URL('../../baz', 'qrc:/foo/bar/').href")), QVariant(QSL("qrc:/baz")));
+    QCOMPARE(eval(QSL("new URL('../../../baz', 'qrc:/foo/bar/').href")), QVariant(QSL("qrc:/baz")));
+
+    // If the relative URL begins with >= 2 slashes, then the scheme is treated
+    // not as a Syntax::Path scheme but as a Syntax::HostPortAndUserInformation
+    // scheme.
+    QCOMPARE(eval(QSL("new URL('//baz', 'qrc:/foo/bar/').href")), QVariant(QSL("qrc://baz/")));
+    QCOMPARE(eval(QSL("new URL('///baz', 'qrc:/foo/bar/').href")), QVariant(QSL("qrc://baz/")));
+}
+
 // Test origin serialization in Blink, implemented by blink::KURL and
 // blink::SecurityOrigin as opposed to GURL and url::Origin.
 void tst_Origins::jsUrlOrigin()
-- 
GitLab