summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJüri Valdmann <juri.valdmann@qt.io>2018-06-07 16:37:02 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-08-02 20:52:46 +0000
commit5d79af42e1e442ef67b3f1c87ae75d60a22ef9fc (patch)
treeddafad6255d4766b669c200b1ab3d60d11623efe
parent36cb71a4808456b6851c3fc7ddb2aa57bf286117 (diff)
Add QWebEngineUrlScheme class
Public API for the new url/url_util_qt extension to Chromium, which allows to integrate custom schemes into Chromium's url parsing library and security model. Previously custom schemes would be treated as 'unknown' schemes and rely on fallback behavior in Chromium. [ChangeLog][Custom Schemes] Added the QWebEngineUrlScheme class for configuring how custom schemes are parsed and which security restrictions should apply. Task-number: QTBUG-62536 Change-Id: I7d8b9da3ad742f568b82ccc6a2456ad35e84069b Reviewed-by: Michal Klocek <michal.klocek@qt.io>
-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>