summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/api/core_api.pro2
-rw-r--r--src/core/api/qwebengineurlscheme.cpp375
-rw-r--r--src/core/api/qwebengineurlscheme.h114
-rw-r--r--src/core/content_browser_client_qt.cpp9
-rw-r--r--src/core/content_browser_client_qt.h1
-rw-r--r--src/core/content_client_qt.cpp5
-rw-r--r--src/core/content_client_qt.h1
-rw-r--r--src/core/content_main_delegate_qt.cpp4
-rw-r--r--src/core/web_engine_context.cpp7
-rw-r--r--src/webengine/api/qquickwebengineprofile.cpp12
-rw-r--r--src/webengine/doc/src/qtwebengine-features.qdoc13
-rw-r--r--src/webenginewidgets/api/qwebengineprofile.cpp12
-rw-r--r--tests/auto/widgets/origins/origins.pro1
-rw-r--r--tests/auto/widgets/origins/resources/mixedSchemes.html (renamed from tests/auto/widgets/origins/resources/mixed.html)0
-rw-r--r--tests/auto/widgets/origins/resources/mixedSchemesWithCsp.html32
-rw-r--r--tests/auto/widgets/origins/resources/mixedSchemes_frame.html (renamed from tests/auto/widgets/origins/resources/mixed_frame.html)0
-rw-r--r--tests/auto/widgets/origins/resources/serviceWorker.html10
-rw-r--r--tests/auto/widgets/origins/resources/viewSource.html9
-rw-r--r--tests/auto/widgets/origins/resources/websocket.html17
-rw-r--r--tests/auto/widgets/origins/tst_origins.cpp374
-rw-r--r--tests/auto/widgets/origins/tst_origins.qrc6
21 files changed, 946 insertions, 58 deletions
diff --git a/src/core/api/core_api.pro b/src/core/api/core_api.pro
index 1d66b47d0..e0600e6fe 100644
--- a/src/core/api/core_api.pro
+++ b/src/core/api/core_api.pro
@@ -43,6 +43,7 @@ HEADERS = \
qwebengineurlrequestinfo.h \
qwebengineurlrequestinfo_p.h \
qwebengineurlrequestjob.h \
+ qwebengineurlscheme.h \
qwebengineurlschemehandler.h
SOURCES = \
@@ -53,6 +54,7 @@ SOURCES = \
qwebengineregisterprotocolhandlerrequest.cpp \
qwebengineurlrequestinfo.cpp \
qwebengineurlrequestjob.cpp \
+ qwebengineurlscheme.cpp \
qwebengineurlschemehandler.cpp
### Qt6 Remove this workaround
diff --git a/src/core/api/qwebengineurlscheme.cpp b/src/core/api/qwebengineurlscheme.cpp
new file mode 100644
index 000000000..24bcae195
--- /dev/null
+++ b/src/core/api/qwebengineurlscheme.cpp
@@ -0,0 +1,375 @@
+/****************************************************************************
+**
+** 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:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwebengineurlscheme.h"
+
+#include <url/url_util_qt.h>
+
+QT_BEGIN_NAMESPACE
+
+ASSERT_ENUMS_MATCH(QWebEngineUrlScheme::PathSyntax, url::SCHEME_WITHOUT_AUTHORITY)
+ASSERT_ENUMS_MATCH(QWebEngineUrlScheme::HostSyntax, url::SCHEME_WITH_HOST)
+ASSERT_ENUMS_MATCH(QWebEngineUrlScheme::HostAndPortSyntax, url::SCHEME_WITH_HOST_AND_PORT)
+ASSERT_ENUMS_MATCH(QWebEngineUrlScheme::HostPortAndUserInformationSyntax,
+ url::SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION)
+
+ASSERT_ENUMS_MATCH(QWebEngineUrlScheme::PortUnspecified, url::PORT_UNSPECIFIED);
+
+ASSERT_ENUMS_MATCH(QWebEngineUrlScheme::Secure, url::CustomScheme::Secure)
+ASSERT_ENUMS_MATCH(QWebEngineUrlScheme::Local, url::CustomScheme::Local)
+ASSERT_ENUMS_MATCH(QWebEngineUrlScheme::LocalAccessAllowed, url::CustomScheme::LocalAccessAllowed)
+ASSERT_ENUMS_MATCH(QWebEngineUrlScheme::NoAccessAllowed, url::CustomScheme::NoAccessAllowed)
+ASSERT_ENUMS_MATCH(QWebEngineUrlScheme::ServiceWorkersAllowed, url::CustomScheme::ServiceWorkersAllowed)
+ASSERT_ENUMS_MATCH(QWebEngineUrlScheme::ViewSourceAllowed, url::CustomScheme::ViewSourceAllowed)
+ASSERT_ENUMS_MATCH(QWebEngineUrlScheme::ContentSecurityPolicyIgnored, url::CustomScheme::ContentSecurityPolicyIgnored)
+
+class QWebEngineUrlSchemePrivate : public QSharedData
+ , public url::CustomScheme
+{
+public:
+ QWebEngineUrlSchemePrivate() {}
+ QWebEngineUrlSchemePrivate(const url::CustomScheme &cs) : url::CustomScheme(cs) {}
+ static QSharedDataPointer<QWebEngineUrlSchemePrivate> defaultConstructed()
+ {
+ static QSharedDataPointer<QWebEngineUrlSchemePrivate> instance(new QWebEngineUrlSchemePrivate);
+ return instance;
+ }
+};
+
+/*!
+ \class QWebEngineUrlScheme
+ \inmodule QtWebEngineCore
+ \since 5.12
+ \brief The QWebEngineUrlScheme class configures a custom URL scheme.
+
+ A web engine URL scheme describes a URL scheme from the web engine's
+ perspective, specifying how URLs of this scheme should be parsed, and which
+ security restrictions should be placed on resources originating from such
+ URLs.
+
+ Custom URL schemes must be configured early at application startup, before
+ creating any Qt WebEngine classes. The configuration applies globally to all
+ profiles.
+
+ \code
+ int main(int argc, char **argv)
+ {
+ QWebEngineUrlScheme scheme("myscheme");
+ scheme.setSyntax(QWebEngineUrlScheme::HostAndPortSyntax);
+ scheme.setDefaultPort(2345);
+ scheme.setFlags(QWebEngineUrlScheme::Secure);
+ QWebEngineUrlScheme::addScheme(scheme);
+ ...
+ }
+ \endcode
+
+ To actually make use of the custom URL scheme, a \l QWebEngineUrlSchemeHandler
+ must be created and registered in a profile.
+
+ \sa QWebEngineUrlSchemeHandler
+*/
+
+/*!
+ \enum QWebEngineUrlScheme::Syntax
+
+ This enum type lists types of URL syntax.
+
+ To apply the same-origin policy to a custom URL scheme, WebEngine must be able
+ to compute the origin (host and port combination) of a URL. The \c {Host...}
+ options indicate that the URL scheme conforms to the standard URL syntax (like
+ \c http) and automatically enable the same-origin policy. The \c {PathSyntax}
+ option indicates that the URL scheme uses a non-standard syntax and that the
+ same-origin policy cannot be applied.
+
+ \value HostPortAndUserInformationSyntax
+ The authority component of a URL of this type has all of the standard
+ elements: host, port, user name, and password. A URL without a port will use
+ the \l defaultPort (which \e must not be \l PortUnspecified).
+
+ \value HostAndPortSyntax
+ The authority component of a URL of this type has only the host and port
+ elements. A URL without a port will use the \l defaultPort (which \e must not
+ be \l PortUnspecified).
+
+ \value HostSyntax
+ The authority component of a URL of this type has only the host part and no
+ port. The \l defaultPort \e must be set to \l PortUnspecified.
+
+ \value PathSyntax
+ A URL of this type has no authority component at all. Everything after scheme
+ name and separator character (:) will be preserved as is without validation
+ or canonicalization. All URLs of such a scheme will be considered as having
+ the same origin (unless the \c NoAccessAllowed flag is used).
+*/
+
+/*!
+ \enum QWebEngineUrlScheme::SpecialPort
+
+ This enum type defines special values for \l defaultPort.
+
+ \value PortUnspecified
+ Indicates that the URL scheme does not have a port element.
+*/
+
+/*!
+ \enum QWebEngineUrlScheme::Flag
+
+ This enum type specifies security options that should apply to a URL scheme.
+
+ \value Secure
+ Indicates that the URL scheme is
+ \l{https://www.w3.org/TR/powerful-features/#is-origin-trustworthy}{potentially
+ trustworthy}. This flag should only be applied to URL schemes which ensure
+ data authenticity, confidentiality, and integrity, either through encryption
+ or other means. Examples of secure builtin schemes include \c https
+ (authenticated and encrypted) and \c qrc (local resources only), whereas \c
+ http is an example of an insecure scheme.
+
+ \value Local
+ Indicates that the URL scheme provides access to local resources. The purpose
+ of this flag is to prevent network content from accessing local resources.
+ Only schemes with the \c LocalAccessAllowed flag may load resources from a
+ scheme with the \c Local flag. The only builtin schemes with this flag are \c
+ file and \c qrc.
+
+ \value LocalAccessAllowed
+ Indicates that content from this scheme should be allowed to load resources
+ from schemes with the \c Local flag.
+
+ \value NoAccessAllowed
+ Indicates that all content from this scheme should be forced to have unique
+ opaque origins: no two resources will have the same origin.
+
+ \value ServiceWorkersAllowed
+ Indicates that the Service Workers API should be enabled.
+
+ \value ViewSourceAllowed
+ Indicates that the View Source feature should be enabled.
+
+ \value ContentSecurityPolicyIgnored
+ Indicates that accesses to this scheme should bypass all
+ Content-Security-Policy checks.
+*/
+
+QWebEngineUrlScheme::QWebEngineUrlScheme(QWebEngineUrlSchemePrivate *d)
+ : d(d)
+{
+}
+
+/*!
+ Constructs a web engine URL scheme with default values.
+*/
+QWebEngineUrlScheme::QWebEngineUrlScheme()
+ : QWebEngineUrlScheme(QWebEngineUrlSchemePrivate::defaultConstructed())
+{
+}
+
+/*!
+ Constructs a web engine URL scheme with given \a name.
+*/
+QWebEngineUrlScheme::QWebEngineUrlScheme(const QByteArray &name)
+ : QWebEngineUrlScheme()
+{
+ setName(name);
+}
+
+/*!
+ Copies \a that.
+*/
+QWebEngineUrlScheme::QWebEngineUrlScheme(const QWebEngineUrlScheme &that) = default;
+
+/*!
+ Copies \a that.
+*/
+QWebEngineUrlScheme &QWebEngineUrlScheme::operator=(const QWebEngineUrlScheme &that) = default;
+
+/*!
+ Moves \a that.
+*/
+QWebEngineUrlScheme::QWebEngineUrlScheme(QWebEngineUrlScheme &&that) = default;
+
+/*!
+ Moves \a that.
+*/
+QWebEngineUrlScheme &QWebEngineUrlScheme::operator=(QWebEngineUrlScheme &&that) = default;
+
+/*!
+ Destructs this object.
+*/
+QWebEngineUrlScheme::~QWebEngineUrlScheme() = default;
+
+/*!
+ Returns \c true if this and \a that object are equal.
+*/
+bool QWebEngineUrlScheme::operator==(const QWebEngineUrlScheme &that)
+{
+ return (d == that.d)
+ || (d->name == that.d->name
+ && d->type == that.d->type
+ && d->default_port == that.d->default_port
+ && d->flags == that.d->flags);
+}
+
+/*!
+ \fn bool QWebEngineUrlScheme::operator!=(const QWebEngineUrlScheme &that)
+
+ Returns \c true if this and \a that object are not equal.
+*/
+
+/*!
+ Returns the name of this URL scheme.
+
+ The default value is an empty string.
+
+ \sa setName()
+*/
+QByteArray QWebEngineUrlScheme::name() const
+{
+ return QByteArray::fromStdString(d->name);
+}
+
+/*!
+ Sets the name of this URL scheme to \a newValue.
+
+ \note The name is automatically converted to lower case.
+
+ \sa name()
+*/
+void QWebEngineUrlScheme::setName(const QByteArray &newValue)
+{
+ d->name = newValue.toLower().toStdString();
+}
+
+/*!
+ Returns the syntax type of this URL scheme.
+
+ The default value is \c PathSyntax.
+
+ \sa Syntax, setSyntax()
+*/
+QWebEngineUrlScheme::Syntax QWebEngineUrlScheme::syntax() const
+{
+ return static_cast<Syntax>(d->type);
+}
+
+/*!
+ Sets the syntax type of this URL scheme to \a newValue.
+
+ \sa Syntax, syntax()
+*/
+void QWebEngineUrlScheme::setSyntax(Syntax newValue)
+{
+ d->type = static_cast<url::SchemeType>(newValue);
+}
+
+/*!
+ Returns the default port of this URL scheme.
+
+ The default value is \c PortUnspecified.
+
+ \sa setDefaultPort()
+*/
+int QWebEngineUrlScheme::defaultPort() const
+{
+ return d->default_port;
+}
+
+/*!
+ Sets the default port of this URL scheme to \a newValue.
+
+ \sa defaultPort()
+*/
+void QWebEngineUrlScheme::setDefaultPort(int newValue)
+{
+ d->default_port = newValue;
+}
+
+/*!
+ Returns the flags for this URL scheme.
+
+ The default value is an empty set of flags.
+
+ \sa Flags, setFlags()
+*/
+QWebEngineUrlScheme::Flags QWebEngineUrlScheme::flags() const
+{
+ return Flags(d->flags);
+}
+
+/*!
+ Sets the flags for this URL scheme to \a newValue.
+
+ \sa Flags, flags()
+*/
+void QWebEngineUrlScheme::setFlags(Flags newValue)
+{
+ d->flags = newValue;
+}
+
+/*!
+ Registers \a scheme with the web engine's URL parser and security model.
+
+ It is recommended that all custom URL schemes are first registered with this
+ function at application startup, even if the default options are to be used.
+
+ \warning This function must be called early at application startup, before
+ creating any WebEngine classes. Late calls will be ignored.
+
+ \sa findScheme()
+*/
+void QWebEngineUrlScheme::addScheme(const QWebEngineUrlScheme &scheme)
+{
+ url::CustomScheme::AddScheme(*scheme.d);
+}
+
+/*!
+ Returns the web engine URL scheme with the given \a name or the
+ default-constructed scheme.
+
+ \sa addScheme()
+*/
+QWebEngineUrlScheme QWebEngineUrlScheme::findScheme(const QByteArray &name)
+{
+ base::StringPiece namePiece{name.data(), static_cast<size_t>(name.size())};
+ if (const url::CustomScheme *cs = url::CustomScheme::FindScheme(namePiece))
+ return QWebEngineUrlScheme(new QWebEngineUrlSchemePrivate(*cs));
+ return QWebEngineUrlScheme();
+}
+
+QT_END_NAMESPACE
diff --git a/src/core/api/qwebengineurlscheme.h b/src/core/api/qwebengineurlscheme.h
new file mode 100644
index 000000000..ee5bf3c3c
--- /dev/null
+++ b/src/core/api/qwebengineurlscheme.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** 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:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWEBENGINEURLSCHEME_H
+#define QWEBENGINEURLSCHEME_H
+
+#include <QtWebEngineCore/qtwebenginecoreglobal.h>
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qshareddata.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWebEngineUrlSchemePrivate;
+
+class QWEBENGINECORE_EXPORT QWebEngineUrlScheme {
+public:
+ enum Syntax {
+ HostPortAndUserInformationSyntax,
+ HostAndPortSyntax,
+ HostSyntax,
+ PathSyntax,
+ };
+
+ enum SpecialPort {
+ PortUnspecified = -1
+ };
+
+ enum Flag {
+ Secure = 0x1,
+ Local = 0x2,
+ LocalAccessAllowed = 0x4,
+ NoAccessAllowed = 0x8,
+ ServiceWorkersAllowed = 0x10,
+ ViewSourceAllowed = 0x20,
+ ContentSecurityPolicyIgnored = 0x40,
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ QWebEngineUrlScheme();
+ QWebEngineUrlScheme(const QByteArray &name);
+
+ QWebEngineUrlScheme(const QWebEngineUrlScheme &that);
+ QWebEngineUrlScheme &operator=(const QWebEngineUrlScheme &that);
+
+ QWebEngineUrlScheme(QWebEngineUrlScheme &&that);
+ QWebEngineUrlScheme &operator=(QWebEngineUrlScheme &&that);
+
+ ~QWebEngineUrlScheme();
+
+ bool operator==(const QWebEngineUrlScheme &that);
+ bool operator!=(const QWebEngineUrlScheme &that) { return !(*this == that); }
+
+ QByteArray name() const;
+ void setName(const QByteArray &newValue);
+
+ Syntax syntax() const;
+ void setSyntax(Syntax newValue);
+
+ int defaultPort() const;
+ void setDefaultPort(int newValue);
+
+ Flags flags() const;
+ void setFlags(Flags newValue);
+
+ static void addScheme(const QWebEngineUrlScheme &scheme);
+ static QWebEngineUrlScheme findScheme(const QByteArray &name);
+
+private:
+ QWebEngineUrlScheme(QWebEngineUrlSchemePrivate *d);
+ QSharedDataPointer<QWebEngineUrlSchemePrivate> d;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QWebEngineUrlScheme::Flags)
+
+QT_END_NAMESPACE
+
+#endif // !QWEBENGINEURLSCHEME_H
diff --git a/src/core/content_browser_client_qt.cpp b/src/core/content_browser_client_qt.cpp
index 2d4ac3552..59d9a4a84 100644
--- a/src/core/content_browser_client_qt.cpp
+++ b/src/core/content_browser_client_qt.cpp
@@ -86,6 +86,7 @@
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_share_group.h"
#include "ui/gl/gpu_timing.h"
+#include "url/url_util_qt.h"
#include "service/service_qt.h"
#include "qtwebengine/grit/qt_webengine_resources.h"
@@ -575,6 +576,8 @@ void ContentBrowserClientQt::AppendExtraCommandLineSwitches(base::CommandLine* c
{
Q_UNUSED(child_process_id);
+ url::CustomScheme::SaveSchemes(command_line);
+
std::string processType = command_line->GetSwitchValueASCII(switches::kProcessType);
if (processType == switches::kZygoteProcess)
command_line->AppendSwitchASCII(switches::kLang, GetApplicationLocale());
@@ -585,12 +588,6 @@ void ContentBrowserClientQt::GetAdditionalWebUISchemes(std::vector<std::string>*
additional_schemes->push_back(content::kChromeDevToolsScheme);
}
-void ContentBrowserClientQt::GetAdditionalViewSourceSchemes(std::vector<std::string>* additional_schemes)
-{
- GetAdditionalWebUISchemes(additional_schemes);
- additional_schemes->push_back(kQrcSchemeQt);
-}
-
#if defined(Q_OS_LINUX)
void ContentBrowserClientQt::GetAdditionalMappedFilesForChildProcess(const base::CommandLine& command_line, int child_process_id, content::PosixFileDescriptorInfo* mappings)
{
diff --git a/src/core/content_browser_client_qt.h b/src/core/content_browser_client_qt.h
index a7f4313ea..842cf513d 100644
--- a/src/core/content_browser_client_qt.h
+++ b/src/core/content_browser_client_qt.h
@@ -112,7 +112,6 @@ public:
std::string GetApplicationLocale() override;
std::string GetAcceptLangs(content::BrowserContext* context) override;
void AppendExtraCommandLineSwitches(base::CommandLine* command_line, int child_process_id) override;
- void GetAdditionalViewSourceSchemes(std::vector<std::string>* additional_schemes) override;
void GetAdditionalWebUISchemes(std::vector<std::string>* additional_schemes) override;
void BindInterfaceRequestFromFrame(content::RenderFrameHost* render_frame_host,
diff --git a/src/core/content_client_qt.cpp b/src/core/content_client_qt.cpp
index 20d9a51fb..fe0ea6a0d 100644
--- a/src/core/content_client_qt.cpp
+++ b/src/core/content_client_qt.cpp
@@ -393,9 +393,4 @@ std::string ContentClientQt::GetProduct() const
return productName.toStdString();
}
-void ContentClientQt::AddAdditionalSchemes(Schemes* schemes)
-{
- schemes->secure_schemes.push_back(kQrcSchemeQt);
-}
-
} // namespace QtWebEngineCore
diff --git a/src/core/content_client_qt.h b/src/core/content_client_qt.h
index 35aa21968..fbafa6a4b 100644
--- a/src/core/content_client_qt.h
+++ b/src/core/content_client_qt.h
@@ -54,7 +54,6 @@ public:
#if QT_CONFIG(webengine_pepper_plugins)
void AddPepperPlugins(std::vector<content::PepperPluginInfo>* plugins) override;
#endif
- void AddAdditionalSchemes(Schemes* schemes) override;
void AddContentDecryptionModules(std::vector<content::CdmInfo> *cdms,
std::vector<media::CdmHostFilePath> *cdm_host_file_paths) override;
diff --git a/src/core/content_main_delegate_qt.cpp b/src/core/content_main_delegate_qt.cpp
index 666fa848f..3989170a0 100644
--- a/src/core/content_main_delegate_qt.cpp
+++ b/src/core/content_main_delegate_qt.cpp
@@ -53,6 +53,7 @@
#include <ui/base/webui/jstemplate_builder.h>
#include "net/grit/net_resources.h"
#include "net/base/net_module.h"
+#include "url/url_util_qt.h"
#include "content_client_qt.h"
#include "renderer/content_renderer_client_qt.h"
@@ -214,6 +215,9 @@ bool ContentMainDelegateQt::BasicStartupComplete(int *exit_code)
SafeOverridePath(base::DIR_APP_DICTIONARIES, WebEngineLibraryInfo::getPath(base::DIR_APP_DICTIONARIES));
#endif
SetContentClient(new ContentClientQt);
+
+ url::CustomScheme::LoadSchemes(base::CommandLine::ForCurrentProcess());
+
return false;
}
diff --git a/src/core/web_engine_context.cpp b/src/core/web_engine_context.cpp
index 23302e8d2..c691e0c6e 100644
--- a/src/core/web_engine_context.cpp
+++ b/src/core/web_engine_context.cpp
@@ -83,6 +83,7 @@
#include "content/public/app/sandbox_helper_win.h"
#endif // OS_WIN
+#include "api/qwebengineurlscheme.h"
#include "profile_adapter.h"
#include "content_browser_client_qt.h"
#include "content_client_qt.h"
@@ -322,6 +323,12 @@ WebEngineContext::WebEngineContext()
qputenv("force_s3tc_enable", "true");
#endif
+ QWebEngineUrlScheme qrcScheme(QByteArrayLiteral("qrc"));
+ qrcScheme.setFlags(QWebEngineUrlScheme::Secure
+ | QWebEngineUrlScheme::LocalAccessAllowed
+ | QWebEngineUrlScheme::ViewSourceAllowed);
+ QWebEngineUrlScheme::addScheme(qrcScheme);
+
// Allow us to inject javascript like any webview toolkit.
content::RenderFrameHost::AllowInjectingJavaScriptForAndroidWebView();
diff --git a/src/webengine/api/qquickwebengineprofile.cpp b/src/webengine/api/qquickwebengineprofile.cpp
index d11214716..48e5175ca 100644
--- a/src/webengine/api/qquickwebengineprofile.cpp
+++ b/src/webengine/api/qquickwebengineprofile.cpp
@@ -836,22 +836,26 @@ static bool checkInternalScheme(const QByteArray &scheme)
/*!
Registers a handler \a handler for custom URL scheme \a scheme in the profile.
+
+ It is recommended to first register the scheme with \l
+ QWebEngineUrlScheme::addScheme at application startup.
*/
void QQuickWebEngineProfile::installUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler)
{
Q_D(QQuickWebEngineProfile);
Q_ASSERT(handler);
- if (checkInternalScheme(scheme)) {
+ QByteArray canonicalScheme = scheme.toLower();
+ if (checkInternalScheme(canonicalScheme)) {
qWarning("Cannot install a URL scheme handler overriding internal scheme: %s", scheme.constData());
return;
}
- if (d->profileAdapter()->customUrlSchemeHandlers().contains(scheme)) {
- if (d->profileAdapter()->customUrlSchemeHandlers().value(scheme) != handler)
+ if (d->profileAdapter()->customUrlSchemeHandlers().contains(canonicalScheme)) {
+ if (d->profileAdapter()->customUrlSchemeHandlers().value(canonicalScheme) != handler)
qWarning("URL scheme handler already installed for the scheme: %s", scheme.constData());
return;
}
- d->profileAdapter()->addCustomUrlSchemeHandler(scheme, handler);
+ d->profileAdapter()->addCustomUrlSchemeHandler(canonicalScheme, handler);
connect(handler, SIGNAL(_q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)), this, SLOT(destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)));
}
diff --git a/src/webengine/doc/src/qtwebengine-features.qdoc b/src/webengine/doc/src/qtwebengine-features.qdoc
index d900e551b..41d0581e5 100644
--- a/src/webengine/doc/src/qtwebengine-features.qdoc
+++ b/src/webengine/doc/src/qtwebengine-features.qdoc
@@ -111,6 +111,19 @@
For more information, see \l {Qt WebEngine Debugging and Profiling}.
+ \section1 Custom Schemes
+
+ Qt WebEngine makes it possible for the application to define its own custom
+ URL schemes with specialized security policies and transport mechanisms.
+
+ Custom schemes can be used to implement alternative network protocols with
+ all the usual web security policies, privileged internal schemes for
+ displaying user interface compoments or debugging information, sandboxed
+ schemes with extra restrictions, and so on.
+
+ For more information, see \l QWebEngineUrlScheme and \l
+ QWebEngineUrlSchemeHandler.
+
\section1 Drag and Drop
Qt WebEngine supports HTML5 drag and drop.
diff --git a/src/webenginewidgets/api/qwebengineprofile.cpp b/src/webenginewidgets/api/qwebengineprofile.cpp
index 4523a425a..6969000a8 100644
--- a/src/webenginewidgets/api/qwebengineprofile.cpp
+++ b/src/webenginewidgets/api/qwebengineprofile.cpp
@@ -680,22 +680,26 @@ static bool checkInternalScheme(const QByteArray &scheme)
\since 5.6
Registers a handler \a handler for custom URL scheme \a scheme in the profile.
+
+ It is recommended to first register the scheme with \l
+ QWebEngineUrlScheme::addScheme at application startup.
*/
void QWebEngineProfile::installUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler)
{
Q_D(QWebEngineProfile);
Q_ASSERT(handler);
- if (checkInternalScheme(scheme)) {
+ QByteArray canonicalScheme = scheme.toLower();
+ if (checkInternalScheme(canonicalScheme)) {
qWarning("Cannot install a URL scheme handler overriding internal scheme: %s", scheme.constData());
return;
}
- if (d->profileAdapter()->customUrlSchemeHandlers().contains(scheme)) {
- if (d->profileAdapter()->customUrlSchemeHandlers().value(scheme) != handler)
+ if (d->profileAdapter()->customUrlSchemeHandlers().contains(canonicalScheme)) {
+ if (d->profileAdapter()->customUrlSchemeHandlers().value(canonicalScheme) != handler)
qWarning("URL scheme handler already installed for the scheme: %s", scheme.constData());
return;
}
- d->profileAdapter()->addCustomUrlSchemeHandler(scheme, handler);
+ d->profileAdapter()->addCustomUrlSchemeHandler(canonicalScheme, handler);
connect(handler, SIGNAL(_q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)), this, SLOT(destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)));
}
diff --git a/tests/auto/widgets/origins/origins.pro b/tests/auto/widgets/origins/origins.pro
index 566666e0a..b569547d0 100644
--- a/tests/auto/widgets/origins/origins.pro
+++ b/tests/auto/widgets/origins/origins.pro
@@ -1,2 +1,3 @@
include(../tests.pri)
CONFIG += c++14
+QT += websockets
diff --git a/tests/auto/widgets/origins/resources/mixed.html b/tests/auto/widgets/origins/resources/mixedSchemes.html
index c73e9ecdc..c73e9ecdc 100644
--- a/tests/auto/widgets/origins/resources/mixed.html
+++ b/tests/auto/widgets/origins/resources/mixedSchemes.html
diff --git a/tests/auto/widgets/origins/resources/mixedSchemesWithCsp.html b/tests/auto/widgets/origins/resources/mixedSchemesWithCsp.html
new file mode 100644
index 000000000..ad7cbeeb7
--- /dev/null
+++ b/tests/auto/widgets/origins/resources/mixedSchemesWithCsp.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="Content-Security-Policy" content="frame-src 'none'">
+ <title>Mixed</title>
+ <script>
+ var result;
+ var canary;
+
+ function setIFrameUrl(url) {
+ result = undefined;
+ canary = undefined;
+ document.getElementById("iframe").setAttribute("src", url);
+ // Early fire is OK unless the test is expecting cannotLoad.
+ // If timeout is too short then a false positive is possible.
+ setTimeout(() => { result = result || "cannotLoad"; }, 500);
+ }
+
+ addEventListener("load", function() {
+ document.getElementById("iframe").addEventListener("load", function() {
+ if (canary && window[0].canary)
+ result = "canLoadAndAccess";
+ else
+ result = "canLoadButNotAccess";
+ });
+ });
+ </script>
+ </head>
+ <body>
+ <iframe id="iframe"></iframe>
+ </body>
+</html>
diff --git a/tests/auto/widgets/origins/resources/mixed_frame.html b/tests/auto/widgets/origins/resources/mixedSchemes_frame.html
index 00c20ba37..00c20ba37 100644
--- a/tests/auto/widgets/origins/resources/mixed_frame.html
+++ b/tests/auto/widgets/origins/resources/mixedSchemes_frame.html
diff --git a/tests/auto/widgets/origins/resources/serviceWorker.html b/tests/auto/widgets/origins/resources/serviceWorker.html
index b2bdc8c60..27890c98f 100644
--- a/tests/auto/widgets/origins/resources/serviceWorker.html
+++ b/tests/auto/widgets/origins/resources/serviceWorker.html
@@ -5,9 +5,13 @@
<script>
var done = false;
var error;
- navigator.serviceWorker.register("serviceWorker.js")
- .then((r) => { done = true; })
- .catch((e) => { done = true; error = e.message; });
+ try {
+ navigator.serviceWorker.register("serviceWorker.js")
+ .then((r) => { done = true; })
+ .catch((e) => { done = true; error = e.message; });
+ } catch (e) {
+ done = true; error = e.message;
+ }
</script>
</head>
<body></body>
diff --git a/tests/auto/widgets/origins/resources/viewSource.html b/tests/auto/widgets/origins/resources/viewSource.html
new file mode 100644
index 000000000..977074c74
--- /dev/null
+++ b/tests/auto/widgets/origins/resources/viewSource.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>viewSource</title>
+ </head>
+ <body>
+ <p>viewSource</p>
+ </body>
+</html>
diff --git a/tests/auto/widgets/origins/resources/websocket.html b/tests/auto/widgets/origins/resources/websocket.html
index 949596d1c..31db66571 100644
--- a/tests/auto/widgets/origins/resources/websocket.html
+++ b/tests/auto/widgets/origins/resources/websocket.html
@@ -2,10 +2,21 @@
<html>
<head>
<title>WebSocket</title>
+ <script src="qrc:/qtwebchannel/qwebchannel.js"></script>
<script>
- var err;
- const ws = new WebSocket("ws://example.invalid");
- ws.addEventListener("close", () => err = event.code);
+ var result;
+ new QWebChannel(qt.webChannelTransport, channel => {
+ const ws = new WebSocket(channel.objects.echoServer.url);
+ ws.addEventListener("open", event => {
+ ws.send("ok");
+ });
+ ws.addEventListener("message", event => {
+ result = event.data;
+ });
+ ws.addEventListener("close", event => {
+ result = event.code;
+ });
+ })
</script>
</head>
<body></body>
diff --git a/tests/auto/widgets/origins/tst_origins.cpp b/tests/auto/widgets/origins/tst_origins.cpp
index 083975d71..19353e8bf 100644
--- a/tests/auto/widgets/origins/tst_origins.cpp
+++ b/tests/auto/widgets/origins/tst_origins.cpp
@@ -31,15 +31,98 @@
#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>
+#include <QtWebChannel/qwebchannel.h>
+#include <QtWebSockets/qwebsocket.h>
+#include <QtWebSockets/qwebsocketserver.h>
+#include <QtWidgets/qaction.h>
#define QSL QStringLiteral
#define QBAL QByteArrayLiteral
#define THIS_DIR TESTS_SOURCE_DIR "origins/"
+void registerSchemes()
+{
+ {
+ QWebEngineUrlScheme scheme(QBAL("PathSyntax"));
+ QWebEngineUrlScheme::addScheme(scheme);
+ }
+
+ {
+ QWebEngineUrlScheme scheme(QBAL("PathSyntax-Secure"));
+ scheme.setFlags(QWebEngineUrlScheme::Secure);
+ QWebEngineUrlScheme::addScheme(scheme);
+ }
+
+ {
+ QWebEngineUrlScheme scheme(QBAL("PathSyntax-Secure-ServiceWorkersAllowed"));
+ scheme.setFlags(QWebEngineUrlScheme::Secure | QWebEngineUrlScheme::ServiceWorkersAllowed);
+ QWebEngineUrlScheme::addScheme(scheme);
+ }
+
+ {
+ QWebEngineUrlScheme scheme(QBAL("PathSyntax-Local"));
+ scheme.setFlags(QWebEngineUrlScheme::Local);
+ QWebEngineUrlScheme::addScheme(scheme);
+ }
+
+ {
+ QWebEngineUrlScheme scheme(QBAL("PathSyntax-LocalAccessAllowed"));
+ scheme.setFlags(QWebEngineUrlScheme::LocalAccessAllowed);
+ QWebEngineUrlScheme::addScheme(scheme);
+ }
+
+ {
+ QWebEngineUrlScheme scheme(QBAL("PathSyntax-NoAccessAllowed"));
+ scheme.setFlags(QWebEngineUrlScheme::NoAccessAllowed);
+ QWebEngineUrlScheme::addScheme(scheme);
+ }
+
+ {
+ QWebEngineUrlScheme scheme(QBAL("PathSyntax-ServiceWorkersAllowed"));
+ scheme.setFlags(QWebEngineUrlScheme::ServiceWorkersAllowed);
+ QWebEngineUrlScheme::addScheme(scheme);
+ }
+
+ {
+ QWebEngineUrlScheme scheme(QBAL("PathSyntax-ViewSourceAllowed"));
+ scheme.setFlags(QWebEngineUrlScheme::ViewSourceAllowed);
+ QWebEngineUrlScheme::addScheme(scheme);
+ }
+
+ {
+ QWebEngineUrlScheme scheme(QBAL("HostSyntax"));
+ scheme.setSyntax(QWebEngineUrlScheme::HostSyntax);
+ QWebEngineUrlScheme::addScheme(scheme);
+ }
+
+ {
+ QWebEngineUrlScheme scheme(QBAL("HostSyntax-ContentSecurityPolicyIgnored"));
+ scheme.setSyntax(QWebEngineUrlScheme::HostSyntax);
+ scheme.setFlags(QWebEngineUrlScheme::ContentSecurityPolicyIgnored);
+ QWebEngineUrlScheme::addScheme(scheme);
+ }
+
+ {
+ QWebEngineUrlScheme scheme(QBAL("HostAndPortSyntax"));
+ scheme.setSyntax(QWebEngineUrlScheme::HostAndPortSyntax);
+ scheme.setDefaultPort(42);
+ QWebEngineUrlScheme::addScheme(scheme);
+ }
+
+ {
+ QWebEngineUrlScheme scheme(QBAL("HostPortAndUserInformationSyntax"));
+ scheme.setSyntax(QWebEngineUrlScheme::HostPortAndUserInformationSyntax);
+ scheme.setDefaultPort(42);
+ QWebEngineUrlScheme::addScheme(scheme);
+ }
+}
+Q_CONSTRUCTOR_FUNCTION(registerSchemes)
+
class TstUrlSchemeHandler final : public QWebEngineUrlSchemeHandler {
Q_OBJECT
@@ -47,6 +130,19 @@ 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);
}
private:
@@ -59,7 +155,10 @@ private:
job->fail(QWebEngineUrlRequestJob::RequestFailed);
return;
}
- job->reply(QBAL("text/html"), file);
+ QByteArray mimeType = QBAL("text/html");
+ if (pathSuffix.endsWith(QSL(".js")))
+ mimeType = QBAL("application/javascript");
+ job->reply(mimeType, file);
}
};
@@ -75,10 +174,12 @@ private Q_SLOTS:
void subdirWithAccess();
void subdirWithoutAccess();
void mixedSchemes();
+ void mixedSchemesWithCsp();
void webSocket();
void dedicatedWorker();
void sharedWorker();
void serviceWorker();
+ void viewSource();
private:
bool load(const QUrl &url)
@@ -136,17 +237,42 @@ void tst_Origins::jsUrlCanon()
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.
+ // 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 custom schemes.
+ // 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 origin serialization in Blink, implemented by blink::KURL and
@@ -169,9 +295,29 @@ void tst_Origins::jsUrlOrigin()
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.
+ // Same with unregistered 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://")));
+
+ // 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://")));
+
+ // The NoAccessAllowed flag forces opaque origins.
+ QCOMPARE(eval(QSL("new URL(\"PathSyntax-NoAccessAllowed:foo\").origin")),
+ QVariant(QSL("null")));
}
class ScopedAttribute {
@@ -246,53 +392,155 @@ void tst_Origins::subdirWithoutAccess()
// For file and qrc schemes, the iframe should load but it should not be
// possible for scripts in different frames to interact.
//
-// Additionally for custom schemes access to local content is forbidden, so it
-// should not be possible to load an iframe over the file: scheme.
+// 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(load(QSL("file:" THIS_DIR "resources/mixed.html")));
- eval(QSL("setIFrameUrl('file:" THIS_DIR "resources/mixed_frame.html')"));
+ QVERIFY(load(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(load(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(load(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(load(QSL("PathSyntax:/resources/mixedSchemes.html")));
+ eval(QSL("setIFrameUrl('PathSyntax:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess")));
- eval(QSL("setIFrameUrl('qrc:/resources/mixed_frame.html')"));
+ 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('tst:/resources/mixed_frame.html')"));
+ eval(QSL("setIFrameUrl('PathSyntax-NoAccessAllowed:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
- QVERIFY(load(QSL("qrc:/resources/mixed.html")));
- eval(QSL("setIFrameUrl('file:" THIS_DIR "resources/mixed_frame.html')"));
+ QVERIFY(load(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('qrc:/resources/mixed_frame.html')"));
+ eval(QSL("setIFrameUrl('PathSyntax-LocalAccessAllowed:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess")));
- eval(QSL("setIFrameUrl('tst:/resources/mixed_frame.html')"));
+ eval(QSL("setIFrameUrl('PathSyntax-NoAccessAllowed:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
- QVERIFY(load(QSL("tst:/resources/mixed.html")));
- eval(QSL("setIFrameUrl('file:" THIS_DIR "resources/mixed_frame.html')"));
+ QVERIFY(load(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('qrc:/resources/mixed_frame.html')"));
+ 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")));
- eval(QSL("setIFrameUrl('tst:/resources/mixed_frame.html')"));
+
+ QVERIFY(load(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(load(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(load(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")));
+}
+
+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()
{
- // 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;
+ const int kAbnormalClosure = 1006;
+
+ EchoServer echoServer;
+ QWebChannel channel;
+ channel.registerObject(QSL("echoServer"), &echoServer);
+ m_page->setWebChannel(&channel);
+ QVERIFY(echoServer.listen());
QVERIFY(load(QSL("file:" THIS_DIR "resources/websocket.html")));
- QTRY_COMPARE_WITH_TIMEOUT(eval(QSL("err")), QVariant(expected), 20000);
+ QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("ok")));
QVERIFY(load(QSL("qrc:/resources/websocket.html")));
- QTRY_COMPARE_WITH_TIMEOUT(eval(QSL("err")), QVariant(expected), 20000);
+ QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("ok")));
+ // Only registered schemes can open WebSockets.
QVERIFY(load(QSL("tst:/resources/websocket.html")));
- QTRY_VERIFY(eval(QSL("err")) == QVariant(expected));
+ QTRY_COMPARE(eval(QSL("result")), QVariant(kAbnormalClosure));
+
+ // Even an insecure registered scheme can open WebSockets.
+ QVERIFY(load(QSL("PathSyntax:/resources/websocket.html")));
+ QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("ok")));
}
// Create a (Dedicated)Worker. Since dedicated workers can only be accessed from
@@ -307,11 +555,22 @@ void tst_Origins::dedicatedWorker()
QTRY_VERIFY(eval(QSL("done")).toBool());
QCOMPARE(eval(QSL("result")), QVariant(42));
- // FIXME(juvaldma): QTBUG-62536
+ // Unregistered schemes cannot create Workers.
QVERIFY(load(QSL("tst:/resources/dedicatedWorker.html")));
QTRY_VERIFY(eval(QSL("done")).toBool());
QVERIFY(eval(QSL("error")).toString()
.contains(QSL("Access to dedicated workers is denied to origin 'tst://'")));
+
+ // Even an insecure registered scheme can create Workers.
+ QVERIFY(load(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(load(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,
@@ -337,12 +596,22 @@ void tst_Origins::sharedWorker()
QTRY_VERIFY(eval(QSL("done")).toBool());
QCOMPARE(eval(QSL("result")), QVariant(42));
+ // Even unregistered schemes can create SharedWorkers.
QVERIFY(load(QSL("tst:/resources/sharedWorker.html")));
QTRY_VERIFY(eval(QSL("done")).toBool());
QCOMPARE(eval(QSL("result")), QVariant(42));
+
+ QVERIFY(load(QSL("PathSyntax:/resources/sharedWorker.html")));
+ QTRY_VERIFY(eval(QSL("done")).toBool());
+ QCOMPARE(eval(QSL("result")), QVariant(42));
+
+ QVERIFY(load(QSL("PathSyntax-NoAccessAllowed:/resources/sharedWorker.html")));
+ QTRY_VERIFY(eval(QSL("done")).toBool());
+ QVERIFY(eval(QSL("error")).toString()
+ .contains(QSL("denied to origin 'null'")));
}
-// Service workers don't work.
+// Service workers have to be explicitly enabled for a scheme.
void tst_Origins::serviceWorker()
{
QVERIFY(load(QSL("file:" THIS_DIR "resources/serviceWorker.html")));
@@ -359,6 +628,53 @@ void tst_Origins::serviceWorker()
QTRY_VERIFY(eval(QSL("done")).toBool());
QVERIFY(eval(QSL("error")).toString()
.contains(QSL("Only secure origins are allowed")));
+
+ QVERIFY(load(QSL("PathSyntax:/resources/serviceWorker.html")));
+ QTRY_VERIFY(eval(QSL("done")).toBool());
+ QVERIFY(eval(QSL("error")).toString()
+ .contains(QSL("Only secure origins are allowed")));
+
+ QVERIFY(load(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(load(QSL("PathSyntax-ServiceWorkersAllowed:/resources/serviceWorker.html")));
+ QTRY_VERIFY(eval(QSL("done")).toBool());
+ QVERIFY(eval(QSL("error")).toString()
+ .contains(QSL("Only secure origins are allowed")));
+
+ QVERIFY(load(QSL("PathSyntax-Secure-ServiceWorkersAllowed:/resources/serviceWorker.html")));
+ QTRY_VERIFY(eval(QSL("done")).toBool());
+ QCOMPARE(eval(QSL("error")), QVariant());
+
+ QVERIFY(load(QSL("PathSyntax-NoAccessAllowed:/resources/serviceWorker.html")));
+ QTRY_VERIFY(eval(QSL("done")).toBool());
+ QVERIFY(eval(QSL("error")).toString()
+ .contains(QSL("denied in this document origin")));
+}
+
+// Support for view-source must be enabled explicitly.
+void tst_Origins::viewSource()
+{
+ QVERIFY(load(QSL("view-source:file:" THIS_DIR "resources/viewSource.html")));
+#ifdef Q_OS_WIN
+ QCOMPARE(m_page->requestedUrl(), QSL("file:///" THIS_DIR "resources/viewSource.html"));
+#else
+ QCOMPARE(m_page->requestedUrl(), QSL("file:" THIS_DIR "resources/viewSource.html"));
+#endif
+
+ QVERIFY(load(QSL("view-source:qrc:/resources/viewSource.html")));
+ QCOMPARE(m_page->requestedUrl(), QSL("qrc:/resources/viewSource.html"));
+
+ QVERIFY(load(QSL("view-source:tst:/resources/viewSource.html")));
+ QCOMPARE(m_page->requestedUrl(), QSL("about:blank"));
+
+ QVERIFY(load(QSL("view-source:PathSyntax:/resources/viewSource.html")));
+ QCOMPARE(m_page->requestedUrl(), QSL("about:blank"));
+
+ QVERIFY(load(QSL("view-source:PathSyntax-ViewSourceAllowed:/resources/viewSource.html")));
+ QCOMPARE(m_page->requestedUrl(), QSL("pathsyntax-viewsourceallowed:/resources/viewSource.html"));
}
QTEST_MAIN(tst_Origins)
diff --git a/tests/auto/widgets/origins/tst_origins.qrc b/tests/auto/widgets/origins/tst_origins.qrc
index 410cdc8a7..438fd10ee 100644
--- a/tests/auto/widgets/origins/tst_origins.qrc
+++ b/tests/auto/widgets/origins/tst_origins.qrc
@@ -3,8 +3,9 @@
<qresource>
<file>resources/dedicatedWorker.html</file>
<file>resources/dedicatedWorker.js</file>
- <file>resources/mixed.html</file>
- <file>resources/mixed_frame.html</file>
+ <file>resources/mixedSchemes.html</file>
+ <file>resources/mixedSchemesWithCsp.html</file>
+ <file>resources/mixedSchemes_frame.html</file>
<file>resources/serviceWorker.html</file>
<file>resources/serviceWorker.js</file>
<file>resources/sharedWorker.html</file>
@@ -12,6 +13,7 @@
<file>resources/subdir/frame2.html</file>
<file>resources/subdir/index.html</file>
<file>resources/subdir_frame1.html</file>
+ <file>resources/viewSource.html</file>
<file>resources/websocket.html</file>
</qresource>
</RCC>