summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tests/auto/widgets/origins/origins.pro2
-rw-r--r--tests/auto/widgets/origins/resources/mixed_frame.html10
-rw-r--r--tests/auto/widgets/origins/resources/mixed_qrc.html12
-rw-r--r--tests/auto/widgets/origins/resources/mixed_tst.html12
-rw-r--r--tests/auto/widgets/origins/resources/subdir/frame2.html10
-rw-r--r--tests/auto/widgets/origins/resources/subdir/index.html26
-rw-r--r--tests/auto/widgets/origins/resources/subdir_frame1.html10
-rw-r--r--tests/auto/widgets/origins/resources/websocket.html12
-rw-r--r--tests/auto/widgets/origins/tst_origins.cpp268
-rw-r--r--tests/auto/widgets/origins/tst_origins.qrc12
-rw-r--r--tests/auto/widgets/widgets.pro5
11 files changed, 377 insertions, 2 deletions
diff --git a/tests/auto/widgets/origins/origins.pro b/tests/auto/widgets/origins/origins.pro
new file mode 100644
index 000000000..566666e0a
--- /dev/null
+++ b/tests/auto/widgets/origins/origins.pro
@@ -0,0 +1,2 @@
+include(../tests.pri)
+CONFIG += c++14
diff --git a/tests/auto/widgets/origins/resources/mixed_frame.html b/tests/auto/widgets/origins/resources/mixed_frame.html
new file mode 100644
index 000000000..53d341b93
--- /dev/null
+++ b/tests/auto/widgets/origins/resources/mixed_frame.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Mixed - Frame</title>
+ <script>
+ parent.msg = "mixed";
+ </script>
+ </head>
+ <body></body>
+</html>
diff --git a/tests/auto/widgets/origins/resources/mixed_qrc.html b/tests/auto/widgets/origins/resources/mixed_qrc.html
new file mode 100644
index 000000000..664f7af6f
--- /dev/null
+++ b/tests/auto/widgets/origins/resources/mixed_qrc.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Mixed</title>
+ <script>
+ var msg;
+ </script>
+ </head>
+ <body>
+ <iframe src="qrc:///resources/mixed_frame.html"></iframe>
+ </body>
+</html>
diff --git a/tests/auto/widgets/origins/resources/mixed_tst.html b/tests/auto/widgets/origins/resources/mixed_tst.html
new file mode 100644
index 000000000..627e58098
--- /dev/null
+++ b/tests/auto/widgets/origins/resources/mixed_tst.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Mixed</title>
+ <script>
+ var msg;
+ </script>
+ </head>
+ <body>
+ <iframe src="tst:///resources/mixed_frame.html"></iframe>
+ </body>
+</html>
diff --git a/tests/auto/widgets/origins/resources/subdir/frame2.html b/tests/auto/widgets/origins/resources/subdir/frame2.html
new file mode 100644
index 000000000..3a2f664ca
--- /dev/null
+++ b/tests/auto/widgets/origins/resources/subdir/frame2.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Subdir - Frame 2</title>
+ <script>
+ parent.msg[1] = "world";
+ </script>
+ </head>
+ <body></body>
+</html>
diff --git a/tests/auto/widgets/origins/resources/subdir/index.html b/tests/auto/widgets/origins/resources/subdir/index.html
new file mode 100644
index 000000000..9c5d5d782
--- /dev/null
+++ b/tests/auto/widgets/origins/resources/subdir/index.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Subdir</title>
+
+ <script>
+ var msg = [];
+ </script>
+
+ <!-- for manual testing -->
+ <script>
+ window.addEventListener("load", () => {
+ for (let i of [0, 1]) {
+ let p = document.createElement("p");
+ p.appendChild(document.createTextNode(`frame ${i+1} says: ${msg[i]}`));
+ document.body.insertBefore(p, null);
+ }
+ });
+ </script>
+
+ </head>
+ <body>
+ <iframe src="../subdir_frame1.html"></iframe>
+ <iframe src="frame2.html"></iframe>
+ </body>
+</html>
diff --git a/tests/auto/widgets/origins/resources/subdir_frame1.html b/tests/auto/widgets/origins/resources/subdir_frame1.html
new file mode 100644
index 000000000..63973f2f4
--- /dev/null
+++ b/tests/auto/widgets/origins/resources/subdir_frame1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Subdir - Frame 1</title>
+ <script>
+ parent.msg[0] = "hello";
+ </script>
+ </head>
+ <body></body>
+</html>
diff --git a/tests/auto/widgets/origins/resources/websocket.html b/tests/auto/widgets/origins/resources/websocket.html
new file mode 100644
index 000000000..949596d1c
--- /dev/null
+++ b/tests/auto/widgets/origins/resources/websocket.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>WebSocket</title>
+ <script>
+ var err;
+ const ws = new WebSocket("ws://example.invalid");
+ ws.addEventListener("close", () => err = event.code);
+ </script>
+ </head>
+ <body></body>
+</html>
diff --git a/tests/auto/widgets/origins/tst_origins.cpp b/tests/auto/widgets/origins/tst_origins.cpp
new file mode 100644
index 000000000..5c798ddc2
--- /dev/null
+++ b/tests/auto/widgets/origins/tst_origins.cpp
@@ -0,0 +1,268 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "../util.h"
+
+#include <QtCore/qfile.h>
+#include <QtTest/QtTest>
+#include <QtWebEngineCore/qwebengineurlrequestjob.h>
+#include <QtWebEngineCore/qwebengineurlschemehandler.h>
+#include <QtWebEngineWidgets/qwebenginepage.h>
+#include <QtWebEngineWidgets/qwebengineprofile.h>
+#include <QtWebEngineWidgets/qwebenginesettings.h>
+
+#define QSL QStringLiteral
+#define QBAL QByteArrayLiteral
+#define THIS_DIR TESTS_SOURCE_DIR "origins/"
+
+class TstUrlSchemeHandler final : public QWebEngineUrlSchemeHandler {
+ Q_OBJECT
+
+public:
+ TstUrlSchemeHandler(QWebEngineProfile *profile)
+ {
+ profile->installUrlSchemeHandler(QBAL("tst"), this);
+ }
+
+private:
+ void requestStarted(QWebEngineUrlRequestJob *job) override
+ {
+ QString pathPrefix = QSL(THIS_DIR);
+ QString pathSuffix = job->requestUrl().path();
+ QFile *file = new QFile(pathPrefix + pathSuffix, job);
+ if (!file->open(QIODevice::ReadOnly)) {
+ job->fail(QWebEngineUrlRequestJob::RequestFailed);
+ return;
+ }
+ job->reply(QBAL("text/html"), file);
+ }
+};
+
+class tst_Origins final : public QObject {
+ Q_OBJECT
+
+private Q_SLOTS:
+ void jsUrlCanon();
+ void jsUrlOrigin();
+ void subdirWithAccess();
+ void subdirWithoutAccess();
+ void mixedSchemes();
+ void webSocket();
+
+private:
+ bool load(const QUrl &url)
+ {
+ QSignalSpy spy(&m_page, &QWebEnginePage::loadFinished);
+ m_page.load(url);
+ return (!spy.empty() || spy.wait())
+ && spy.front().value(0).toBool();
+ }
+
+ QVariant eval(const QString &code)
+ {
+ return evaluateJavaScriptSync(&m_page, code);
+ }
+
+ QWebEngineProfile m_profile;
+ QWebEnginePage m_page{&m_profile};
+ TstUrlSchemeHandler m_handler{&m_profile};
+};
+
+// Test URL parsing and canonicalization in Blink. The implementation of this
+// part is mostly shared between Blink and Chromium proper.
+void tst_Origins::jsUrlCanon()
+{
+ QVERIFY(load(QSL("about:blank")));
+
+ // Standard schemes are biased towards the authority part.
+ QCOMPARE(eval(QSL("new URL(\"http:foo/bar\").href")), QVariant(QSL("http://foo/bar")));
+ QCOMPARE(eval(QSL("new URL(\"http:/foo/bar\").href")), QVariant(QSL("http://foo/bar")));
+ QCOMPARE(eval(QSL("new URL(\"http://foo/bar\").href")), QVariant(QSL("http://foo/bar")));
+ QCOMPARE(eval(QSL("new URL(\"http:///foo/bar\").href")), QVariant(QSL("http://foo/bar")));
+
+ // The file scheme is however a (particularly) special case.
+#ifdef Q_OS_WIN
+ QCOMPARE(eval(QSL("new URL(\"file:foo/bar\").href")), QVariant(QSL("file://foo/bar")));
+ QCOMPARE(eval(QSL("new URL(\"file:/foo/bar\").href")), QVariant(QSL("file://foo/bar")));
+ QCOMPARE(eval(QSL("new URL(\"file://foo/bar\").href")), QVariant(QSL("file://foo/bar")));
+ QCOMPARE(eval(QSL("new URL(\"file:///foo/bar\").href")), QVariant(QSL("file:///foo/bar")));
+#else
+ QCOMPARE(eval(QSL("new URL(\"file:foo/bar\").href")), QVariant(QSL("file:///foo/bar")));
+ QCOMPARE(eval(QSL("new URL(\"file:/foo/bar\").href")), QVariant(QSL("file:///foo/bar")));
+ QCOMPARE(eval(QSL("new URL(\"file://foo/bar\").href")), QVariant(QSL("file://foo/bar")));
+ QCOMPARE(eval(QSL("new URL(\"file:///foo/bar\").href")), QVariant(QSL("file:///foo/bar")));
+#endif
+
+ // The qrc scheme is a 'dumb' URL, having only a path and nothing else.
+ QCOMPARE(eval(QSL("new URL(\"qrc:foo/bar\").href")), QVariant(QSL("qrc:foo/bar")));
+ QCOMPARE(eval(QSL("new URL(\"qrc:/foo/bar\").href")), QVariant(QSL("qrc:/foo/bar")));
+ QCOMPARE(eval(QSL("new URL(\"qrc://foo/bar\").href")), QVariant(QSL("qrc://foo/bar")));
+ QCOMPARE(eval(QSL("new URL(\"qrc:///foo/bar\").href")), QVariant(QSL("qrc:///foo/bar")));
+
+ // Same for custom schemes.
+ QCOMPARE(eval(QSL("new URL(\"tst:foo/bar\").href")), QVariant(QSL("tst:foo/bar")));
+ QCOMPARE(eval(QSL("new URL(\"tst:/foo/bar\").href")), QVariant(QSL("tst:/foo/bar")));
+ QCOMPARE(eval(QSL("new URL(\"tst://foo/bar\").href")), QVariant(QSL("tst://foo/bar")));
+ QCOMPARE(eval(QSL("new URL(\"tst:///foo/bar\").href")), QVariant(QSL("tst:///foo/bar")));
+}
+
+// Test origin serialization in Blink, implemented by blink::KURL and
+// blink::SecurityOrigin as opposed to GURL and url::Origin.
+void tst_Origins::jsUrlOrigin()
+{
+ QVERIFY(load(QSL("about:blank")));
+
+ // For network protocols the origin string must include the domain and port.
+ QCOMPARE(eval(QSL("new URL(\"http://foo.com/page.html\").origin")), QVariant(QSL("http://foo.com")));
+ QCOMPARE(eval(QSL("new URL(\"https://foo.com/page.html\").origin")), QVariant(QSL("https://foo.com")));
+
+ // Even though file URL can also have domains, these are not included in the
+ // origin string by Chromium. The standard does not specify a value here,
+ // but suggests 'null' (https://url.spec.whatwg.org/#origin).
+ QCOMPARE(eval(QSL("new URL(\"file:/etc/passwd\").origin")), QVariant(QSL("file://")));
+ QCOMPARE(eval(QSL("new URL(\"file://foo.com/etc/passwd\").origin")), QVariant(QSL("file://")));
+
+ // The qrc scheme should behave like file.
+ QCOMPARE(eval(QSL("new URL(\"qrc:/crysis.css\").origin")), QVariant(QSL("qrc://")));
+ QCOMPARE(eval(QSL("new URL(\"qrc://foo.com/crysis.css\").origin")), QVariant(QSL("qrc://")));
+
+ // Same with custom schemes.
+ QCOMPARE(eval(QSL("new URL(\"tst:/banana\").origin")), QVariant(QSL("tst://")));
+ QCOMPARE(eval(QSL("new URL(\"tst://foo.com/banana\").origin")), QVariant(QSL("tst://")));
+}
+
+class ScopedAttribute {
+public:
+ ScopedAttribute(QWebEngineSettings *settings, QWebEngineSettings::WebAttribute attribute, bool newValue)
+ : m_settings(settings)
+ , m_attribute(attribute)
+ , m_oldValue(m_settings->testAttribute(m_attribute))
+ {
+ m_settings->setAttribute(m_attribute, newValue);
+ }
+ ~ScopedAttribute()
+ {
+ m_settings->setAttribute(m_attribute, m_oldValue);
+ }
+private:
+ QWebEngineSettings *m_settings;
+ QWebEngineSettings::WebAttribute m_attribute;
+ bool m_oldValue;
+};
+
+// Test same-origin policy of file, qrc and custom schemes.
+//
+// Note the test case involves the main page trying to load an iframe from a
+// file that resides in a parent directory. This is just a small detail to
+// demonstrate the difference with Firefox where such access is not allowed.
+void tst_Origins::subdirWithAccess()
+{
+ ScopedAttribute sa(m_page.settings(), QWebEngineSettings::LocalContentCanAccessFileUrls, true);
+
+ QVERIFY(load(QSL("file:" THIS_DIR "resources/subdir/index.html")));
+ QCOMPARE(eval(QSL("msg[0]")), QVariant(QSL("hello")));
+ QCOMPARE(eval(QSL("msg[1]")), QVariant(QSL("world")));
+
+ QVERIFY(load(QSL("qrc:/resources/subdir/index.html")));
+ QCOMPARE(eval(QSL("msg[0]")), QVariant(QSL("hello")));
+ QCOMPARE(eval(QSL("msg[1]")), QVariant(QSL("world")));
+
+ QVERIFY(load(QSL("tst:/resources/subdir/index.html")));
+ QCOMPARE(eval(QSL("msg[0]")), QVariant(QSL("hello")));
+ QCOMPARE(eval(QSL("msg[1]")), QVariant(QSL("world")));
+}
+
+// In this variation the LocalContentCanAccessFileUrls attribute is disabled. As
+// a result all file URLs will be considered to have unique/opaque origins, that
+// is, they are not the 'same origin as' any other origin.
+//
+// Note that this applies only to file URLs and not qrc or custom schemes.
+//
+// See also (in Blink):
+// - the allow_file_access_from_file_urls option and
+// - the blink::SecurityOrigin::BlockLocalAccessFromLocalOrigin() method.
+void tst_Origins::subdirWithoutAccess()
+{
+ ScopedAttribute sa(m_page.settings(), QWebEngineSettings::LocalContentCanAccessFileUrls, false);
+
+ QVERIFY(load(QSL("file:" THIS_DIR "resources/subdir/index.html")));
+ QCOMPARE(eval(QSL("msg[0]")), QVariant());
+ QCOMPARE(eval(QSL("msg[1]")), QVariant());
+
+ QVERIFY(load(QSL("qrc:/resources/subdir/index.html")));
+ QCOMPARE(eval(QSL("msg[0]")), QVariant(QSL("hello")));
+ QCOMPARE(eval(QSL("msg[1]")), QVariant(QSL("world")));
+
+ QVERIFY(load(QSL("tst:/resources/subdir/index.html")));
+ QCOMPARE(eval(QSL("msg[0]")), QVariant(QSL("hello")));
+ QCOMPARE(eval(QSL("msg[1]")), QVariant(QSL("world")));
+}
+
+// Try to mix schemes, for example by loading the main page over file with an
+// iframe over qrc. This should be forbidden.
+void tst_Origins::mixedSchemes()
+{
+ QVERIFY(load(QSL("file:" THIS_DIR "resources/mixed_qrc.html")));
+ QCOMPARE(eval(QSL("msg")), QVariant());
+ QVERIFY(load(QSL("file:" THIS_DIR "resources/mixed_tst.html")));
+ QCOMPARE(eval(QSL("msg")), QVariant());
+
+ QVERIFY(load(QSL("qrc:/resources/mixed_qrc.html")));
+ QCOMPARE(eval(QSL("msg")), QVariant(QSL("mixed")));
+ QVERIFY(load(QSL("qrc:/resources/mixed_tst.html")));
+ QCOMPARE(eval(QSL("msg")), QVariant());
+
+ QVERIFY(load(QSL("tst:/resources/mixed_qrc.html")));
+ QCOMPARE(eval(QSL("msg")), QVariant());
+ QVERIFY(load(QSL("tst:/resources/mixed_tst.html")));
+ QCOMPARE(eval(QSL("msg")), QVariant(QSL("mixed")));
+}
+
+// Try opening a WebSocket from pages loaded over various URL schemes.
+void tst_Origins::webSocket()
+{
+ // 1006 indicates 'Abnormal Closure'.
+ //
+ // The example page is passing a URL with a non-existent domain to the
+ // WebSocket constructor, so we expect the connection to fail. This is
+ // enough though to trigger the origin checks.
+ const int expected = 1006;
+
+ QVERIFY(load(QSL("file:" THIS_DIR "resources/websocket.html")));
+ QTRY_VERIFY(eval(QSL("err")) == QVariant(expected));
+
+ QVERIFY(load(QSL("qrc:/resources/websocket.html")));
+ QTRY_VERIFY(eval(QSL("err")) == QVariant(expected));
+
+ // FIXME(juvaldma): QTBUG-62536
+ // QVERIFY(load(QSL("tst:/resources/websocket.html")));
+ // QTRY_VERIFY(eval(QSL("err")) == QVariant(expected));
+}
+
+QTEST_MAIN(tst_Origins)
+#include "tst_origins.moc"
diff --git a/tests/auto/widgets/origins/tst_origins.qrc b/tests/auto/widgets/origins/tst_origins.qrc
new file mode 100644
index 000000000..47be3bd0d
--- /dev/null
+++ b/tests/auto/widgets/origins/tst_origins.qrc
@@ -0,0 +1,12 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+<qresource>
+ <file>resources/mixed_frame.html</file>
+ <file>resources/mixed_qrc.html</file>
+ <file>resources/mixed_tst.html</file>
+ <file>resources/subdir/frame2.html</file>
+ <file>resources/subdir/index.html</file>
+ <file>resources/subdir_frame1.html</file>
+ <file>resources/websocket.html</file>
+</qresource>
+</RCC>
diff --git a/tests/auto/widgets/widgets.pro b/tests/auto/widgets/widgets.pro
index 36dfaba9f..5a13e8075 100644
--- a/tests/auto/widgets/widgets.pro
+++ b/tests/auto/widgets/widgets.pro
@@ -3,6 +3,7 @@ QT_FOR_CONFIG += webengine
TEMPLATE = subdirs
SUBDIRS += \
+ origins \
qwebenginedefaultsurfaceformat \
qwebenginedownloads \
qwebenginefaviconmanager \
@@ -32,5 +33,5 @@ qtConfig(webengine-spellchecker):!cross_compile {
boot2qt: SUBDIRS -= qwebengineaccessibility qwebenginedefaultsurfaceformat \
qwebenginefaviconmanager qwebenginepage qwebenginehistory \
qwebengineprofile qwebengineschemes qwebenginescript \
- qwebengineview qwebenginedownloads qwebenginesettings
-
+ qwebengineview qwebenginedownloads qwebenginesettings \
+ origins