| /**************************************************************************** |
| ** |
| ** 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/qwebengineurlscheme.h> |
| #include <QtWebEngineCore/qwebengineurlschemehandler.h> |
| #include <QtWebEngineWidgets/qwebenginepage.h> |
| #include <QtWebEngineWidgets/qwebengineprofile.h> |
| #include <QtWebEngineWidgets/qwebenginesettings.h> |
| #if defined(WEBSOCKETS) |
| #include <QtWebSockets/qwebsocket.h> |
| #include <QtWebSockets/qwebsocketserver.h> |
| #include <QtWebChannel/qwebchannel.h> |
| #endif |
| #include <QtWidgets/qaction.h> |
| |
| #define QSL QStringLiteral |
| #define QBAL QByteArrayLiteral |
| #define THIS_DIR TESTS_SOURCE_DIR "origins/" |
| |
| void registerSchemes() |
| { |
| { |
| QWebEngineUrlScheme scheme(QBAL("PathSyntax")); |
| QWebEngineUrlScheme::registerScheme(scheme); |
| } |
| |
| { |
| QWebEngineUrlScheme scheme(QBAL("PathSyntax-Secure")); |
| scheme.setFlags(QWebEngineUrlScheme::SecureScheme); |
| QWebEngineUrlScheme::registerScheme(scheme); |
| } |
| |
| { |
| QWebEngineUrlScheme scheme(QBAL("PathSyntax-Secure-ServiceWorkersAllowed")); |
| scheme.setFlags(QWebEngineUrlScheme::SecureScheme | QWebEngineUrlScheme::ServiceWorkersAllowed); |
| QWebEngineUrlScheme::registerScheme(scheme); |
| } |
| |
| { |
| QWebEngineUrlScheme scheme(QBAL("PathSyntax-Local")); |
| scheme.setFlags(QWebEngineUrlScheme::LocalScheme); |
| QWebEngineUrlScheme::registerScheme(scheme); |
| } |
| |
| { |
| QWebEngineUrlScheme scheme(QBAL("PathSyntax-LocalAccessAllowed")); |
| scheme.setFlags(QWebEngineUrlScheme::LocalAccessAllowed); |
| QWebEngineUrlScheme::registerScheme(scheme); |
| } |
| |
| { |
| QWebEngineUrlScheme scheme(QBAL("PathSyntax-NoAccessAllowed")); |
| scheme.setFlags(QWebEngineUrlScheme::NoAccessAllowed); |
| QWebEngineUrlScheme::registerScheme(scheme); |
| } |
| |
| { |
| QWebEngineUrlScheme scheme(QBAL("PathSyntax-ServiceWorkersAllowed")); |
| scheme.setFlags(QWebEngineUrlScheme::ServiceWorkersAllowed); |
| QWebEngineUrlScheme::registerScheme(scheme); |
| } |
| |
| { |
| QWebEngineUrlScheme scheme(QBAL("PathSyntax-ViewSourceAllowed")); |
| scheme.setFlags(QWebEngineUrlScheme::ViewSourceAllowed); |
| QWebEngineUrlScheme::registerScheme(scheme); |
| } |
| |
| { |
| QWebEngineUrlScheme scheme(QBAL("HostSyntax")); |
| scheme.setSyntax(QWebEngineUrlScheme::Syntax::Host); |
| QWebEngineUrlScheme::registerScheme(scheme); |
| } |
| |
| { |
| QWebEngineUrlScheme scheme(QBAL("HostSyntax-ContentSecurityPolicyIgnored")); |
| scheme.setSyntax(QWebEngineUrlScheme::Syntax::Host); |
| scheme.setFlags(QWebEngineUrlScheme::ContentSecurityPolicyIgnored); |
| QWebEngineUrlScheme::registerScheme(scheme); |
| } |
| |
| { |
| QWebEngineUrlScheme scheme(QBAL("HostAndPortSyntax")); |
| scheme.setSyntax(QWebEngineUrlScheme::Syntax::HostAndPort); |
| scheme.setDefaultPort(42); |
| QWebEngineUrlScheme::registerScheme(scheme); |
| } |
| |
| { |
| QWebEngineUrlScheme scheme(QBAL("HostPortAndUserInformationSyntax")); |
| scheme.setSyntax(QWebEngineUrlScheme::Syntax::HostPortAndUserInformation); |
| scheme.setDefaultPort(42); |
| QWebEngineUrlScheme::registerScheme(scheme); |
| } |
| |
| { |
| QWebEngineUrlScheme scheme(QBAL("redirect1")); |
| scheme.setFlags(QWebEngineUrlScheme::CorsEnabled); |
| QWebEngineUrlScheme::registerScheme(scheme); |
| } |
| |
| { |
| QWebEngineUrlScheme scheme(QBAL("redirect2")); |
| scheme.setFlags(QWebEngineUrlScheme::CorsEnabled); |
| QWebEngineUrlScheme::registerScheme(scheme); |
| } |
| |
| { |
| QWebEngineUrlScheme scheme(QBAL("cors")); |
| scheme.setFlags(QWebEngineUrlScheme::CorsEnabled); |
| QWebEngineUrlScheme::registerScheme(scheme); |
| } |
| |
| } |
| Q_CONSTRUCTOR_FUNCTION(registerSchemes) |
| |
| class TstUrlSchemeHandler final : public QWebEngineUrlSchemeHandler { |
| Q_OBJECT |
| |
| public: |
| TstUrlSchemeHandler(QWebEngineProfile *profile) |
| { |
| profile->installUrlSchemeHandler(QBAL("tst"), this); |
| |
| profile->installUrlSchemeHandler(QBAL("PathSyntax"), this); |
| profile->installUrlSchemeHandler(QBAL("PathSyntax-Secure"), this); |
| profile->installUrlSchemeHandler(QBAL("PathSyntax-Secure-ServiceWorkersAllowed"), this); |
| profile->installUrlSchemeHandler(QBAL("PathSyntax-Local"), this); |
| profile->installUrlSchemeHandler(QBAL("PathSyntax-LocalAccessAllowed"), this); |
| profile->installUrlSchemeHandler(QBAL("PathSyntax-NoAccessAllowed"), this); |
| profile->installUrlSchemeHandler(QBAL("PathSyntax-ServiceWorkersAllowed"), this); |
| profile->installUrlSchemeHandler(QBAL("PathSyntax-ViewSourceAllowed"), this); |
| profile->installUrlSchemeHandler(QBAL("HostSyntax"), this); |
| profile->installUrlSchemeHandler(QBAL("HostSyntax-ContentSecurityPolicyIgnored"), this); |
| profile->installUrlSchemeHandler(QBAL("HostAndPortSyntax"), this); |
| profile->installUrlSchemeHandler(QBAL("HostPortAndUserInformationSyntax"), this); |
| profile->installUrlSchemeHandler(QBAL("redirect1"), this); |
| profile->installUrlSchemeHandler(QBAL("redirect2"), this); |
| profile->installUrlSchemeHandler(QBAL("cors"), this); |
| } |
| |
| QVector<QUrl> &requests() { return m_requests; } |
| |
| private: |
| void requestStarted(QWebEngineUrlRequestJob *job) override |
| { |
| QUrl url = job->requestUrl(); |
| m_requests << url; |
| |
| if (url.scheme() == QBAL("redirect1")) { |
| url.setScheme(QBAL("redirect2")); |
| job->redirect(url); |
| return; |
| } |
| |
| QString pathPrefix = QSL(THIS_DIR); |
| QString pathSuffix = url.path(); |
| QFile *file = new QFile(pathPrefix + pathSuffix, job); |
| if (!file->open(QIODevice::ReadOnly)) { |
| job->fail(QWebEngineUrlRequestJob::RequestFailed); |
| return; |
| } |
| QByteArray mimeType = QBAL("text/html"); |
| if (pathSuffix.endsWith(QSL(".js"))) |
| mimeType = QBAL("application/javascript"); |
| else if (pathSuffix.endsWith(QSL(".css"))) |
| mimeType = QBAL("text/css"); |
| job->reply(mimeType, file); |
| } |
| |
| QVector<QUrl> m_requests; |
| }; |
| |
| class tst_Origins final : public QObject { |
| Q_OBJECT |
| |
| private Q_SLOTS: |
| void initTestCase(); |
| void cleanupTestCase(); |
| void init(); |
| void cleanup(); |
| |
| void jsUrlCanon(); |
| void jsUrlRelative(); |
| void jsUrlOrigin(); |
| void subdirWithAccess(); |
| void subdirWithoutAccess(); |
| void mixedSchemes(); |
| void mixedSchemesWithCsp(); |
| void mixedXHR(); |
| #if defined(WEBSOCKETS) |
| void webSocket(); |
| #endif |
| void dedicatedWorker(); |
| void sharedWorker(); |
| void serviceWorker(); |
| void viewSource(); |
| void createObjectURL(); |
| void redirect(); |
| |
| private: |
| bool verifyLoad(const QUrl &url) |
| { |
| QSignalSpy spy(m_page, &QWebEnginePage::loadFinished); |
| m_page->load(url); |
| [&spy]() { QTRY_VERIFY_WITH_TIMEOUT(!spy.isEmpty(), 90000); }(); |
| return !spy.isEmpty() && spy.front().value(0).toBool(); |
| } |
| |
| QVariant eval(const QString &code) |
| { |
| return evaluateJavaScriptSync(m_page, code); |
| } |
| |
| QWebEngineProfile m_profile; |
| QWebEnginePage *m_page = nullptr; |
| TstUrlSchemeHandler *m_handler = nullptr; |
| }; |
| |
| void tst_Origins::initTestCase() |
| { |
| QTest::ignoreMessage( |
| QtWarningMsg, |
| QRegularExpression("Please register the custom scheme 'tst'.*")); |
| |
| m_handler = new TstUrlSchemeHandler(&m_profile); |
| } |
| |
| void tst_Origins::cleanupTestCase() |
| { |
| QVERIFY(!m_page); |
| delete m_handler; |
| } |
| |
| void tst_Origins::init() |
| { |
| m_page = new QWebEnginePage(&m_profile, nullptr); |
| } |
| |
| void tst_Origins::cleanup() |
| { |
| delete m_page; |
| m_page = nullptr; |
| m_handler->requests().clear(); |
| } |
| |
| // 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(verifyLoad(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 PathSyntax scheme, 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 unregistered 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"))); |
| |
| // A HostSyntax scheme is like http without the port & user information. |
| QCOMPARE(eval(QSL("new URL(\"HostSyntax:foo/bar\").href")), QVariant(QSL("hostsyntax://foo/bar"))); |
| QCOMPARE(eval(QSL("new URL(\"HostSyntax:foo:42/bar\").href")), QVariant(QSL("hostsyntax://foo/bar"))); |
| QCOMPARE(eval(QSL("new URL(\"HostSyntax:a:b@foo/bar\").href")), QVariant(QSL("hostsyntax://foo/bar"))); |
| |
| // A HostAndPortSyntax scheme is like http without the user information. |
| QCOMPARE(eval(QSL("new URL(\"HostAndPortSyntax:foo/bar\").href")), |
| QVariant(QSL("hostandportsyntax://foo/bar"))); |
| QCOMPARE(eval(QSL("new URL(\"HostAndPortSyntax:foo:41/bar\").href")), |
| QVariant(QSL("hostandportsyntax://foo:41/bar"))); |
| QCOMPARE(eval(QSL("new URL(\"HostAndPortSyntax:foo:42/bar\").href")), |
| QVariant(QSL("hostandportsyntax://foo/bar"))); |
| QCOMPARE(eval(QSL("new URL(\"HostAndPortSyntax:a:b@foo/bar\").href")), |
| QVariant(QSL("hostandportsyntax://foo/bar"))); |
| |
| // A HostPortAndUserInformationSyntax scheme is exactly like http. |
| QCOMPARE(eval(QSL("new URL(\"HostPortAndUserInformationSyntax:foo/bar\").href")), |
| QVariant(QSL("hostportanduserinformationsyntax://foo/bar"))); |
| QCOMPARE(eval(QSL("new URL(\"HostPortAndUserInformationSyntax:foo:41/bar\").href")), |
| QVariant(QSL("hostportanduserinformationsyntax://foo:41/bar"))); |
| QCOMPARE(eval(QSL("new URL(\"HostPortAndUserInformationSyntax:foo:42/bar\").href")), |
| QVariant(QSL("hostportanduserinformationsyntax://foo/bar"))); |
| QCOMPARE(eval(QSL("new URL(\"HostPortAndUserInformationSyntax:a:b@foo/bar\").href")), |
| QVariant(QSL("hostportanduserinformationsyntax://a:b@foo/bar"))); |
| } |
| |
| // Test relative URL resolution. |
| void tst_Origins::jsUrlRelative() |
| { |
| QVERIFY(verifyLoad(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() |
| { |
| QVERIFY(verifyLoad(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://"))); |
| |
| // Unregistered schemes behave like file. |
| QCOMPARE(eval(QSL("new URL(\"tst:/banana\").origin")), QVariant(QSL("tst://"))); |
| QCOMPARE(eval(QSL("new URL(\"tst://foo.com/banana\").origin")), QVariant(QSL("tst://"))); |
| |
| // The non-PathSyntax schemes should have hosts and potentially ports. |
| QCOMPARE(eval(QSL("new URL(\"HostSyntax:foo:41/bar\").origin")), |
| QVariant(QSL("hostsyntax://foo"))); |
| QCOMPARE(eval(QSL("new URL(\"HostAndPortSyntax:foo:41/bar\").origin")), |
| QVariant(QSL("hostandportsyntax://foo:41"))); |
| QCOMPARE(eval(QSL("new URL(\"HostAndPortSyntax:foo:42/bar\").origin")), |
| QVariant(QSL("hostandportsyntax://foo"))); |
| QCOMPARE(eval(QSL("new URL(\"HostPortAndUserInformationSyntax:foo:41/bar\").origin")), |
| QVariant(QSL("hostportanduserinformationsyntax://foo:41"))); |
| QCOMPARE(eval(QSL("new URL(\"HostPortAndUserInformationSyntax:foo:42/bar\").origin")), |
| QVariant(QSL("hostportanduserinformationsyntax://foo"))); |
| |
| // A PathSyntax scheme should have a 'universal' origin. |
| QCOMPARE(eval(QSL("new URL(\"PathSyntax:foo\").origin")), QVariant(QSL("pathsyntax:"))); |
| 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:"))); |
| |
| // The NoAccessAllowed flag forces opaque origins. |
| QCOMPARE(eval(QSL("new URL(\"PathSyntax-NoAccessAllowed:foo\").origin")), |
| QVariant(QSL("null"))); |
| } |
| |
| 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(verifyLoad(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(verifyLoad(QSL("qrc:/resources/subdir/index.html"))); |
| QCOMPARE(eval(QSL("msg[0]")), QVariant(QSL("hello"))); |
| QCOMPARE(eval(QSL("msg[1]")), QVariant(QSL("world"))); |
| |
| QVERIFY(verifyLoad(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(verifyLoad(QSL("file:" THIS_DIR "resources/subdir/index.html"))); |
| QCOMPARE(eval(QSL("msg[0]")), QVariant()); |
| QCOMPARE(eval(QSL("msg[1]")), QVariant()); |
| |
| QVERIFY(verifyLoad(QSL("qrc:/resources/subdir/index.html"))); |
| QCOMPARE(eval(QSL("msg[0]")), QVariant(QSL("hello"))); |
| QCOMPARE(eval(QSL("msg[1]")), QVariant(QSL("world"))); |
| |
| QVERIFY(verifyLoad(QSL("tst:/resources/subdir/index.html"))); |
| QCOMPARE(eval(QSL("msg[0]")), QVariant(QSL("hello"))); |
| QCOMPARE(eval(QSL("msg[1]")), QVariant(QSL("world"))); |
| } |
| |
| // Load the main page over one scheme with an iframe over another scheme. |
| // |
| // For file and qrc schemes, the iframe should load but it should not be |
| // possible for scripts in different frames to interact. |
| // |
| // Additionally for unregistered custom schemes and custom schemes without |
| // LocalAccessAllowed it should not be possible to load an iframe over the |
| // file: scheme. |
| void tst_Origins::mixedSchemes() |
| { |
| QVERIFY(verifyLoad(QSL("file:" THIS_DIR "resources/mixedSchemes.html"))); |
| eval(QSL("setIFrameUrl('file:" THIS_DIR "resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess"))); |
| eval(QSL("setIFrameUrl('qrc:/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); |
| eval(QSL("setIFrameUrl('tst:/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); |
| |
| QVERIFY(verifyLoad(QSL("qrc:/resources/mixedSchemes.html"))); |
| eval(QSL("setIFrameUrl('file:" THIS_DIR "resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); |
| eval(QSL("setIFrameUrl('qrc:/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess"))); |
| eval(QSL("setIFrameUrl('tst:/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); |
| |
| QVERIFY(verifyLoad(QSL("tst:/resources/mixedSchemes.html"))); |
| eval(QSL("setIFrameUrl('file:" THIS_DIR "resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("cannotLoad"))); |
| eval(QSL("setIFrameUrl('qrc:/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); |
| eval(QSL("setIFrameUrl('tst:/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess"))); |
| |
| QVERIFY(verifyLoad(QSL("PathSyntax:/resources/mixedSchemes.html"))); |
| eval(QSL("setIFrameUrl('PathSyntax:/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess"))); |
| eval(QSL("setIFrameUrl('PathSyntax-Local:/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("cannotLoad"))); |
| eval(QSL("setIFrameUrl('PathSyntax-LocalAccessAllowed:/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); |
| eval(QSL("setIFrameUrl('PathSyntax-NoAccessAllowed:/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); |
| |
| QVERIFY(verifyLoad(QSL("PathSyntax-LocalAccessAllowed:/resources/mixedSchemes.html"))); |
| eval(QSL("setIFrameUrl('PathSyntax:/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); |
| eval(QSL("setIFrameUrl('PathSyntax-Local:/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); |
| eval(QSL("setIFrameUrl('PathSyntax-LocalAccessAllowed:/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess"))); |
| eval(QSL("setIFrameUrl('PathSyntax-NoAccessAllowed:/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); |
| |
| QVERIFY(verifyLoad(QSL("PathSyntax-NoAccessAllowed:/resources/mixedSchemes.html"))); |
| eval(QSL("setIFrameUrl('PathSyntax:/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); |
| eval(QSL("setIFrameUrl('PathSyntax-Local:/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("cannotLoad"))); |
| eval(QSL("setIFrameUrl('PathSyntax-LocalAccessAllowed:/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); |
| eval(QSL("setIFrameUrl('PathSyntax-NoAccessAllowed:/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); |
| |
| QVERIFY(verifyLoad(QSL("HostSyntax://a/resources/mixedSchemes.html"))); |
| eval(QSL("setIFrameUrl('HostSyntax://a/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess"))); |
| eval(QSL("setIFrameUrl('HostSyntax://b/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); |
| } |
| |
| // Like mixedSchemes but adds a Content-Security-Policy: frame-src 'none' header. |
| void tst_Origins::mixedSchemesWithCsp() |
| { |
| QVERIFY(verifyLoad(QSL("HostSyntax://a/resources/mixedSchemesWithCsp.html"))); |
| eval(QSL("setIFrameUrl('HostSyntax://a/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); |
| eval(QSL("setIFrameUrl('HostSyntax://b/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); |
| |
| QVERIFY(verifyLoad(QSL("HostSyntax-ContentSecurityPolicyIgnored://a/resources/mixedSchemesWithCsp.html"))); |
| eval(QSL("setIFrameUrl('HostSyntax-ContentSecurityPolicyIgnored://a/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess"))); |
| eval(QSL("setIFrameUrl('HostSyntax-ContentSecurityPolicyIgnored://b/resources/mixedSchemes_frame.html')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); |
| } |
| |
| // Load the main page over one scheme, then make an XMLHttpRequest to a |
| // different scheme. |
| // |
| // Cross-origin XMLHttpRequests can only be made to CORS-enabled schemes. These |
| // include the builtin schemes http, https, data, and chrome, as well as custom |
| // schemes with the CorsEnabled flag. |
| void tst_Origins::mixedXHR() |
| { |
| QVERIFY(verifyLoad(QSL("file:" THIS_DIR "resources/mixedXHR.html"))); |
| eval(QSL("sendXHR('file:" THIS_DIR "resources/mixedXHR.txt')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("ok"))); |
| eval(QSL("sendXHR('qrc:/resources/mixedXHR.txt')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("error"))); |
| eval(QSL("sendXHR('tst:/resources/mixedXHR.txt')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("error"))); |
| eval(QSL("sendXHR('data:,ok')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("ok"))); |
| eval(QSL("sendXHR('cors:/resources/mixedXHR.txt')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("ok"))); |
| |
| QVERIFY(verifyLoad(QSL("qrc:/resources/mixedXHR.html"))); |
| eval(QSL("sendXHR('file:" THIS_DIR "resources/mixedXHR.txt')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("ok"))); |
| eval(QSL("sendXHR('qrc:/resources/mixedXHR.txt')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("ok"))); |
| eval(QSL("sendXHR('tst:/resources/mixedXHR.txt')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("error"))); |
| eval(QSL("sendXHR('data:,ok')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("ok"))); |
| eval(QSL("sendXHR('cors:/resources/mixedXHR.txt')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("ok"))); |
| |
| QVERIFY(verifyLoad(QSL("tst:/resources/mixedXHR.html"))); |
| eval(QSL("sendXHR('file:" THIS_DIR "resources/mixedXHR.txt')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("error"))); |
| eval(QSL("sendXHR('qrc:/resources/mixedXHR.txt')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("error"))); |
| eval(QSL("sendXHR('tst:/resources/mixedXHR.txt')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("ok"))); |
| eval(QSL("sendXHR('data:,ok')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("ok"))); |
| eval(QSL("sendXHR('cors:/resources/mixedXHR.txt')")); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("ok"))); |
| } |
| |
| #if defined(WEBSOCKETS) |
| class EchoServer : public QObject { |
| Q_OBJECT |
| Q_PROPERTY(QUrl url READ url NOTIFY urlChanged) |
| public: |
| EchoServer() : webSocketServer(QSL("EchoServer"), QWebSocketServer::NonSecureMode) |
| { |
| connect(&webSocketServer, &QWebSocketServer::newConnection, this, &EchoServer::onNewConnection); |
| } |
| |
| bool listen() |
| { |
| if (webSocketServer.listen(QHostAddress::Any)) { |
| Q_EMIT urlChanged(); |
| return true; |
| } |
| return false; |
| } |
| |
| QUrl url() const |
| { |
| return webSocketServer.serverUrl(); |
| } |
| |
| Q_SIGNALS: |
| void urlChanged(); |
| |
| private: |
| void onNewConnection() |
| { |
| QWebSocket *socket = webSocketServer.nextPendingConnection(); |
| connect(socket, &QWebSocket::textMessageReceived, this, &EchoServer::onTextMessageReceived); |
| connect(socket, &QWebSocket::disconnected, socket, &QObject::deleteLater); |
| } |
| |
| void onTextMessageReceived(const QString &message) |
| { |
| QWebSocket *socket = qobject_cast<QWebSocket *>(sender()); |
| socket->sendTextMessage(message); |
| } |
| |
| QWebSocketServer webSocketServer; |
| }; |
| |
| // Try opening a WebSocket from pages loaded over various URL schemes. |
| void tst_Origins::webSocket() |
| { |
| EchoServer echoServer; |
| QWebChannel channel; |
| channel.registerObject(QSL("echoServer"), &echoServer); |
| m_page->setWebChannel(&channel); |
| QVERIFY(echoServer.listen()); |
| |
| QVERIFY(verifyLoad(QSL("file:" THIS_DIR "resources/websocket.html"))); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("ok"))); |
| |
| QVERIFY(verifyLoad(QSL("qrc:/resources/websocket.html"))); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("ok"))); |
| |
| // Unregistered schemes can also open WebSockets (since Chromium 71) |
| QVERIFY(verifyLoad(QSL("tst:/resources/websocket.html"))); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("ok"))); |
| |
| // Even an insecure registered scheme can open WebSockets. |
| QVERIFY(verifyLoad(QSL("PathSyntax:/resources/websocket.html"))); |
| QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("ok"))); |
| } |
| #endif |
| // Create a (Dedicated)Worker. Since dedicated workers can only be accessed from |
| // one page, there is not much need for security restrictions. |
| void tst_Origins::dedicatedWorker() |
| { |
| QVERIFY(verifyLoad(QSL("file:" THIS_DIR "resources/dedicatedWorker.html"))); |
| QTRY_VERIFY(eval(QSL("done")).toBool()); |
| QCOMPARE(eval(QSL("result")), QVariant(42)); |
| |
| QVERIFY(verifyLoad(QSL("qrc:/resources/dedicatedWorker.html"))); |
| QTRY_VERIFY(eval(QSL("done")).toBool()); |
| QCOMPARE(eval(QSL("result")), QVariant(42)); |
| |
| // Unregistered schemes can also create Workers (since Chromium 71) |
| QVERIFY(verifyLoad(QSL("tst:/resources/dedicatedWorker.html"))); |
| QTRY_VERIFY(eval(QSL("done")).toBool()); |
| QCOMPARE(eval(QSL("result")), QVariant(42)); |
| |
| // Even an insecure registered scheme can create Workers. |
| QVERIFY(verifyLoad(QSL("PathSyntax:/resources/dedicatedWorker.html"))); |
| QTRY_VERIFY(eval(QSL("done")).toBool()); |
| QCOMPARE(eval(QSL("result")), QVariant(42)); |
| |
| // But not if the NoAccessAllowed flag is set. |
| QVERIFY(verifyLoad(QSL("PathSyntax-NoAccessAllowed:/resources/dedicatedWorker.html"))); |
| QTRY_VERIFY(eval(QSL("done")).toBool()); |
| QVERIFY(eval(QSL("error")).toString() |
| .contains(QSL("cannot be accessed from origin 'null'"))); |
| } |
| |
| // Create a SharedWorker. Shared workers can be accessed from multiple pages, |
| // and therefore the same-origin policy applies. |
| void tst_Origins::sharedWorker() |
| { |
| { |
| ScopedAttribute sa(m_page->settings(), QWebEngineSettings::LocalContentCanAccessFileUrls, false); |
| QVERIFY(verifyLoad(QSL("file:" THIS_DIR "resources/sharedWorker.html"))); |
| QTRY_VERIFY_WITH_TIMEOUT(eval(QSL("done")).toBool(), 10000); |
| QVERIFY(eval(QSL("error")).toString() |
| .contains(QSL("cannot be accessed from origin 'null'"))); |
| } |
| |
| { |
| ScopedAttribute sa(m_page->settings(), QWebEngineSettings::LocalContentCanAccessFileUrls, true); |
| QVERIFY(verifyLoad(QSL("file:" THIS_DIR "resources/sharedWorker.html"))); |
| QTRY_VERIFY_WITH_TIMEOUT(eval(QSL("done")).toBool(), 10000); |
| QCOMPARE(eval(QSL("result")), QVariant(42)); |
| } |
| |
| QVERIFY(verifyLoad(QSL("qrc:/resources/sharedWorker.html"))); |
| QTRY_VERIFY_WITH_TIMEOUT(eval(QSL("done")).toBool(), 10000); |
| QCOMPARE(eval(QSL("result")), QVariant(42)); |
| |
| // Unregistered schemes should not create SharedWorkers. |
| |
| QVERIFY(verifyLoad(QSL("PathSyntax:/resources/sharedWorker.html"))); |
| QTRY_VERIFY_WITH_TIMEOUT(eval(QSL("done")).toBool(), 10000); |
| QCOMPARE(eval(QSL("result")), QVariant(42)); |
| |
| QVERIFY(verifyLoad(QSL("PathSyntax-NoAccessAllowed:/resources/sharedWorker.html"))); |
| QTRY_VERIFY_WITH_TIMEOUT(eval(QSL("done")).toBool(), 10000); |
| QVERIFY(eval(QSL("error")).toString() |
| .contains(QSL("denied to origin 'null'"))); |
| } |
| |
| // Service workers have to be explicitly enabled for a scheme. |
| void tst_Origins::serviceWorker() |
| { |
| QVERIFY(verifyLoad(QSL("file:" THIS_DIR "resources/serviceWorker.html"))); |
| QTRY_VERIFY(eval(QSL("done")).toBool()); |
| QVERIFY(eval(QSL("error")).toString() |
| .contains(QSL("The URL protocol of the current origin ('file://') is not supported."))); |
| |
| QVERIFY(verifyLoad(QSL("qrc:/resources/serviceWorker.html"))); |
| QTRY_VERIFY(eval(QSL("done")).toBool()); |
| QVERIFY(eval(QSL("error")).toString() |
| .contains(QSL("The URL protocol of the current origin ('qrc:') is not supported."))); |
| |
| QVERIFY(verifyLoad(QSL("tst:/resources/serviceWorker.html"))); |
| QTRY_VERIFY(eval(QSL("done")).toBool()); |
| QVERIFY(eval(QSL("error")).toString() |
| .contains(QSL("Cannot read property 'register' of undefined"))); |
| |
| QVERIFY(verifyLoad(QSL("PathSyntax:/resources/serviceWorker.html"))); |
| QTRY_VERIFY(eval(QSL("done")).toBool()); |
| QVERIFY(eval(QSL("error")).toString() |
| .contains(QSL("Cannot read property 'register' of undefined"))); |
| |
| QVERIFY(verifyLoad(QSL("PathSyntax-Secure:/resources/serviceWorker.html"))); |
| QTRY_VERIFY(eval(QSL("done")).toBool()); |
| QVERIFY(eval(QSL("error")).toString() |
| .contains(QSL("The URL protocol of the current origin ('pathsyntax-secure:') is not supported."))); |
| |
| QVERIFY(verifyLoad(QSL("PathSyntax-ServiceWorkersAllowed:/resources/serviceWorker.html"))); |
| QTRY_VERIFY(eval(QSL("done")).toBool()); |
| QVERIFY(eval(QSL("error")).toString() |
| .contains(QSL("Cannot read property 'register' of undefined"))); |
| |
| QVERIFY(verifyLoad(QSL("PathSyntax-Secure-ServiceWorkersAllowed:/resources/serviceWorker.html"))); |
| QTRY_VERIFY(eval(QSL("done")).toBool()); |
| QCOMPARE(eval(QSL("error")), QVariant()); |
| |
| QVERIFY(verifyLoad(QSL("PathSyntax-NoAccessAllowed:/resources/serviceWorker.html"))); |
| QTRY_VERIFY(eval(QSL("done")).toBool()); |
| QVERIFY(eval(QSL("error")).toString() |
| .contains(QSL("Cannot read property 'register' of undefined"))); |
| } |
| |
| // Support for view-source must be enabled explicitly. |
| void tst_Origins::viewSource() |
| { |
| QVERIFY(verifyLoad(QSL("view-source:file:" THIS_DIR "resources/viewSource.html"))); |
| #ifdef Q_OS_WIN |
| QCOMPARE(m_page->requestedUrl().toString(), QSL("file:///" THIS_DIR "resources/viewSource.html")); |
| #else |
| QCOMPARE(m_page->requestedUrl().toString(), QSL("file://" THIS_DIR "resources/viewSource.html")); |
| #endif |
| |
| QVERIFY(verifyLoad(QSL("view-source:qrc:/resources/viewSource.html"))); |
| QCOMPARE(m_page->requestedUrl().toString(), QSL("qrc:/resources/viewSource.html")); |
| |
| QVERIFY(verifyLoad(QSL("view-source:tst:/resources/viewSource.html"))); |
| QCOMPARE(m_page->requestedUrl().toString(), QSL("about:blank")); |
| |
| QVERIFY(verifyLoad(QSL("view-source:PathSyntax:/resources/viewSource.html"))); |
| QCOMPARE(m_page->requestedUrl().toString(), QSL("about:blank")); |
| |
| QVERIFY(verifyLoad(QSL("view-source:PathSyntax-ViewSourceAllowed:/resources/viewSource.html"))); |
| QCOMPARE(m_page->requestedUrl().toString(), QSL("pathsyntax-viewsourceallowed:/resources/viewSource.html")); |
| } |
| |
| void tst_Origins::createObjectURL() |
| { |
| // Legal for registered custom schemes. |
| QVERIFY(verifyLoad(QSL("qrc:/resources/createObjectURL.html"))); |
| QVERIFY(eval(QSL("result")).toString().startsWith(QSL("blob:qrc:"))); |
| |
| // Also legal for unregistered schemes (since Chromium 71) |
| QVERIFY(verifyLoad(QSL("tst:/resources/createObjectURL.html"))); |
| QVERIFY(eval(QSL("result")).toString().startsWith(QSL("blob:tst:"))); |
| } |
| |
| void tst_Origins::redirect() |
| { |
| QVERIFY(verifyLoad(QSL("redirect1:/resources/redirect.html"))); |
| QTRY_COMPARE(m_handler->requests().size(), 7); |
| QCOMPARE(m_handler->requests()[0], QUrl(QStringLiteral("redirect1:/resources/redirect.html"))); |
| QCOMPARE(m_handler->requests()[1], QUrl(QStringLiteral("redirect2:/resources/redirect.html"))); |
| QCOMPARE(m_handler->requests()[2], QUrl(QStringLiteral("redirect1:/resources/redirect.css"))); |
| QCOMPARE(m_handler->requests()[3], QUrl(QStringLiteral("redirect2:/resources/redirect.css"))); |
| QCOMPARE(m_handler->requests()[4], QUrl(QStringLiteral("redirect1:/resources/Akronim-Regular.woff2"))); |
| QCOMPARE(m_handler->requests()[5], QUrl(QStringLiteral("redirect1:/resources/Akronim-Regular.woff2"))); |
| QCOMPARE(m_handler->requests()[6], QUrl(QStringLiteral("redirect2:/resources/Akronim-Regular.woff2"))); |
| } |
| |
| QTEST_MAIN(tst_Origins) |
| #include "tst_origins.moc" |