diff options
| author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-02-15 16:14:58 +0100 |
|---|---|---|
| committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-02-15 16:14:58 +0100 |
| commit | 14988cd6b243986f501edbba678796ea5efe2c28 (patch) | |
| tree | c2555af8f13b5336047e17548092bf1590f81576 | |
| parent | f17f815878d1d3f02f464347d2807555cab00a88 (diff) | |
| parent | c69f510ed6e85c64f02ee0ee030336cd9aa66793 (diff) | |
| download | qtwebengine-14988cd6b243986f501edbba678796ea5efe2c28.tar.gz | |
Merge branch '5.13' into dev
Change-Id: I06d0cb28448768f16c886b96fec1da4a1b82e1d7
105 files changed, 3067 insertions, 802 deletions
diff --git a/dist/changes-5.12.1 b/dist/changes-5.12.1 new file mode 100644 index 000000000..44dfdd5c7 --- /dev/null +++ b/dist/changes-5.12.1 @@ -0,0 +1,78 @@ +Qt 5.12.1 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.12.0. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +http://doc.qt.io/qt-5/index.html + +The Qt version 5.12 series is binary compatible with the 5.11.x series. +Applications compiled for 5.11 will continue to run with 5.12. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Qt 5.12.1 Changes * +**************************************************************************** + +Chromium +-------- + + - Security fixes from Chromium up to version 71.0.3578.94, including: + * CVE-2018-17480 + * CVE-2018-17481 + * CVE-2018-18335 + * CVE-2018-18336 + * CVE-2018-18337 + * CVE-2018-18338 + * CVE-2018-18339 + * CVE-2018-18340 + * CVE-2018-18341 + * CVE-2018-18342 + * CVE-2018-18343 + * CVE-2018-18345 + * CVE-2018-18347 + * CVE-2018-18349 + * CVE-2018-18350 + * CVE-2018-18355 + * CVE-2018-18356 + * CVE-2018-18357 + * CVE-2018-18358 + * CVE-2018-18359 + * Security Bug 839250 + * Security Bug 851821 + * Security Bug 869067 + * Security Bug 877843 + * Security Bug 879965 + * Security Bug 880665 + * Security Bug 882270 + * Security Bug 892643 + * Security Bug 895885 + * Security Bug 900910 + - Fixed x86-32 builds on GCC 8. + - Fixed building on ARM hosts. + + +General +------- + + * Enabled idle detection and sleep inhibition on Linux and macOS. + * [QTBUG-66262] Improved suggested file name of downloads when conflicts arise. + * [QTBUG-71322] Enabled bilinear filtering of textures in software rendering mode. + * [QTBUG-71370] Fixed autoscrolling regression. + * [QTBUG-71895] Fixed crash when calling clearHttpCache(). + * [QTBUG-72260] Fixed crash on requestInterceptor changes. + * [QTBUG-72654] Fixed Event.timeStamp being 0. + * [QTBUG-72655] Fixed some JS properties of dead key event. + + +QtQuickWebEngine +---------------- + + * [QTBUG-72299] Fixed problem with blank view after reuse. diff --git a/examples/webenginewidgets/simplebrowser/browserwindow.cpp b/examples/webenginewidgets/simplebrowser/browserwindow.cpp index 4351dfe71..5d00cd19a 100644 --- a/examples/webenginewidgets/simplebrowser/browserwindow.cpp +++ b/examples/webenginewidgets/simplebrowser/browserwindow.cpp @@ -62,6 +62,7 @@ #include <QMenuBar> #include <QMessageBox> #include <QProgressBar> +#include <QScreen> #include <QStatusBar> #include <QToolBar> #include <QVBoxLayout> @@ -143,7 +144,7 @@ BrowserWindow::BrowserWindow(Browser *browser, QWebEngineProfile *profile, bool QSize BrowserWindow::sizeHint() const { - QRect desktopRect = QApplication::desktop()->screenGeometry(); + QRect desktopRect = QApplication::primaryScreen()->geometry(); QSize size = desktopRect.size() * qreal(0.9); return size; } diff --git a/examples/webenginewidgets/simplebrowser/webpage.cpp b/examples/webenginewidgets/simplebrowser/webpage.cpp index e44410284..99849c77d 100644 --- a/examples/webenginewidgets/simplebrowser/webpage.cpp +++ b/examples/webenginewidgets/simplebrowser/webpage.cpp @@ -138,7 +138,7 @@ inline QString questionForFeature(QWebEnginePage::Feature feature) case QWebEnginePage::DesktopAudioVideoCapture: return WebPage::tr("Allow %1 to capture audio and video of your desktop?"); case QWebEnginePage::Notifications: - return QString(); + return WebPage::tr("Allow %1 to show notification on your desktop?"); } return QString(); } diff --git a/src/3rdparty b/src/3rdparty -Subproject 5a76a0a325a041dab3c93943c99835d3e929762 +Subproject 8d510183ca27142801b56bb50d63cbb2a92d433 diff --git a/src/core/api/core_api.pro b/src/core/api/core_api.pro index b5bb93847..4d3ddcc8e 100644 --- a/src/core/api/core_api.pro +++ b/src/core/api/core_api.pro @@ -38,6 +38,7 @@ HEADERS = \ qwebenginecookiestore.h \ qwebenginecookiestore_p.h \ qwebenginehttprequest.h \ + qwebenginenotification.h \ qwebenginequotarequest.h \ qwebengineregisterprotocolhandlerrequest.h \ qwebengineurlrequestinterceptor.h \ @@ -49,8 +50,10 @@ HEADERS = \ SOURCES = \ qtwebenginecoreglobal.cpp \ + qwebengineclientcertificatestore.cpp \ qwebenginecookiestore.cpp \ qwebenginehttprequest.cpp \ + qwebenginenotification.cpp \ qwebenginequotarequest.cpp \ qwebengineregisterprotocolhandlerrequest.cpp \ qwebengineurlrequestinfo.cpp \ diff --git a/src/core/qwebengineclientcertificatestore.cpp b/src/core/api/qwebengineclientcertificatestore.cpp index 08f4389d7..850dd16d7 100644 --- a/src/core/qwebengineclientcertificatestore.cpp +++ b/src/core/api/qwebengineclientcertificatestore.cpp @@ -37,34 +37,9 @@ ** ****************************************************************************/ -#include "api/qwebengineclientcertificatestore.h" -#include "client_cert_override_key_p.h" -#include "client_cert_override_p.h" +#include "qwebengineclientcertificatestore.h" -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/task/post_task.h" -#include "base/callback_forward.h" - -#include "net/ssl/client_cert_store.h" -#include "net/ssl/ssl_cert_request_info.h" -#include "net/cert/x509_certificate.h" - -#include "third_party/boringssl/src/include/openssl/pem.h" -#include "third_party/boringssl/src/include/openssl/err.h" -#include "third_party/boringssl/src/include/openssl/evp.h" - -#if defined(USE_NSS_CERTS) -#include "net/ssl/client_cert_store_nss.h" -#endif - -#if defined(OS_WIN) -#include "net/ssl/client_cert_store_win.h" -#endif - -#if defined(OS_MACOSX) -#include "net/ssl/client_cert_store_mac.h" -#endif +#include "net/client_cert_store_data.h" #include <QByteArray> #include <QList> @@ -73,19 +48,7 @@ QT_BEGIN_NAMESPACE #if QT_CONFIG(ssl) -typedef struct OverrideData { - QSslKey key; - QSslCertificate certificate; - scoped_refptr<net::X509Certificate> certPtr; - scoped_refptr<net::SSLPrivateKey> keyPtr; -} OverrideData; - -struct QWebEngineClientCertificateStoreData { - QList<OverrideData*> deletedCerts; -}; - -static QList<OverrideData*> ClientCertOverrideData; -QWebEngineClientCertificateStore *QWebEngineClientCertificateStore::m_instance = NULL; +QWebEngineClientCertificateStore *QWebEngineClientCertificateStore::m_instance = nullptr; /*! \class QWebEngineClientCertificateStore::Entry @@ -107,8 +70,8 @@ QWebEngineClientCertificateStore *QWebEngineClientCertificateStore::m_instance = */ QWebEngineClientCertificateStore::QWebEngineClientCertificateStore() + : d_ptr(new QtWebEngineCore::ClientCertificateStoreData) { - this->d_ptr = new QWebEngineClientCertificateStoreData; } /*! @@ -119,9 +82,6 @@ QWebEngineClientCertificateStore::~QWebEngineClientCertificateStore() { // Just in case user has not deleted in-memory certificates clear(); - - qDeleteAll(d_ptr->deletedCerts); - delete d_ptr; } /*! @@ -141,17 +101,7 @@ QWebEngineClientCertificateStore *QWebEngineClientCertificateStore::getInstance( void QWebEngineClientCertificateStore::add(const QSslCertificate &certificate, const QSslKey &privateKey) { - - QByteArray sslKeyInBytes = privateKey.toPem(); - QByteArray certInBytes = certificate.toDer(); - - OverrideData* data = new OverrideData; - data->keyPtr = net::WrapOpenSSLPrivateKey(sslKeyInBytes); - data->certPtr = net::X509Certificate::CreateFromBytes( - certInBytes.data(), certInBytes.length()); - data->key = privateKey; - data->certificate = certificate; - ClientCertOverrideData.append(data); + d_ptr->add(certificate, privateKey); } /*! @@ -162,7 +112,7 @@ void QWebEngineClientCertificateStore::add(const QSslCertificate &certificate, c QList<QWebEngineClientCertificateStore::Entry> QWebEngineClientCertificateStore::toList() const { QList<Entry> certificateList; - for (auto data : ClientCertOverrideData) { + for (auto data : qAsConst(d_ptr->addedCerts)) { Entry entry; entry.certificate = data->certificate; entry.privateKey = data->key; @@ -178,13 +128,15 @@ QList<QWebEngineClientCertificateStore::Entry> QWebEngineClientCertificateStore: void QWebEngineClientCertificateStore::remove(Entry entry) { - QMutableListIterator<OverrideData*> iterator(ClientCertOverrideData); - while (iterator.hasNext()) { - auto overrideData = iterator.next(); + auto it = d_ptr->addedCerts.begin(); + while (it != d_ptr->addedCerts.end()) { + auto *overrideData = *it; if (entry.certificate.toDer() == overrideData->certificate.toDer()) { d_ptr->deletedCerts.append(overrideData); - iterator.remove(); + it = d_ptr->addedCerts.erase(it); + continue; } + ++it; } } @@ -194,95 +146,10 @@ void QWebEngineClientCertificateStore::remove(Entry entry) void QWebEngineClientCertificateStore::clear() { - for (auto data : ClientCertOverrideData) - d_ptr->deletedCerts.append(data); - ClientCertOverrideData.clear(); + d_ptr->deletedCerts.append(d_ptr->addedCerts); + d_ptr->addedCerts.clear(); } #endif // QT_CONFIG(ssl) QT_END_NAMESPACE - -namespace net { - -namespace { - -class ClientCertIdentityOverride : public ClientCertIdentity { -public: - ClientCertIdentityOverride( - scoped_refptr<net::X509Certificate> cert, - scoped_refptr<net::SSLPrivateKey> key) - : ClientCertIdentity(std::move(cert)), - key_(std::move(key)) {} - ~ClientCertIdentityOverride() override = default; - - void AcquirePrivateKey( - const base::Callback<void(scoped_refptr<SSLPrivateKey>)>& - private_key_callback) override - { - private_key_callback.Run(key_); - } - -#if defined(OS_MACOSX) - SecIdentityRef sec_identity_ref() const override - { - return nullptr; - } -#endif - -private: - scoped_refptr<net::SSLPrivateKey> key_; -}; - -} // namespace - - -ClientCertOverrideStore::ClientCertOverrideStore() - : ClientCertStore() -{ -} - -ClientCertOverrideStore::~ClientCertOverrideStore() -{ -} - -void ClientCertOverrideStore::GetClientCerts(const SSLCertRequestInfo &cert_request_info, - const ClientCertListCallback &callback) -{ -#if QT_CONFIG(ssl) - // Look for certificates in memory store - for (int i = 0; i < ClientCertOverrideData.length(); i++) { - scoped_refptr<net::X509Certificate> cert = ClientCertOverrideData[i]->certPtr; - if (cert != NULL && cert->IsIssuedByEncoded(cert_request_info.cert_authorities)) { - ClientCertIdentityList selected_identities; - selected_identities.push_back(std::make_unique<ClientCertIdentityOverride>(cert, ClientCertOverrideData[i]->keyPtr)); - callback.Run(std::move(selected_identities)); - return; - } - } -#endif // QT_CONFIG(ssl) - - // Continue with native cert store if matching certificate is not found in memory - std::unique_ptr<net::ClientCertStore> store = getNativeStore(); - if (store != NULL) { - store->GetClientCerts(cert_request_info, callback); - return; - } - - callback.Run(ClientCertIdentityList()); - return; -} - -std::unique_ptr<net::ClientCertStore> ClientCertOverrideStore::getNativeStore() -{ -#if defined(USE_NSS_CERTS) - return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreNSS(net::ClientCertStoreNSS::PasswordDelegateFactory())); -#elif defined(OS_WIN) - return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreWin()); -#elif defined(OS_MACOSX) - return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreMac()); -#else - return nullptr; -#endif -} -} diff --git a/src/core/api/qwebengineclientcertificatestore.h b/src/core/api/qwebengineclientcertificatestore.h index 6ba998465..c0bd66e2b 100644 --- a/src/core/api/qwebengineclientcertificatestore.h +++ b/src/core/api/qwebengineclientcertificatestore.h @@ -42,14 +42,19 @@ #include <QtWebEngineCore/qtwebenginecoreglobal.h> +#include <QtCore/qscopedpointer.h> #include <QtNetwork/qsslcertificate.h> #include <QtNetwork/qsslkey.h> +namespace QtWebEngineCore { +class ClientCertOverrideStore; +struct ClientCertificateStoreData; +} + QT_BEGIN_NAMESPACE #if QT_CONFIG(ssl) -struct QWebEngineClientCertificateStoreData; class QWEBENGINECORE_EXPORT QWebEngineClientCertificateStore { @@ -66,12 +71,13 @@ public: void clear(); private: + friend class QtWebEngineCore::ClientCertOverrideStore; static QWebEngineClientCertificateStore *m_instance; Q_DISABLE_COPY(QWebEngineClientCertificateStore) QWebEngineClientCertificateStore(); ~QWebEngineClientCertificateStore(); - QWebEngineClientCertificateStoreData *d_ptr; + QScopedPointer<QtWebEngineCore::ClientCertificateStoreData> d_ptr; }; #endif // QT_CONFIG(ssl) diff --git a/src/core/api/qwebenginenotification.cpp b/src/core/api/qwebenginenotification.cpp new file mode 100644 index 000000000..0b91cf273 --- /dev/null +++ b/src/core/api/qwebenginenotification.cpp @@ -0,0 +1,275 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "qwebenginenotification.h" + +#include "user_notification_controller.h" + +#include <QExplicitlySharedDataPointer> + +QT_BEGIN_NAMESPACE + +using QtWebEngineCore::UserNotificationController; + +/*! + \class QWebEngineNotification + \brief The QWebEngineNotification class encapsulates the data of an HTML5 web notification. + \since 5.13 + + \inmodule QtWebEngineCore + + This class contains the information and API for HTML5 desktop and push notifications. +*/ + +class QWebEngineNotificationPrivate : public UserNotificationController::Client { +public: + QWebEngineNotificationPrivate(QWebEngineNotification *q, const QSharedPointer<UserNotificationController> &controller) + : controller(controller) + , q(q) + { + controller->setClient(this); + } + ~QWebEngineNotificationPrivate() override + { + if (controller->client() == this) + controller->setClient(0); + } + + // UserNotificationController::Client: + virtual void notificationClosed(const UserNotificationController *) Q_DECL_OVERRIDE + { + Q_EMIT q->closed(); + } + + QSharedPointer<UserNotificationController> controller; + QWebEngineNotification *q; +}; + + +/*! + Creates a null QWebEngineNotification. + + \sa isNull() +*/ +QWebEngineNotification::QWebEngineNotification() { } + +/*! \internal +*/ +QWebEngineNotification::QWebEngineNotification(const QSharedPointer<UserNotificationController> &controller) + : d_ptr(new QWebEngineNotificationPrivate(this, controller)) +{ } + +/*! \internal +*/ +QWebEngineNotification::QWebEngineNotification(const QWebEngineNotification &other) + : QObject() + , d_ptr(new QWebEngineNotificationPrivate(this, other.d_ptr->controller)) +{ } + +/*! \internal +*/ +QWebEngineNotification::~QWebEngineNotification() +{ +} + +/*! \internal +*/ +const QWebEngineNotification &QWebEngineNotification::operator=(const QWebEngineNotification &other) +{ + d_ptr.reset(new QWebEngineNotificationPrivate(this, other.d_ptr->controller)); + return *this; +} + +/*! + Returns \c true if the two notifications belong to the same message chain. + That is, if their tag() and origin() are the same. This means one is + a replacement or an update of the \a other. + + \sa tag(), origin() +*/ +bool QWebEngineNotification::matches(const QWebEngineNotification &other) const +{ + if (!d_ptr) + return !other.d_ptr; + if (!other.d_ptr) + return false; + return tag() == other.tag() && origin() == other.origin(); +} + +/*! + \property QWebEngineNotification::title + \brief The title of the notification. + \sa message() +*/ +QString QWebEngineNotification::title() const +{ + Q_D(const QWebEngineNotification); + return d ? d->controller->title() : QString(); +} + +/*! + \property QWebEngineNotification::message + \brief The body of the notification message. + \sa title() +*/ + +QString QWebEngineNotification::message() const +{ + Q_D(const QWebEngineNotification); + return d ? d->controller->body() : QString(); +} + +/*! + \property QWebEngineNotification::tag + \brief The tag of the notification message. + + New notifications that have the same tag and origin URL as an existing + one should replace or update the old notification with the same tag. + + \sa matches() +*/ +QString QWebEngineNotification::tag() const +{ + Q_D(const QWebEngineNotification); + return d ? d->controller->tag() : QString(); +} + +/*! + \property QWebEngineNotification::origin + \brief The URL of the page sending the notification. +*/ + +QUrl QWebEngineNotification::origin() const +{ + Q_D(const QWebEngineNotification); + return d ? d->controller->origin() : QUrl(); +} + +/*! + \property QWebEngineNotification::icon + \brief The icon to be shown with the notification. + + If no icon is set by the sender, an null QIcon is returned. +*/ +QIcon QWebEngineNotification::icon() const +{ + Q_D(const QWebEngineNotification); + return d ? d->controller->icon() : QIcon(); +} + +/*! + \property QWebEngineNotification::language + \brief The primary language for the notification's title and body. + + Its value is a valid BCP 47 language tag, or the empty string. + + \sa title(), message() +*/ +QString QWebEngineNotification::language() const +{ + Q_D(const QWebEngineNotification); + return d ? d->controller->language() : QString(); +} + +/*! + \property QWebEngineNotification::direction + \brief The text direction for the notification's title and body. + \sa title(), message() +*/ +QWebEngineNotification::Direction QWebEngineNotification::direction() const +{ + Q_D(const QWebEngineNotification); + return d ? static_cast<Direction>(d->controller->direction()) : DirectionAuto; +} + +/*! + Returns \c true if the notification is a default constructed null notification. +*/ +bool QWebEngineNotification::isNull() const +{ + return d_ptr.isNull(); +} + +/*! + Creates and dispatches a JavaScript \e {show event} on notification. + + Should be called by the notification platform when the notification has been shown to user. +*/ +void QWebEngineNotification::show() const +{ + Q_D(const QWebEngineNotification); + if (d) + d->controller->notificationDisplayed(); +} + +/*! + Creates and dispatches a JavaScript \e {click event} on notification. + + Should be called by the notification platform when the notification is activated by the user. +*/ +void QWebEngineNotification::click() const +{ + Q_D(const QWebEngineNotification); + if (d) + d->controller->notificationClicked(); +} + +/*! + Creates and dispatches a JavaScript \e {close event} on notification. + + Should be called by the notification platform when the notification is closed, + either by the underlying platform or by the user. +*/ +void QWebEngineNotification::close() const +{ + Q_D(const QWebEngineNotification); + if (d) + d->controller->notificationClosed(); +} + +/*! + \fn void QWebEngineNotification::closed() + + This signal is emitted when the web page calls close steps for the notification, + and it no longer needs to be shown. +*/ + +QT_END_NAMESPACE + +#include "moc_qwebenginenotification.cpp" diff --git a/src/core/api/qwebenginenotification.h b/src/core/api/qwebenginenotification.h new file mode 100644 index 000000000..b6b7414f9 --- /dev/null +++ b/src/core/api/qwebenginenotification.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 QWEBENGINENOTIFICATION_H +#define QWEBENGINENOTIFICATION_H + +#include <QtWebEngineCore/qtwebenginecoreglobal.h> + +#include <QtCore/QObject> +#include <QtCore/QScopedPointer> +#include <QtCore/QSharedPointer> +#include <QtCore/QUrl> +#include <QtGui/QIcon> + +namespace QtWebEngineCore { +class UserNotificationController; +} + +QT_BEGIN_NAMESPACE + +class QWebEngineNotificationPrivate; + +class QWEBENGINECORE_EXPORT QWebEngineNotification : public QObject { + Q_OBJECT + Q_PROPERTY(QUrl origin READ origin CONSTANT FINAL) + Q_PROPERTY(QIcon icon READ icon CONSTANT FINAL) + Q_PROPERTY(QString title READ title CONSTANT FINAL) + Q_PROPERTY(QString message READ message CONSTANT FINAL) + Q_PROPERTY(QString tag READ tag CONSTANT FINAL) + Q_PROPERTY(QString language READ language CONSTANT FINAL) + Q_PROPERTY(Direction direction READ direction CONSTANT FINAL) + +public: + QWebEngineNotification(); + QWebEngineNotification(const QWebEngineNotification &); + virtual ~QWebEngineNotification(); + const QWebEngineNotification &operator=(const QWebEngineNotification &); + + enum Direction { + LeftToRight = Qt::LeftToRight, + RightToLeft = Qt::RightToLeft, + DirectionAuto = Qt::LayoutDirectionAuto + }; + Q_ENUM(Direction) + + bool matches(const QWebEngineNotification &) const; + + QUrl origin() const; + QIcon icon() const; + QString title() const; + QString message() const; + QString tag() const; + QString language() const; + Direction direction() const; + + bool isNull() const; + +public Q_SLOTS: + void show() const; + void click() const; + void close() const; + +Q_SIGNALS: + void closed(); + +private: + QWebEngineNotification(const QSharedPointer<QtWebEngineCore::UserNotificationController> &); + Q_DECLARE_PRIVATE(QWebEngineNotification) + QScopedPointer<QWebEngineNotificationPrivate> d_ptr; + friend class QQuickWebEngineProfilePrivate; + friend class QWebEngineProfilePrivate; +}; + +QT_END_NAMESPACE + +#endif // QWEBENGINENOTIFICATION_H diff --git a/src/core/api/qwebengineurlrequestinfo.cpp b/src/core/api/qwebengineurlrequestinfo.cpp index c3e5b5445..dc2d07740 100644 --- a/src/core/api/qwebengineurlrequestinfo.cpp +++ b/src/core/api/qwebengineurlrequestinfo.cpp @@ -96,8 +96,8 @@ ASSERT_ENUMS_MATCH(QtWebEngineCore::WebContentsAdapterClient::OtherNavigation, Q interceptor on the profile enables intercepting, blocking, and modifying URL requests before they reach the networking stack of Chromium. - You can install the interceptor on a profile via QWebEngineProfile::setRequestInterceptor() - or QQuickWebEngineProfile::setRequestInterceptor(). + You can install the interceptor on a profile via QWebEngineProfile::setUrlRequestInterceptor() + or QQuickWebEngineProfile::setUrlRequestInterceptor(). When using the \l{Qt WebEngine Widgets Module}, \l{QWebEnginePage::acceptNavigationRequest()} offers further options to accept or block requests. @@ -115,11 +115,7 @@ ASSERT_ENUMS_MATCH(QtWebEngineCore::WebContentsAdapterClient::OtherNavigation, Q \fn void QWebEngineUrlRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info) Reimplementing this virtual function makes it possible to intercept URL - requests. For interceptors installed on a QWebEngineProfile, the function is executed - on the I/O thread, and thus it may not be thread-safe to interact with pages. If the - interceptor was installed on a QWebEnginePage, the function is executed on the main - application thread, and can safely interact with other user classes. Both versions will - be stalling the URL request until handled. + requests. This method will be stalling the URL request until handled. \a info contains the information about the URL request and will track internally whether its members have been altered. diff --git a/src/core/api/qwebengineurlrequestinterceptor.h b/src/core/api/qwebengineurlrequestinterceptor.h index dc2a15ee3..2b07681ca 100644 --- a/src/core/api/qwebengineurlrequestinterceptor.h +++ b/src/core/api/qwebengineurlrequestinterceptor.h @@ -55,7 +55,7 @@ class QWEBENGINECORE_EXPORT QWebEngineUrlRequestInterceptor : public QObject Q_OBJECT Q_DISABLE_COPY(QWebEngineUrlRequestInterceptor) public: - explicit QWebEngineUrlRequestInterceptor(QObject *p = Q_NULLPTR) + explicit QWebEngineUrlRequestInterceptor(QObject *p = nullptr) : QObject (p) { } diff --git a/src/core/api/qwebengineurlscheme.cpp b/src/core/api/qwebengineurlscheme.cpp index d63599163..9f7288f2d 100644 --- a/src/core/api/qwebengineurlscheme.cpp +++ b/src/core/api/qwebengineurlscheme.cpp @@ -39,7 +39,9 @@ #include "qwebengineurlscheme.h" -#include <url/url_util_qt.h> +#include "url/url_util_qt.h" + +#include <QtDebug> QT_BEGIN_NAMESPACE @@ -59,6 +61,8 @@ ASSERT_ENUMS_MATCH(QWebEngineUrlScheme::ServiceWorkersAllowed, url::CustomScheme ASSERT_ENUMS_MATCH(QWebEngineUrlScheme::ViewSourceAllowed, url::CustomScheme::ViewSourceAllowed) ASSERT_ENUMS_MATCH(QWebEngineUrlScheme::ContentSecurityPolicyIgnored, url::CustomScheme::ContentSecurityPolicyIgnored) +static bool g_schemesLocked = false; + class QWebEngineUrlSchemePrivate : public QSharedData , public url::CustomScheme { @@ -357,6 +361,28 @@ void QWebEngineUrlScheme::setFlags(Flags newValue) */ void QWebEngineUrlScheme::registerScheme(const QWebEngineUrlScheme &scheme) { + if (scheme.d->name.empty()) { + qWarning() << "QWebEngineUrlScheme::registerScheme: Scheme name cannot be empty"; + return; + } + + bool needsPort = scheme.d->has_port_component(); + bool hasPort = scheme.d->default_port != url::PORT_UNSPECIFIED; + if (needsPort && !hasPort) { + qWarning() << "QWebEngineUrlScheme::registerScheme: Scheme" << scheme.name() << "needs a default port"; + return; + } + + if (url::CustomScheme::FindScheme(scheme.d->name)) { + qWarning() << "QWebEngineUrlScheme::registerScheme: Scheme" << scheme.name() << "already registered"; + return; + } + + if (g_schemesLocked) { + qWarning() << "QWebEngineUrlScheme::registerScheme: Too late to register scheme" << scheme.name(); + return; + } + url::CustomScheme::AddScheme(*scheme.d); } @@ -374,4 +400,9 @@ QWebEngineUrlScheme QWebEngineUrlScheme::schemeByName(const QByteArray &name) return QWebEngineUrlScheme(); } +void QWebEngineUrlScheme::lockSchemes() +{ + g_schemesLocked = true; +} + QT_END_NAMESPACE diff --git a/src/core/api/qwebengineurlscheme.h b/src/core/api/qwebengineurlscheme.h index 88a8f5065..da3010335 100644 --- a/src/core/api/qwebengineurlscheme.h +++ b/src/core/api/qwebengineurlscheme.h @@ -46,6 +46,8 @@ #include <QtCore/qobjectdefs.h> #include <QtCore/qshareddata.h> +namespace QtWebEngineCore { class WebEngineContext; } + QT_BEGIN_NAMESPACE class QWebEngineUrlSchemePrivate; @@ -106,6 +108,8 @@ public: static QWebEngineUrlScheme schemeByName(const QByteArray &name); private: + friend QtWebEngineCore::WebEngineContext; + static void lockSchemes(); QWebEngineUrlScheme(QWebEngineUrlSchemePrivate *d); QSharedDataPointer<QWebEngineUrlSchemePrivate> d; }; diff --git a/src/core/chromium_overrides.cpp b/src/core/chromium_overrides.cpp index c25d6dda9..09058301b 100644 --- a/src/core/chromium_overrides.cpp +++ b/src/core/chromium_overrides.cpp @@ -37,8 +37,6 @@ ** ****************************************************************************/ -#include "chromium_overrides.h" - #include "ozone/gl_context_qt.h" #include "qtwebenginecoreglobal_p.h" #include "web_contents_view_qt.h" @@ -72,27 +70,6 @@ #include "net/ssl/openssl_client_key_store.h" #endif -namespace QtWebEngineCore { -void GetScreenInfoFromNativeWindow(QWindow* window, content::ScreenInfo* results) -{ - QScreen* screen = window->screen(); - if (!screen) - return; - content::ScreenInfo r; - r.device_scale_factor = screen->devicePixelRatio(); - r.depth_per_component = 8; - r.depth = screen->depth(); - r.is_monochrome = (r.depth == 1); - - QRect screenGeometry = screen->geometry(); - r.rect = gfx::Rect(screenGeometry.x(), screenGeometry.y(), screenGeometry.width(), screenGeometry.height()); - QRect available = screen->availableGeometry(); - r.available_rect = gfx::Rect(available.x(), available.y(), available.width(), available.height()); - *results = r; -} - -} // namespace QtWebEngineCore - void *GetQtXDisplay() { return GLContextHelper::getXDisplay(); @@ -113,13 +90,6 @@ WebContentsView* CreateWebContentsView(WebContentsImpl *web_contents, return rv; } -// static -void WebContentsView::GetDefaultScreenInfo(content::ScreenInfo* results) -{ - QWindow dummy; - QtWebEngineCore::GetScreenInfoFromNativeWindow(&dummy, results); -} - #if defined(Q_OS_MACOS) std::string getQtPrefix() { diff --git a/src/core/command_line_pref_store_qt.cpp b/src/core/command_line_pref_store_qt.cpp new file mode 100644 index 000000000..5c5c82e1a --- /dev/null +++ b/src/core/command_line_pref_store_qt.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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$ +** +****************************************************************************/ + +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "command_line_pref_store_qt.h" + +#include "chrome/common/chrome_switches.h" +#include "components/proxy_config/proxy_config_dictionary.h" +#include "components/proxy_config/proxy_config_pref_names.h" +#include "content/public/common/content_switches.h" +#include <QDebug> + +CommandLinePrefStoreQt::CommandLinePrefStoreQt(const base::CommandLine *commandLine) + : CommandLinePrefStore(commandLine) +{ + + if (commandLine->HasSwitch(switches::kNoProxyServer)) { + SetValue(proxy_config::prefs::kProxy, + std::make_unique<base::Value>(ProxyConfigDictionary::CreateDirect()), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + } else if (commandLine->HasSwitch(switches::kProxyPacUrl)) { + std::string pac_script_url = + commandLine->GetSwitchValueASCII(switches::kProxyPacUrl); + SetValue(proxy_config::prefs::kProxy, + std::make_unique<base::Value>(ProxyConfigDictionary::CreatePacScript( + pac_script_url, false)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + } else if (commandLine->HasSwitch(switches::kProxyAutoDetect)) { + SetValue(proxy_config::prefs::kProxy, + std::make_unique<base::Value>( + ProxyConfigDictionary::CreateAutoDetect()), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + } else if (commandLine->HasSwitch(switches::kProxyServer)) { + std::string proxy_server = + commandLine->GetSwitchValueASCII(switches::kProxyServer); + std::string bypass_list = + commandLine->GetSwitchValueASCII(switches::kProxyBypassList); + SetValue( + proxy_config::prefs::kProxy, + std::make_unique<base::Value>(ProxyConfigDictionary::CreateFixedServers( + proxy_server, bypass_list)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + } + + if (commandLine->HasSwitch(switches::kNoProxyServer) && (commandLine->HasSwitch(switches::kProxyAutoDetect) || commandLine->HasSwitch(switches::kProxyServer) || commandLine->HasSwitch(switches::kProxyPacUrl) || commandLine->HasSwitch(switches::kProxyBypassList))) { + qWarning("Additional command-line proxy switches specified when --%s was also specified", + qPrintable(switches::kNoProxyServer)); + } +} + +CommandLinePrefStoreQt::~CommandLinePrefStoreQt() = default; diff --git a/src/core/chromium_overrides.h b/src/core/command_line_pref_store_qt.h index b27bf309c..a509f8ca9 100644 --- a/src/core/chromium_overrides.h +++ b/src/core/command_line_pref_store_qt.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtWebEngine module of the Qt Toolkit. @@ -37,18 +37,20 @@ ** ****************************************************************************/ -#ifndef CHROMIUM_OVERRIDES_H -#define CHROMIUM_OVERRIDES_H +#ifndef COMMAND_LINE_PREF_STORE_QT_H +#define COMMAND_LINE_PREF_STORE_QT_H -#include "content/public/common/screen_info.h" -#include <QtGlobal> +#include "base/command_line.h" +#include "components/prefs/command_line_pref_store.h" -QT_BEGIN_NAMESPACE -class QWindow; -QT_END_NAMESPACE +class CommandLinePrefStoreQt : public CommandLinePrefStore +{ +public: + explicit CommandLinePrefStoreQt(const base::CommandLine *commandLine); -namespace QtWebEngineCore { -void GetScreenInfoFromNativeWindow(QWindow* window, content::ScreenInfo* results); -} +protected: + ~CommandLinePrefStoreQt() override; + DISALLOW_COPY_AND_ASSIGN(CommandLinePrefStoreQt); +}; -#endif +#endif // COMMAND_LINE_PREF_STORE_QT_H diff --git a/src/core/compositor/compositor_resource.h b/src/core/compositor/compositor_resource.h index c40ce3d5e..f7df2ab59 100644 --- a/src/core/compositor/compositor_resource.h +++ b/src/core/compositor/compositor_resource.h @@ -71,8 +71,9 @@ using CompositorResourceId = quint32; // // - A committed resource's fields may not be updated and are safe to use from // other threads without synchronization (unless noted otherwise). -struct CompositorResource : viz::TransferableResource +class CompositorResource : public viz::TransferableResource { +public: CompositorResource(const viz::TransferableResource &tr) : viz::TransferableResource(tr) {} // Counts the number of times this resource has been encountered in diff --git a/src/core/compositor/delegated_frame_node.cpp b/src/core/compositor/delegated_frame_node.cpp index e2b6714a1..4fb05051b 100644 --- a/src/core/compositor/delegated_frame_node.cpp +++ b/src/core/compositor/delegated_frame_node.cpp @@ -619,7 +619,7 @@ void DelegatedFrameNode::commit(const viz::CompositorFrame &pendingFrame, QScopedPointer<DelegatedNodeTreeHandler> nodeHandler; - const QSizeF viewportSizeInPt = apiDelegate->screenRect().size(); + const QSizeF viewportSizeInPt = apiDelegate->viewGeometry().size(); const QSizeF viewportSizeF = viewportSizeInPt * devicePixelRatio; const QSize viewportSize(std::ceil(viewportSizeF.width()), std::ceil(viewportSizeF.height())); diff --git a/src/core/config/linux.pri b/src/core/config/linux.pri index 85b948db2..04c9eca89 100644 --- a/src/core/config/linux.pri +++ b/src/core/config/linux.pri @@ -19,11 +19,14 @@ gn_args += \ qtConfig(webengine-embedded-build) { gn_args += is_desktop_linux=false - gn_args += use_gold=false -} else { - !use_gold_linker: gn_args += use_gold=false } +use_gold_linker: gn_args += use_gold=true +else: gn_args += use_gold=false + +use_lld_linker: gn_args += use_lld=true +else: gn_args += use_lld=false + clang { clang_full_path = $$which($${QMAKE_CXX}) # Remove the "/bin/clang++" part. diff --git a/src/core/content_browser_client_qt.cpp b/src/core/content_browser_client_qt.cpp index 846f3b908..7440771cb 100644 --- a/src/core/content_browser_client_qt.cpp +++ b/src/core/content_browser_client_qt.cpp @@ -76,6 +76,7 @@ #include "mojo/public/cpp/bindings/binding_set.h" #include "printing/buildflags/buildflags.h" #include "net/ssl/client_cert_identity.h" +#include "net/ssl/client_cert_store.h" #include "services/proxy_resolver/proxy_resolver_service.h" #include "services/service_manager/public/cpp/connector.h" #include "services/service_manager/public/cpp/service.h" @@ -91,7 +92,6 @@ #include "qtwebengine/grit/qt_webengine_resources.h" -#include "client_cert_override_p.h" #include "profile_adapter.h" #include "browser_main_parts_qt.h" #include "browser_message_filter_qt.h" @@ -103,10 +103,12 @@ #include "media_capture_devices_dispatcher.h" #include "net/network_delegate_qt.h" #include "net/url_request_context_getter_qt.h" +#include "platform_notification_service_qt.h" #if QT_CONFIG(webengine_printing_and_pdf) #include "printing/printing_message_filter_qt.h" #endif #include "profile_qt.h" +#include "profile_io_data_qt.h" #include "quota_permission_context_qt.h" #include "renderer_host/user_resource_controller_host.h" #include "service/service_qt.h" @@ -161,7 +163,7 @@ public: { QString platform = qApp->platformName().toLower(); QPlatformNativeInterface *pni = QGuiApplication::platformNativeInterface(); - if (platform == QLatin1String("xcb")) { + if (platform == QLatin1String("xcb") || platform == QLatin1String("offscreen")) { if (gl::GetGLImplementation() == gl::kGLImplementationEGLGLES2) m_handle = pni->nativeResourceForContext(QByteArrayLiteral("eglcontext"), qtContext); else @@ -400,7 +402,7 @@ std::unique_ptr<net::ClientCertStore> ContentBrowserClientQt::CreateClientCertSt if (!resource_context) return nullptr; - return std::unique_ptr<net::ClientCertStore>(new net::ClientCertOverrideStore()); + return ProfileIODataQt::FromResourceContext(resource_context)->CreateClientCertStore(); } std::string ContentBrowserClientQt::GetApplicationLocale() @@ -454,7 +456,7 @@ void ContentBrowserClientQt::GetAdditionalMappedFilesForChildProcess(const base: void ContentBrowserClientQt::DidCreatePpapiPlugin(content::BrowserPpapiHost* browser_host) { browser_host->GetPpapiHost()->AddHostFactoryFilter( - base::WrapUnique(new QtWebEngineCore::PepperHostFactoryQt(browser_host))); + std::make_unique<QtWebEngineCore::PepperHostFactoryQt>(browser_host)); } #endif @@ -463,6 +465,13 @@ content::DevToolsManagerDelegate* ContentBrowserClientQt::GetDevToolsManagerDele return new DevToolsManagerDelegateQt; } +content::PlatformNotificationService *ContentBrowserClientQt::GetPlatformNotificationService() +{ + if (!m_platformNotificationService) + m_platformNotificationService = std::make_unique<PlatformNotificationServiceQt>(); + return m_platformNotificationService.get(); +} + // This is a really complicated way of doing absolutely nothing, but Mojo demands it: class ServiceDriver : public blink::mojom::InsecureInputService @@ -760,13 +769,19 @@ scoped_refptr<content::LoginDelegate> ContentBrowserClientQt::CreateLoginDelegat return loginDelegate; } +bool ContentBrowserClientQt::ShouldIsolateErrorPage(bool in_main_frame) +{ + Q_UNUSED(in_main_frame); + return false; +} + bool ContentBrowserClientQt::ShouldUseProcessPerSite(content::BrowserContext* browser_context, const GURL& effective_url) { #if BUILDFLAG(ENABLE_EXTENSIONS) - return true; -#else - return ContentBrowserClient::ShouldUseProcessPerSite(browser_context, effective_url); + if (effective_url.SchemeIs(extensions::kExtensionScheme)) + return true; #endif + return ContentBrowserClient::ShouldUseProcessPerSite(browser_context, effective_url); } } // namespace QtWebEngineCore diff --git a/src/core/content_browser_client_qt.h b/src/core/content_browser_client_qt.h index e90b687cc..403ff3a9d 100644 --- a/src/core/content_browser_client_qt.h +++ b/src/core/content_browser_client_qt.h @@ -108,6 +108,7 @@ public: std::unique_ptr<content::ClientCertificateDelegate> delegate) override; std::unique_ptr<net::ClientCertStore> CreateClientCertStore(content::ResourceContext *resource_context) override; content::DevToolsManagerDelegate *GetDevToolsManagerDelegate() override; + content::PlatformNotificationService *GetPlatformNotificationService() override; std::string GetApplicationLocale() override; std::string GetAcceptLangs(content::BrowserContext* context) override; @@ -175,6 +176,7 @@ public: #if QT_CONFIG(webengine_geolocation) std::unique_ptr<device::LocationProvider> OverrideSystemLocationProvider() override; #endif + bool ShouldIsolateErrorPage(bool in_main_frame) override; bool ShouldUseProcessPerSite(content::BrowserContext* browser_context, const GURL& effective_url) override; #if defined(Q_OS_LINUX) @@ -209,6 +211,7 @@ private: BrowserMainPartsQt* m_browserMainParts; std::unique_ptr<content::ResourceDispatcherHostDelegate> m_resourceDispatcherHostDelegate; + std::unique_ptr<content::PlatformNotificationService> m_platformNotificationService; scoped_refptr<ShareGroupQtQuick> m_shareGroupQtQuick; std::unique_ptr<service_manager::BinderRegistry> m_frameInterfaces; std::unique_ptr<service_manager::BinderRegistryWithArgs<content::RenderFrameHost*>> m_frameInterfacesParameterized; diff --git a/src/core/content_client_qt.cpp b/src/core/content_client_qt.cpp index 9065174b8..9bfc8eede 100644 --- a/src/core/content_client_qt.cpp +++ b/src/core/content_client_qt.cpp @@ -288,9 +288,19 @@ static bool IsWidevineAvailable(base::FilePath *cdm_path, pluginPaths << ppapiPluginsPath() + QStringLiteral("/") + QString::fromLatin1(kWidevineCdmFileName); #endif #if defined(Q_OS_OSX) - QDir potentialWidevineDir(QDir::homePath() + "/Library/Application Support/Google/Chrome/WidevineCDM"); + QDir potentialWidevineDir("/Applications/Google Chrome.app/Contents/Versions"); if (potentialWidevineDir.exists()) { - QFileInfoList widevineVersionDirs = potentialWidevineDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed); + QFileInfoList widevineVersionDirs = potentialWidevineDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, + QDir::Name | QDir::Reversed); + const QString library = QLatin1String("/Google Chrome Framework.framework/Versions/A/Libraries/" + "WidevineCdm/_platform_specific/mac_x64/libwidevinecdm.dylib"); + for (const QFileInfo &info : widevineVersionDirs) + pluginPaths << info.absoluteFilePath() + library; + } + + QDir oldPotentialWidevineDir(QDir::homePath() + "/Library/Application Support/Google/Chrome/WidevineCDM"); + if (oldPotentialWidevineDir.exists()) { + QFileInfoList widevineVersionDirs = oldPotentialWidevineDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed); for (int i = 0; i < widevineVersionDirs.size(); ++i) { QString versionDirPath(widevineVersionDirs.at(i).absoluteFilePath()); QString potentialWidevinePluginPath = versionDirPath + "/_platform_specific/mac_x64/" + QString::fromLatin1(kWidevineCdmFileName); diff --git a/src/core/core_chromium.pri b/src/core/core_chromium.pri index 18cedc63e..f4cb8c9da 100644 --- a/src/core/core_chromium.pri +++ b/src/core/core_chromium.pri @@ -48,11 +48,11 @@ SOURCES = \ browser_message_filter_qt.cpp \ certificate_error_controller.cpp \ chromium_overrides.cpp \ - client_cert_override_key.cpp \ client_cert_select_controller.cpp \ clipboard_qt.cpp \ color_chooser_qt.cpp \ color_chooser_controller.cpp \ + command_line_pref_store_qt.cpp \ common/qt_ipc_logging.cpp \ common/qt_messages.cpp \ common/user_script_data.cpp \ @@ -76,6 +76,8 @@ SOURCES = \ login_delegate_qt.cpp \ media_capture_devices_dispatcher.cpp \ native_web_keyboard_event_qt.cpp \ + net/client_cert_override.cpp \ + net/client_cert_store_data.cpp \ net/cookie_monster_delegate_qt.cpp \ net/custom_protocol_handler.cpp \ net/network_delegate_qt.cpp \ @@ -86,6 +88,7 @@ SOURCES = \ net/url_request_custom_job.cpp \ net/url_request_custom_job_delegate.cpp \ net/url_request_custom_job_proxy.cpp \ + net/url_request_notification.cpp \ net/webui_controller_factory_qt.cpp \ ozone/gl_context_qt.cpp \ ozone/gl_ozone_egl_qt.cpp \ @@ -96,6 +99,7 @@ SOURCES = \ ozone/platform_window_qt.cpp \ ozone/surface_factory_qt.cpp \ permission_manager_qt.cpp \ + platform_notification_service_qt.cpp \ process_main.cpp \ profile_adapter.cpp \ profile_adapter_client.cpp \ @@ -103,7 +107,6 @@ SOURCES = \ profile_io_data_qt.cpp \ quota_permission_context_qt.cpp \ quota_request_controller_impl.cpp \ - qwebengineclientcertificatestore.cpp \ register_protocol_handler_request_controller_impl.cpp \ render_view_context_menu_qt.cpp \ render_view_observer_host_qt.cpp \ @@ -121,6 +124,7 @@ SOURCES = \ touch_selection_controller_client_qt.cpp \ touch_selection_menu_controller.cpp \ type_conversion.cpp \ + user_notification_controller.cpp \ user_script.cpp \ visited_links_manager_qt.cpp \ web_contents_adapter.cpp \ @@ -145,11 +149,9 @@ HEADERS = \ browser_message_filter_qt.h \ certificate_error_controller_p.h \ certificate_error_controller.h \ - chromium_overrides.h \ - client_cert_override_key_p.h \ - client_cert_override_p.h \ client_cert_select_controller.h \ clipboard_qt.h \ + command_line_pref_store_qt.h \ color_chooser_qt.h \ color_chooser_controller_p.h \ color_chooser_controller.h \ @@ -178,6 +180,8 @@ HEADERS = \ locked_ptr.h \ login_delegate_qt.h \ media_capture_devices_dispatcher.h \ + net/client_cert_override.h \ + net/client_cert_store_data.h \ net/cookie_monster_delegate_qt.h \ net/custom_protocol_handler.h \ net/network_delegate_qt.h \ @@ -187,6 +191,7 @@ HEADERS = \ net/url_request_custom_job.h \ net/url_request_custom_job_delegate.h \ net/url_request_custom_job_proxy.h \ + net/url_request_notification.h \ net/webui_controller_factory_qt.h \ ozone/gl_context_qt.h \ ozone/gl_ozone_egl_qt.h \ @@ -197,6 +202,7 @@ HEADERS = \ ozone/platform_window_qt.h \ ozone/surface_factory_qt.h \ permission_manager_qt.h \ + platform_notification_service_qt.h \ process_main.h \ profile_adapter.h \ profile_adapter_client.h \ @@ -226,6 +232,7 @@ HEADERS = \ touch_selection_controller_client_qt.h \ touch_selection_menu_controller.h \ type_conversion.h \ + user_notification_controller.h \ user_script.h \ visited_links_manager_qt.h \ web_contents_adapter.h \ diff --git a/src/core/core_common.pri b/src/core/core_common.pri index 5f9f3c4f6..8375d89e5 100644 --- a/src/core/core_common.pri +++ b/src/core/core_common.pri @@ -13,3 +13,7 @@ CONFIG -= ltcg # Chromium requires C++14 CONFIG += c++14 + +#QTBUG-73216 ci has to be updated with latest yocto +boot2qt: CONFIG -= use_gold_linker + diff --git a/src/core/download_manager_delegate_qt.cpp b/src/core/download_manager_delegate_qt.cpp index 398bde710..c8b75a893 100644 --- a/src/core/download_manager_delegate_qt.cpp +++ b/src/core/download_manager_delegate_qt.cpp @@ -79,7 +79,7 @@ DownloadManagerDelegateQt::~DownloadManagerDelegateQt() void DownloadManagerDelegateQt::GetNextId(const content::DownloadIdCallback& callback) { - callback.Run(++m_currentId); + callback.Run(m_currentId); } download::DownloadItem *DownloadManagerDelegateQt::findDownloadById(quint32 downloadId) @@ -124,6 +124,8 @@ void DownloadManagerDelegateQt::removeDownload(quint32 downloadId) bool DownloadManagerDelegateQt::DetermineDownloadTarget(download::DownloadItem* item, const content::DownloadTargetCallback& callback) { + m_currentId = item->GetId(); + // Keep the forced file path if set, also as the temporary file, so the check for existence // will already return that the file exists. Forced file paths seem to be only used for // store downloads and other special downloads, so they might never end up here anyway. @@ -184,6 +186,7 @@ bool DownloadManagerDelegateQt::DetermineDownloadTarget(download::DownloadItem* if (webContents) adapterClient = static_cast<WebContentsDelegateQt *>(webContents->GetDelegate())->adapterClient(); + Q_ASSERT(m_currentId == item->GetId()); ProfileAdapterClient::DownloadItemInfo info = { item->GetId(), toQt(item->GetURL()), @@ -284,8 +287,9 @@ void DownloadManagerDelegateQt::ChooseSavePath(content::WebContents *web_content if (web_contents) adapterClient = static_cast<WebContentsDelegateQt *>(web_contents->GetDelegate())->adapterClient(); + // Chromium doesn't increase download ID when saving page. ProfileAdapterClient::DownloadItemInfo info = { - m_currentId + 1, + ++m_currentId, toQt(web_contents->GetURL()), download::DownloadItem::IN_PROGRESS, 0, /* totalBytes */ diff --git a/src/core/net/client_cert_override.cpp b/src/core/net/client_cert_override.cpp new file mode 100644 index 000000000..caf6adad7 --- /dev/null +++ b/src/core/net/client_cert_override.cpp @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** 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 "net/client_cert_override.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/task/post_task.h" +#include "base/callback_forward.h" +#include "net/ssl/client_cert_store.h" +#include "net/ssl/ssl_cert_request_info.h" +#include "net/ssl/ssl_private_key.h" +#include "net/cert/x509_certificate.h" +#include "third_party/boringssl/src/include/openssl/pem.h" +#include "third_party/boringssl/src/include/openssl/err.h" +#include "third_party/boringssl/src/include/openssl/evp.h" + +#include "api/qwebengineclientcertificatestore.h" +#include "net/client_cert_store_data.h" +#include "profile_io_data_qt.h" + +#include <QtNetwork/qtnetworkglobal.h> + +#if defined(USE_NSS_CERTS) +#include "net/ssl/client_cert_store_nss.h" +#endif + +#if defined(OS_WIN) +#include "net/ssl/client_cert_store_win.h" +#endif + +#if defined(OS_MACOSX) +#include "net/ssl/client_cert_store_mac.h" +#endif + +namespace { + +class ClientCertIdentityOverride : public net::ClientCertIdentity +{ +public: + ClientCertIdentityOverride(scoped_refptr<net::X509Certificate> cert, scoped_refptr<net::SSLPrivateKey> key) + : net::ClientCertIdentity(std::move(cert)), m_key(std::move(key)) {} + ~ClientCertIdentityOverride() override = default; + + void AcquirePrivateKey(const base::Callback<void(scoped_refptr<net::SSLPrivateKey>)> &private_key_callback) override + { + private_key_callback.Run(m_key); + } + +#if defined(OS_MACOSX) + SecIdentityRef sec_identity_ref() const override + { + return nullptr; + } +#endif + +private: + scoped_refptr<net::SSLPrivateKey> m_key; +}; + +} // namespace + +namespace QtWebEngineCore { + +ClientCertOverrideStore::ClientCertOverrideStore() + : ClientCertStore() + , m_nativeStore(createNativeStore()) +{ +} + +ClientCertOverrideStore::~ClientCertOverrideStore() = default; + +void ClientCertOverrideStore::GetClientCerts(const net::SSLCertRequestInfo &cert_request_info, + const ClientCertListCallback &callback) +{ +#if QT_CONFIG(ssl) + QWebEngineClientCertificateStore *clientCertificateStore = QWebEngineClientCertificateStore::getInstance(); + const auto &clientCertOverrideData = clientCertificateStore->d_ptr->addedCerts; + // Look for certificates in memory store + for (int i = 0; i < clientCertOverrideData.length(); i++) { + scoped_refptr<net::X509Certificate> cert = clientCertOverrideData[i]->certPtr; + if (cert != NULL && cert->IsIssuedByEncoded(cert_request_info.cert_authorities)) { + net::ClientCertIdentityList selected_identities; + selected_identities.push_back(std::make_unique<ClientCertIdentityOverride>(cert, clientCertOverrideData[i]->keyPtr)); + callback.Run(std::move(selected_identities)); + return; + } + } +#endif // QT_CONFIG(ssl) + + // Continue with native cert store if matching certificate is not found in memory + if (m_nativeStore) { + m_nativeStore->GetClientCerts(cert_request_info, callback); + return; + } + + callback.Run(net::ClientCertIdentityList()); + return; +} + +// static +std::unique_ptr<net::ClientCertStore> ClientCertOverrideStore::createNativeStore() +{ +#if defined(USE_NSS_CERTS) + return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreNSS(net::ClientCertStoreNSS::PasswordDelegateFactory())); +#elif defined(OS_WIN) + return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreWin()); +#elif defined(OS_MACOSX) + return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreMac()); +#else + return nullptr; +#endif +} +} // namespace QtWebEngineCore diff --git a/src/core/client_cert_override_p.h b/src/core/net/client_cert_override.h index b222bf810..4f13c3116 100644 --- a/src/core/client_cert_override_p.h +++ b/src/core/net/client_cert_override.h @@ -46,17 +46,23 @@ namespace net { class SSLCertRequestInfo; -class ClientCertOverrideStore : public ClientCertStore +} // namespace net + +namespace QtWebEngineCore { + +class ClientCertOverrideStore : public net::ClientCertStore { public: ClientCertOverrideStore(); virtual ~ClientCertOverrideStore() override; - void GetClientCerts(const SSLCertRequestInfo &cert_request_info, + void GetClientCerts(const net::SSLCertRequestInfo &cert_request_info, const ClientCertListCallback &callback) override; private: - std::unique_ptr<net::ClientCertStore> getNativeStore(); + static std::unique_ptr<net::ClientCertStore> createNativeStore(); + std::unique_ptr<net::ClientCertStore> m_nativeStore; }; -} // namespace net + +} // QtWebEngineCore #endif diff --git a/src/core/client_cert_override_key.cpp b/src/core/net/client_cert_store_data.cpp index 99ddf7466..ae4deed1c 100644 --- a/src/core/client_cert_override_key.cpp +++ b/src/core/net/client_cert_store_data.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtWebEngine module of the Qt Toolkit. @@ -37,101 +37,115 @@ ** ****************************************************************************/ -#ifndef CLIENT_CERT_OVERRIDE_KEY_H -#define CLIENT_CERT_OVERRIDE_KEY_H - -#include "client_cert_override_key_p.h" - -#include "third_party/boringssl/src/include/openssl/ssl.h" -#include "third_party/boringssl/src/include/openssl/digest.h" -#include "third_party/boringssl/src/include/openssl/evp.h" -#include "third_party/boringssl/src/include/openssl/rsa.h" -#include "third_party/boringssl/src/include/openssl/pem.h" - -#include <utility> -#include <QByteArray> +#include "net/client_cert_store_data.h" +#if QT_CONFIG(ssl) #include "base/logging.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "net/base/net_errors.h" +#include "net/cert/x509_certificate.h" #include "net/ssl/ssl_platform_key_util.h" #include "net/ssl/ssl_private_key.h" #include "net/ssl/threaded_ssl_private_key.h" -namespace net { +#include "third_party/boringssl/src/include/openssl/ssl.h" +#include "third_party/boringssl/src/include/openssl/digest.h" +#include "third_party/boringssl/src/include/openssl/evp.h" +#include "third_party/boringssl/src/include/openssl/rsa.h" +#include "third_party/boringssl/src/include/openssl/pem.h" + +#include "QtCore/qbytearray.h" namespace { -class SSLPlatformKeyOverride : public ThreadedSSLPrivateKey::Delegate { +class SSLPlatformKeyOverride : public net::ThreadedSSLPrivateKey::Delegate { public: SSLPlatformKeyOverride(const QByteArray &sslKeyInBytes) { - mem_ = BIO_new_mem_buf(sslKeyInBytes, -1); - key_ = PEM_read_bio_PrivateKey(mem_, NULL, 0, NULL); + m_mem = BIO_new_mem_buf(sslKeyInBytes, -1); + m_key = PEM_read_bio_PrivateKey(m_mem, nullptr, nullptr, nullptr); } - ~SSLPlatformKeyOverride() override { - if (key_) - EVP_PKEY_free(key_); - if (mem_) - BIO_free(mem_); + ~SSLPlatformKeyOverride() override + { + if (m_key) + EVP_PKEY_free(m_key); + if (m_mem) + BIO_free(m_mem); } - Error Sign(uint16_t algorithm, - base::span<const uint8_t> input, - std::vector<uint8_t>* signature) override { + net::Error Sign(uint16_t algorithm, base::span<const uint8_t> input, std::vector<uint8_t> *signature) override + { bssl::ScopedEVP_MD_CTX ctx; - EVP_PKEY_CTX* pctx; + EVP_PKEY_CTX *pctx; if (!EVP_DigestSignInit(ctx.get(), &pctx, SSL_get_signature_algorithm_digest(algorithm), - nullptr, key_)) { - return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED; + nullptr, m_key)) { + return net::ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED; } if (SSL_is_signature_algorithm_rsa_pss(algorithm)) { if (!EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) || !EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1 /* hash length */)) { - return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED; + return net::ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED; } } size_t sig_len = 0; if (!EVP_DigestSign(ctx.get(), NULL, &sig_len, input.data(), input.size())) - return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED; + return net::ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED; signature->resize(sig_len); - if (!EVP_DigestSign(ctx.get(), signature->data(), &sig_len, input.data(), - input.size())) { - return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED; - } + if (!EVP_DigestSign(ctx.get(), signature->data(), &sig_len, input.data(), input.size())) + return net::ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED; signature->resize(sig_len); - return OK; + return net::OK; } - std::vector<uint16_t> GetAlgorithmPreferences() override { - return { - SSL_SIGN_RSA_PKCS1_SHA1, SSL_SIGN_RSA_PKCS1_SHA512, - SSL_SIGN_RSA_PKCS1_SHA384, SSL_SIGN_RSA_PKCS1_SHA256, - }; + std::vector<uint16_t> GetAlgorithmPreferences() override + { + return { SSL_SIGN_RSA_PKCS1_SHA1, SSL_SIGN_RSA_PKCS1_SHA512 + , SSL_SIGN_RSA_PKCS1_SHA384, SSL_SIGN_RSA_PKCS1_SHA256 }; } private: - EVP_PKEY* key_; - BIO * mem_; + EVP_PKEY *m_key; + BIO *m_mem; DISALLOW_COPY_AND_ASSIGN(SSLPlatformKeyOverride); }; -} // namespace - -scoped_refptr<SSLPrivateKey> WrapOpenSSLPrivateKey(const QByteArray &sslKeyInBytes) { +scoped_refptr<net::SSLPrivateKey> wrapOpenSSLPrivateKey(const QByteArray &sslKeyInBytes) +{ if (sslKeyInBytes.isEmpty()) return nullptr; - return base::MakeRefCounted<ThreadedSSLPrivateKey>( + return base::MakeRefCounted<net::ThreadedSSLPrivateKey>( std::make_unique<SSLPlatformKeyOverride>(sslKeyInBytes), - GetSSLPlatformKeyTaskRunner()); + net::GetSSLPlatformKeyTaskRunner()); +} + +} // namespace + +namespace QtWebEngineCore { + +void ClientCertificateStoreData::add(const QSslCertificate &certificate, const QSslKey &privateKey) +{ + QByteArray sslKeyInBytes = privateKey.toPem(); + QByteArray certInBytes = certificate.toDer(); + + Entry *data = new Entry; + data->keyPtr = wrapOpenSSLPrivateKey(sslKeyInBytes); + data->certPtr = net::X509Certificate::CreateFromBytes(certInBytes.data(), certInBytes.length()); + data->key = privateKey; + data->certificate = certificate; + addedCerts.append(data); +} + +ClientCertificateStoreData::~ClientCertificateStoreData() +{ + qDeleteAll(deletedCerts); } -} // namespace net +} // namespace QtWebEngineCore #endif diff --git a/src/core/client_cert_override_key_p.h b/src/core/net/client_cert_store_data.h index 7ac610be4..41dc1f8ec 100644 --- a/src/core/client_cert_override_key_p.h +++ b/src/core/net/client_cert_store_data.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtWebEngine module of the Qt Toolkit. @@ -37,16 +37,42 @@ ** ****************************************************************************/ -#ifndef CLIENT_CERT_OVERRIDE_KEY_P_H -#define CLIENT_CERT_OVERRIDE_KEY_P_H +#ifndef CLIENT_CERT_STORE_DATA_H +#define CLIENT_CERT_STORE_DATA_H -#include "net/ssl/ssl_private_key.h" +#include "qtwebenginecoreglobal.h" +#include "qtnetworkglobal.h" -#include <QByteArray> +#if QT_CONFIG(ssl) +#include "base/memory/ref_counted.h" + +#include <QtCore/qlist.h> +#include <QtNetwork/qsslcertificate.h> +#include <QtNetwork/qsslkey.h> namespace net { - class SSLPrivateKey; - scoped_refptr<SSLPrivateKey> WrapOpenSSLPrivateKey(const QByteArray &sslKeyInBytes); -} // namespace net +class SSLPrivateKey; +class X509Certificate; +} + +namespace QtWebEngineCore { + +struct ClientCertificateStoreData { + struct Entry { + QSslKey key; + QSslCertificate certificate; + scoped_refptr<net::X509Certificate> certPtr; + scoped_refptr<net::SSLPrivateKey> keyPtr; + }; + + ~ClientCertificateStoreData(); + void add(const QSslCertificate &certificate, const QSslKey &privateKey); + + QList<Entry*> addedCerts; + QList<Entry*> deletedCerts; +}; + +} // namespace QtWebEngineCore #endif +#endif // CLIENT_CERT_STORE_DATA_H diff --git a/src/core/net/network_delegate_qt.cpp b/src/core/net/network_delegate_qt.cpp index 73f3ff818..3641cb845 100644 --- a/src/core/net/network_delegate_qt.cpp +++ b/src/core/net/network_delegate_qt.cpp @@ -58,6 +58,7 @@ #include "type_conversion.h" #include "web_contents_adapter_client.h" #include "web_contents_view_qt.h" +#include "url_request_notification.h" namespace QtWebEngineCore { @@ -91,146 +92,11 @@ static QWebEngineUrlRequestInfo::ResourceType toQt(content::ResourceType resourc return QWebEngineUrlRequestInfo::ResourceTypeUnknown; } -static content::ResourceType fromQt(QWebEngineUrlRequestInfo::ResourceType resourceType) -{ - return static_cast<content::ResourceType>(resourceType); -} - static QWebEngineUrlRequestInfo::NavigationType toQt(WebContentsAdapterClient::NavigationType navigationType) { return static_cast<QWebEngineUrlRequestInfo::NavigationType>(navigationType); } -// Notifies WebContentsAdapterClient of a new URLRequest. -class URLRequestNotification { -public: - URLRequestNotification(net::URLRequest *request, - bool isMainFrameRequest, - GURL *newUrl, - QWebEngineUrlRequestInfo &&requestInfo, - content::ResourceRequestInfo::WebContentsGetter webContentsGetter, - net::CompletionOnceCallback callback) - : m_request(request) - , m_isMainFrameRequest(isMainFrameRequest) - , m_newUrl(newUrl) - , m_originalUrl(requestInfo.requestUrl()) - , m_requestInfo(std::move(requestInfo)) - , m_webContentsGetter(webContentsGetter) - , m_callback(std::move(callback)) - { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - - m_request->SetUserData(UserData::key, std::make_unique<UserData>(this)); - - base::PostTaskWithTraits( - FROM_HERE, - {content::BrowserThread::UI}, - base::BindOnce(&URLRequestNotification::notify, base::Unretained(this))); - } - -private: - // Calls cancel() when the URLRequest is destroyed. - class UserData : public base::SupportsUserData::Data { - public: - UserData(URLRequestNotification *ptr) : m_ptr(ptr) {} - ~UserData() { m_ptr->cancel(); } - static const char key[]; - private: - URLRequestNotification *m_ptr; - }; - - void cancel() - { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - - // May run concurrently with notify() but we only touch m_request here. - - m_request = nullptr; - } - - void notify() - { - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - - // May run concurrently with cancel() so no peeking at m_request here. - - int error = net::OK; - content::WebContents *webContents = m_webContentsGetter.Run(); - - if (webContents) { - WebContentsAdapterClient *client = - WebContentsViewQt::from(static_cast<content::WebContentsImpl*>(webContents)->GetView())->client(); - - client->interceptRequest(m_requestInfo); - if (m_requestInfo.changed()) { - error = m_requestInfo.d_ptr->shouldBlockRequest ? net::ERR_BLOCKED_BY_CLIENT : net::OK; - // We handle the rest of the changes later when we are back in I/O thread - } - - // Only do navigationRequested on MAIN_FRAME and SUB_FRAME resources - if (error == net::OK && content::IsResourceTypeFrame(fromQt(m_requestInfo.resourceType()))) { - int navigationRequestAction = WebContentsAdapterClient::AcceptRequest; - client->navigationRequested(m_requestInfo.navigationType(), - m_requestInfo.requestUrl(), - navigationRequestAction, - m_isMainFrameRequest); - error = net::ERR_FAILED; - switch (static_cast<WebContentsAdapterClient::NavigationRequestAction>(navigationRequestAction)) { - case WebContentsAdapterClient::AcceptRequest: - error = net::OK; - break; - case WebContentsAdapterClient::IgnoreRequest: - error = net::ERR_ABORTED; - break; - } - DCHECK(error != net::ERR_FAILED); - } - } - - // Run the callback on the IO thread. - base::PostTaskWithTraits( - FROM_HERE, - {content::BrowserThread::IO}, - base::BindOnce(&URLRequestNotification::complete, base::Unretained(this), error)); - } - - void complete(int error) - { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - - if (m_request) { - if (m_requestInfo.changed()) { - if (m_originalUrl != m_requestInfo.d_ptr->url) - *m_newUrl = toGurl(m_requestInfo.d_ptr->url); - - if (!m_requestInfo.d_ptr->extraHeaders.isEmpty()) { - auto end = m_requestInfo.d_ptr->extraHeaders.constEnd(); - for (auto header = m_requestInfo.d_ptr->extraHeaders.constBegin(); header != end; ++header) - m_request->SetExtraRequestHeaderByName(header.key().toStdString(), header.value().toStdString(), /* overwrite */ true); - } - } - - if (m_request->status().status() != net::URLRequestStatus::CANCELED) - std::move(m_callback).Run(error); - m_request->RemoveUserData(UserData::key); - } - - delete this; - } - - ~URLRequestNotification() {} - - net::URLRequest *m_request; - bool m_isMainFrameRequest; - GURL *m_newUrl; - const QUrl m_originalUrl; - QWebEngineUrlRequestInfo m_requestInfo; - content::ResourceRequestInfo::WebContentsGetter m_webContentsGetter; - net::CompletionOnceCallback m_callback; -}; - -const char URLRequestNotification::UserData::key[] = "QtWebEngineCore::URLRequestNotification"; - NetworkDelegateQt::NetworkDelegateQt(ProfileIODataQt *data) : m_profileIOData(data) { @@ -265,33 +131,41 @@ int NetworkDelegateQt::OnBeforeURLRequest(net::URLRequest *request, net::Complet QByteArray::fromStdString(request->method())); QWebEngineUrlRequestInfo requestInfo(infoPrivate); - if (QWebEngineUrlRequestInterceptor* interceptor = m_profileIOData->acquireInterceptor()) { - interceptor->interceptRequest(requestInfo); - m_profileIOData->releaseInterceptor(); - if (requestInfo.changed()) { - int result = infoPrivate->shouldBlockRequest ? net::ERR_BLOCKED_BY_CLIENT : net::OK; + // Deprecated =begin + // quick peek if deprecated + QWebEngineUrlRequestInterceptor* profileInterceptor = m_profileIOData->requestInterceptor(); + if (profileInterceptor && profileInterceptor->property("deprecated").toBool()) { + profileInterceptor = nullptr; + if (QWebEngineUrlRequestInterceptor* interceptor = m_profileIOData->acquireInterceptor()) { + interceptor->interceptRequest(requestInfo); + m_profileIOData->releaseInterceptor(); + if (requestInfo.changed()) { + int result = infoPrivate->shouldBlockRequest ? net::ERR_BLOCKED_BY_CLIENT : net::OK; + + if (qUrl != infoPrivate->url) + *newUrl = toGurl(infoPrivate->url); + + if (!infoPrivate->extraHeaders.isEmpty()) { + auto end = infoPrivate->extraHeaders.constEnd(); + for (auto header = infoPrivate->extraHeaders.constBegin(); header != end; ++header) + request->SetExtraRequestHeaderByName(header.key().toStdString(), header.value().toStdString(), /* overwrite */ true); + } - if (qUrl != infoPrivate->url) - *newUrl = toGurl(infoPrivate->url); + if (result != net::OK) + return result; - if (!infoPrivate->extraHeaders.isEmpty()) { - auto end = infoPrivate->extraHeaders.constEnd(); - for (auto header = infoPrivate->extraHeaders.constBegin(); header != end; ++header) - request->SetExtraRequestHeaderByName(header.key().toStdString(), header.value().toStdString(), /* overwrite */ true); + requestInfo.resetChanged(); } - - if (result != net::OK) - return result; - - requestInfo.resetChanged(); + } else { + m_profileIOData->releaseInterceptor(); } - } else - m_profileIOData->releaseInterceptor(); + } + // Deprecated =cut if (!resourceInfo) return net::OK; - if (!m_profileIOData->hasPageInterceptors() && !content::IsResourceTypeFrame(resourceType)) + if (!m_profileIOData->hasPageInterceptors() && !profileInterceptor && !content::IsResourceTypeFrame(resourceType)) return net::OK; auto webContentsGetter = resourceInfo->GetWebContentsGetterForRequest(); @@ -301,7 +175,8 @@ int NetworkDelegateQt::OnBeforeURLRequest(net::URLRequest *request, net::Complet newUrl, std::move(requestInfo), webContentsGetter, - std::move(callback) + std::move(callback), + profileInterceptor ? m_profileIOData->profileAdapter() : nullptr ); // We'll run the callback after we notified the UI thread. diff --git a/src/core/net/proxy_config_service_qt.cpp b/src/core/net/proxy_config_service_qt.cpp index 13b969281..ff8ab20aa 100644 --- a/src/core/net/proxy_config_service_qt.cpp +++ b/src/core/net/proxy_config_service_qt.cpp @@ -47,6 +47,7 @@ #include "base/bind.h" #include "content/public/browser/browser_thread.h" +#include "components/proxy_config/pref_proxy_config_tracker_impl.h" using content::BrowserThread; @@ -68,10 +69,13 @@ net::ProxyServer ProxyConfigServiceQt::fromQNetworkProxy(const QNetworkProxy &qt } } -ProxyConfigServiceQt::ProxyConfigServiceQt(std::unique_ptr<ProxyConfigService> baseService) +ProxyConfigServiceQt::ProxyConfigServiceQt(std::unique_ptr<ProxyConfigService> baseService, + const net::ProxyConfigWithAnnotation& initialConfig, ProxyPrefs::ConfigState initialState) : m_baseService(baseService.release()), m_usesSystemConfiguration(false), - m_registeredObserver(false) + m_registeredObserver(false), + m_prefConfig(initialConfig), + m_perfState(initialState) { } @@ -100,8 +104,10 @@ net::ProxyConfigService::ConfigAvailability ProxyConfigServiceQt::GetLatestProxy ConfigAvailability systemAvailability = net::ProxyConfigService::CONFIG_UNSET; if (m_baseService.get()) systemAvailability = m_baseService->GetLatestProxyConfig(&systemConfig); - *config = systemConfig; - // make sure to get updates via OnProxyConfigChanged + ProxyPrefs::ConfigState configState; + systemAvailability = PrefProxyConfigTrackerImpl::GetEffectiveProxyConfig( + m_perfState, m_prefConfig, systemAvailability, systemConfig, + false, &configState, config); RegisterObserver(); return systemAvailability; } diff --git a/src/core/net/proxy_config_service_qt.h b/src/core/net/proxy_config_service_qt.h index 961927b89..09e88d445 100644 --- a/src/core/net/proxy_config_service_qt.h +++ b/src/core/net/proxy_config_service_qt.h @@ -43,8 +43,10 @@ #include "base/memory/ref_counted.h" #include "base/observer_list.h" +#include "net/proxy_resolution/proxy_config.h" #include "net/proxy_resolution/proxy_config_service.h" #include "net/proxy_resolution/proxy_config_with_annotation.h" +#include "components/proxy_config/proxy_prefs.h" #include <QNetworkProxy> @@ -55,7 +57,9 @@ public: static net::ProxyServer fromQNetworkProxy(const QNetworkProxy &); - explicit ProxyConfigServiceQt(std::unique_ptr<ProxyConfigService> baseService); + explicit ProxyConfigServiceQt(std::unique_ptr<ProxyConfigService> baseService, + const net::ProxyConfigWithAnnotation& initialConfig, + ProxyPrefs::ConfigState initialState); ~ProxyConfigServiceQt() override; // ProxyConfigService implementation: @@ -86,6 +90,10 @@ private: // Indicates whether the base service registration is done. bool m_registeredObserver; + // Configuration as defined by prefs. + net::ProxyConfigWithAnnotation m_prefConfig; + ProxyPrefs::ConfigState m_perfState; + DISALLOW_COPY_AND_ASSIGN(ProxyConfigServiceQt); }; diff --git a/src/core/net/url_request_notification.cpp b/src/core/net/url_request_notification.cpp new file mode 100644 index 000000000..6da661cff --- /dev/null +++ b/src/core/net/url_request_notification.cpp @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 "url_request_notification.h" + +#include "base/supports_user_data.h" +#include "base/task/post_task.h" +#include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/browser_thread.h" +#include "net/url_request/url_request.h" +#include "web_contents_adapter_client.h" +#include "web_contents_view_qt.h" +#include "profile_io_data_qt.h" +#include "qwebengineurlrequestinfo_p.h" +#include "type_conversion.h" + +namespace QtWebEngineCore { + +// Calls cancel() when the URLRequest is destroyed. +class UserData : public base::SupportsUserData::Data { +public: + UserData(URLRequestNotification *ptr) : m_ptr(ptr) {} + ~UserData() { m_ptr->cancel(); } + static const char key[]; +private: + URLRequestNotification *m_ptr; +}; + +const char UserData::key[] = "QtWebEngineCore::URLRequestNotification"; + +static content::ResourceType fromQt(QWebEngineUrlRequestInfo::ResourceType resourceType) +{ + return static_cast<content::ResourceType>(resourceType); +} + +URLRequestNotification::URLRequestNotification(net::URLRequest *request, + bool isMainFrameRequest, + GURL *newUrl, + QWebEngineUrlRequestInfo &&requestInfo, + content::ResourceRequestInfo::WebContentsGetter webContentsGetter, + net::CompletionOnceCallback callback, + QPointer<ProfileAdapter> adapter) + : m_request(request) + , m_isMainFrameRequest(isMainFrameRequest) + , m_newUrl(newUrl) + , m_originalUrl(requestInfo.requestUrl()) + , m_requestInfo(std::move(requestInfo)) + , m_webContentsGetter(webContentsGetter) + , m_callback(std::move(callback)) + , m_profileAdapter(adapter) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + m_request->SetUserData(UserData::key, std::make_unique<UserData>(this)); + + base::PostTaskWithTraits( + FROM_HERE, + {content::BrowserThread::UI}, + base::BindOnce(&URLRequestNotification::notify, base::Unretained(this))); +} + + +void URLRequestNotification::notify() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + // May run concurrently with cancel() so no peeking at m_request here. + + int result = net::OK; + content::WebContents *webContents = m_webContentsGetter.Run(); + + if (webContents) { + + if (m_profileAdapter) { + QWebEngineUrlRequestInterceptor* interceptor = m_profileAdapter->requestInterceptor(); + interceptor->interceptRequest(m_requestInfo); + } + + WebContentsAdapterClient *client = + WebContentsViewQt::from(static_cast<content::WebContentsImpl*>(webContents)->GetView())->client(); + + if (!m_requestInfo.changed()) { + client->interceptRequest(m_requestInfo); + } + + if (m_requestInfo.changed()) { + result = m_requestInfo.d_ptr->shouldBlockRequest ? net::ERR_BLOCKED_BY_CLIENT : net::OK; + // We handle the rest of the changes later when we are back in I/O thread + } + + // Only do navigationRequested on MAIN_FRAME and SUB_FRAME resources + if (result == net::OK && content::IsResourceTypeFrame(fromQt(m_requestInfo.resourceType()))) { + int navigationRequestAction = WebContentsAdapterClient::AcceptRequest; + client->navigationRequested(m_requestInfo.navigationType(), + m_requestInfo.requestUrl(), + navigationRequestAction, + m_isMainFrameRequest); + result = net::ERR_FAILED; + switch (static_cast<WebContentsAdapterClient::NavigationRequestAction>(navigationRequestAction)) { + case WebContentsAdapterClient::AcceptRequest: + result = net::OK; + break; + case WebContentsAdapterClient::IgnoreRequest: + result = net::ERR_ABORTED; + break; + } + DCHECK(result != net::ERR_FAILED); + } + } + + // Run the callback on the IO thread. + base::PostTaskWithTraits( + FROM_HERE, + {content::BrowserThread::IO}, + base::BindOnce(&URLRequestNotification::complete, base::Unretained(this), result)); +} + +void URLRequestNotification::cancel() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + // May run concurrently with notify() but we only touch m_request here. + + m_request = nullptr; +} + +void URLRequestNotification::complete(int error) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + if (m_request) { + if (m_requestInfo.changed()) { + if (m_originalUrl != m_requestInfo.d_ptr->url) + *m_newUrl = toGurl(m_requestInfo.d_ptr->url); + + if (!m_requestInfo.d_ptr->extraHeaders.isEmpty()) { + auto end = m_requestInfo.d_ptr->extraHeaders.constEnd(); + for (auto header = m_requestInfo.d_ptr->extraHeaders.constBegin(); header != end; ++header) + m_request->SetExtraRequestHeaderByName(header.key().toStdString(), header.value().toStdString(), /* overwrite */ true); + } + } + + if (m_request->status().status() != net::URLRequestStatus::CANCELED) + std::move(m_callback).Run(error); + m_request->RemoveUserData(UserData::key); + } + + delete this; +} + +} diff --git a/src/core/net/url_request_notification.h b/src/core/net/url_request_notification.h new file mode 100644 index 000000000..1d9acf12f --- /dev/null +++ b/src/core/net/url_request_notification.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 URL_REQUEST_NOTIFIACTION_H +#define URL_REQUEST_NOTIFIACTION_H + +#include "content/public/browser/resource_request_info.h" +#include "net/base/completion_once_callback.h" +#include "qwebengineurlrequestinfo.h" +#include <QPointer> + +class GURL; + +namespace net { +class URLRequest; +} + +namespace QtWebEngineCore { + +class ProfileAdapter; +class ProfileIoDataQt; + +// Notifies WebContentsAdapterClient of a new URLRequest. +class URLRequestNotification { +public: + URLRequestNotification(net::URLRequest *request, + bool isMainFrameRequest, + GURL *newUrl, + QWebEngineUrlRequestInfo &&requestInfo, + content::ResourceRequestInfo::WebContentsGetter webContentsGetter, + net::CompletionOnceCallback callback, + QPointer<ProfileAdapter> adapter); + ~URLRequestNotification() = default; + void cancel(); + void notify(); + void complete(int error); + +private: + net::URLRequest *m_request; //used only by io thread + bool m_isMainFrameRequest; + GURL *m_newUrl; + const QUrl m_originalUrl; + QWebEngineUrlRequestInfo m_requestInfo; + content::ResourceRequestInfo::WebContentsGetter m_webContentsGetter; + net::CompletionOnceCallback m_callback; + QPointer<ProfileAdapter> m_profileAdapter; +}; +} +#endif diff --git a/src/core/ozone/gl_context_qt.cpp b/src/core/ozone/gl_context_qt.cpp index 7e913817e..c4a075544 100644 --- a/src/core/ozone/gl_context_qt.cpp +++ b/src/core/ozone/gl_context_qt.cpp @@ -130,9 +130,10 @@ void* GLContextHelper::getEGLDisplay() void* GLContextHelper::getXDisplay() { - if (QGuiApplication::platformName() != QLatin1String("xcb")) - return nullptr; - return qApp->platformNativeInterface()->nativeResourceForScreen(QByteArrayLiteral("display"), qApp->primaryScreen()); + QPlatformNativeInterface *pni = QGuiApplication::platformNativeInterface(); + if (pni) + return pni->nativeResourceForScreen(QByteArrayLiteral("display"), qApp->primaryScreen()); + return nullptr; } void* GLContextHelper::getNativeDisplay() diff --git a/src/core/ozone/gl_surface_glx_qt.cpp b/src/core/ozone/gl_surface_glx_qt.cpp index eebefa59b..4faa08091 100644 --- a/src/core/ozone/gl_surface_glx_qt.cpp +++ b/src/core/ozone/gl_surface_glx_qt.cpp @@ -173,7 +173,7 @@ bool GLSurfaceGLXQt::Initialize(GLSurfaceFormat format) GLX_PBUFFER_HEIGHT, m_size.height(), GLX_LARGEST_PBUFFER, x11::False, GLX_PRESERVED_CONTENTS, x11::False, - GLX_NONE + NULL }; m_surfaceBuffer = glXCreatePbuffer(display, static_cast<GLXFBConfig>(g_config), pbuffer_attributes); diff --git a/src/core/permission_manager_qt.cpp b/src/core/permission_manager_qt.cpp index b6d055e27..2a7311187 100644 --- a/src/core/permission_manager_qt.cpp +++ b/src/core/permission_manager_qt.cpp @@ -48,6 +48,7 @@ #include "type_conversion.h" #include "web_contents_delegate_qt.h" +#include "web_engine_settings.h" namespace QtWebEngineCore { @@ -60,8 +61,9 @@ ProfileAdapter::PermissionType toQt(content::PermissionType type) return ProfileAdapter::AudioCapturePermission; case content::PermissionType::VIDEO_CAPTURE: return ProfileAdapter::VideoCapturePermission; - case content::PermissionType::FLASH: case content::PermissionType::NOTIFICATIONS: + return ProfileAdapter::NotificationPermission; + case content::PermissionType::FLASH: case content::PermissionType::MIDI_SYSEX: case content::PermissionType::PROTECTED_MEDIA_IDENTIFIER: case content::PermissionType::MIDI: @@ -69,8 +71,11 @@ ProfileAdapter::PermissionType toQt(content::PermissionType type) case content::PermissionType::BACKGROUND_SYNC: case content::PermissionType::SENSORS: case content::PermissionType::ACCESSIBILITY_EVENTS: + break; case content::PermissionType::CLIPBOARD_READ: + return ProfileAdapter::ClipboardRead; case content::PermissionType::CLIPBOARD_WRITE: + return ProfileAdapter::ClipboardWrite; case content::PermissionType::PAYMENT_HANDLER: case content::PermissionType::BACKGROUND_FETCH: case content::PermissionType::NUM: @@ -154,19 +159,28 @@ int PermissionManagerQt::RequestPermission(content::PermissionType permission, bool /*user_gesture*/, const base::Callback<void(blink::mojom::PermissionStatus)>& callback) { - int request_id = ++m_requestIdCount; + WebContentsDelegateQt *contentsDelegate = static_cast<WebContentsDelegateQt *>( + content::WebContents::FromRenderFrameHost(frameHost)->GetDelegate()); + Q_ASSERT(contentsDelegate); + ProfileAdapter::PermissionType permissionType = toQt(permission); if (permissionType == ProfileAdapter::UnsupportedPermission) { callback.Run(blink::mojom::PermissionStatus::DENIED); return content::PermissionController::kNoPendingOperation; + } else if (permissionType == ProfileAdapter::ClipboardRead) { + WebEngineSettings *settings = contentsDelegate->webEngineSettings(); + if (settings->testAttribute(WebEngineSettings::JavascriptCanAccessClipboard) + && settings->testAttribute(WebEngineSettings::JavascriptCanPaste)) + callback.Run(blink::mojom::PermissionStatus::GRANTED); + else + callback.Run(blink::mojom::PermissionStatus::DENIED); + return content::PermissionController::kNoPendingOperation; } // Audio and video-capture should not come this way currently Q_ASSERT(permissionType != ProfileAdapter::AudioCapturePermission && permissionType != ProfileAdapter::VideoCapturePermission); - content::WebContents *webContents = frameHost->GetRenderViewHost()->GetDelegate()->GetAsWebContents(); - WebContentsDelegateQt* contentsDelegate = static_cast<WebContentsDelegateQt*>(webContents->GetDelegate()); - Q_ASSERT(contentsDelegate); + int request_id = ++m_requestIdCount; RequestOrSubscription request = { permissionType, toQt(requesting_origin), @@ -175,6 +189,9 @@ int PermissionManagerQt::RequestPermission(content::PermissionType permission, m_requests.insert(request_id, request); if (permissionType == ProfileAdapter::GeolocationPermission) contentsDelegate->requestGeolocationPermission(request.origin); + else if (permissionType == ProfileAdapter::NotificationPermission) + contentsDelegate->requestUserNotificationPermission(request.origin); + return request_id; } @@ -184,6 +201,10 @@ int PermissionManagerQt::RequestPermissions(const std::vector<content::Permissio bool /*user_gesture*/, const base::Callback<void(const std::vector<blink::mojom::PermissionStatus>&)>& callback) { + WebContentsDelegateQt *contentsDelegate = static_cast<WebContentsDelegateQt *>( + content::WebContents::FromRenderFrameHost(frameHost)->GetDelegate()); + Q_ASSERT(contentsDelegate); + bool answerable = true; std::vector<blink::mojom::PermissionStatus> result; result.reserve(permissions.size()); @@ -191,7 +212,14 @@ int PermissionManagerQt::RequestPermissions(const std::vector<content::Permissio const ProfileAdapter::PermissionType permissionType = toQt(permission); if (permissionType == ProfileAdapter::UnsupportedPermission) result.push_back(blink::mojom::PermissionStatus::DENIED); - else { + else if (permissionType == ProfileAdapter::ClipboardRead) { + WebEngineSettings *settings = contentsDelegate->webEngineSettings(); + if (settings->testAttribute(WebEngineSettings::JavascriptCanAccessClipboard) + && settings->testAttribute(WebEngineSettings::JavascriptCanPaste)) + result.push_back(blink::mojom::PermissionStatus::GRANTED); + else + result.push_back(blink::mojom::PermissionStatus::DENIED); + } else { answerable = false; break; } @@ -202,9 +230,6 @@ int PermissionManagerQt::RequestPermissions(const std::vector<content::Permissio } int request_id = ++m_requestIdCount; - content::WebContents *webContents = frameHost->GetRenderViewHost()->GetDelegate()->GetAsWebContents(); - WebContentsDelegateQt* contentsDelegate = static_cast<WebContentsDelegateQt*>(webContents->GetDelegate()); - Q_ASSERT(contentsDelegate); MultiRequest request = { permissions, toQt(requesting_origin), @@ -215,6 +240,8 @@ int PermissionManagerQt::RequestPermissions(const std::vector<content::Permissio const ProfileAdapter::PermissionType permissionType = toQt(permission); if (permissionType == ProfileAdapter::GeolocationPermission) contentsDelegate->requestGeolocationPermission(request.origin); + else if (permissionType == ProfileAdapter::NotificationPermission) + contentsDelegate->requestUserNotificationPermission(request.origin); } return request_id; } @@ -241,6 +268,18 @@ blink::mojom::PermissionStatus PermissionManagerQt::GetPermissionStatusForFrame( content::RenderFrameHost *render_frame_host, const GURL &requesting_origin) { + if (permission == content::PermissionType::CLIPBOARD_READ || + permission == content::PermissionType::CLIPBOARD_WRITE) { + WebContentsDelegateQt *delegate = static_cast<WebContentsDelegateQt *>( + content::WebContents::FromRenderFrameHost(render_frame_host)->GetDelegate()); + if (!delegate->webEngineSettings()->testAttribute(WebEngineSettings::JavascriptCanAccessClipboard)) + return blink::mojom::PermissionStatus::DENIED; + if (permission == content::PermissionType::CLIPBOARD_READ && + !delegate->webEngineSettings()->testAttribute(WebEngineSettings::JavascriptCanPaste)) + return blink::mojom::PermissionStatus::DENIED; + return blink::mojom::PermissionStatus::GRANTED; + } + return GetPermissionStatus( permission, requesting_origin, diff --git a/src/core/platform_notification_service_qt.cpp b/src/core/platform_notification_service_qt.cpp new file mode 100644 index 000000000..dff3aed61 --- /dev/null +++ b/src/core/platform_notification_service_qt.cpp @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "platform_notification_service_qt.h" + +#include "chrome/common/pref_names.h" +#include "components/prefs/pref_service.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/permission_type.h" +#include "content/public/browser/notification_event_dispatcher.h" +#include "ui/message_center/public/cpp/notification_delegate.h" + +#include "profile_adapter.h" +#include "profile_adapter_client.h" +#include "profile_qt.h" +#include "user_notification_controller.h" +#include "resource_context_qt.h" +#include "type_conversion.h" + +#include <QSharedPointer> + +namespace QtWebEngineCore { + +struct NonPersistentNotificationDelegate : UserNotificationController::Delegate { + NonPersistentNotificationDelegate(const std::string &id) : notification_id(id) { } + + virtual void shown() override { + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + if (auto inst = content::NotificationEventDispatcher::GetInstance()) + inst->DispatchNonPersistentShowEvent(notification_id); + } + + virtual void clicked() override { + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + if (auto inst = content::NotificationEventDispatcher::GetInstance()) + inst->DispatchNonPersistentClickEvent(notification_id, base::DoNothing()); + } + + virtual void closed(bool /*by_user*/) override { + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + if (auto inst = content::NotificationEventDispatcher::GetInstance()) + inst->DispatchNonPersistentCloseEvent(notification_id, base::DoNothing()); + } + + const std::string notification_id; +}; + +struct PersistentNotificationDelegate : UserNotificationController::Delegate { + PersistentNotificationDelegate(content::BrowserContext *context, const std::string &id, const GURL &origin) + : browser_context(context), notification_id(id), origin(origin) { } + + virtual void shown() override { } + + virtual void clicked() override { + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + if (auto inst = content::NotificationEventDispatcher::GetInstance()) + inst->DispatchNotificationClickEvent(browser_context, notification_id, origin, base::nullopt, base::nullopt, base::DoNothing()); + } + + virtual void closed(bool by_user) override { + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + if (auto inst = content::NotificationEventDispatcher::GetInstance()) + inst->DispatchNotificationCloseEvent(browser_context, notification_id, origin, by_user, base::DoNothing()); + } + + content::BrowserContext *browser_context; + const std::string notification_id; + const GURL origin; +}; + + +PlatformNotificationServiceQt::PlatformNotificationServiceQt() {} + +PlatformNotificationServiceQt::~PlatformNotificationServiceQt() {} + +void PlatformNotificationServiceQt::DisplayNotification( + content::BrowserContext *browser_context, + const std::string ¬ification_id, + const GURL &origin, + const blink::PlatformNotificationData ¬ificationData, + const blink::NotificationResources ¬ificationResources) +{ + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + ProfileQt *profile = static_cast<ProfileQt*>(browser_context); + + auto delegate = new NonPersistentNotificationDelegate(notification_id); + QSharedPointer<UserNotificationController> controller( + new UserNotificationController(notificationData, notificationResources, origin, delegate)); + + profile->profileAdapter()->ephemeralNotifications().insert(QByteArray::fromStdString(notification_id), controller); + + const QList<ProfileAdapterClient *> clients = profile->profileAdapter()->clients(); + for (ProfileAdapterClient *client : clients) + client->showNotification(controller); +} + +void PlatformNotificationServiceQt::DisplayPersistentNotification( + content::BrowserContext *browser_context, + const std::string ¬ification_id, + const GURL &service_worker_origin, + const GURL &origin, + const blink::PlatformNotificationData ¬ificationData, + const blink::NotificationResources ¬ificationResources) +{ + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + ProfileQt * profile = static_cast<ProfileQt*>(browser_context); + + auto delegate = new PersistentNotificationDelegate(profile, notification_id, service_worker_origin); + QSharedPointer<UserNotificationController> controller( + new UserNotificationController(notificationData, notificationResources, service_worker_origin, delegate)); + + profile->profileAdapter()->persistentNotifications().insert(QByteArray::fromStdString(notification_id), controller); + const QList<ProfileAdapterClient *> clients = profile->profileAdapter()->clients(); + for (ProfileAdapterClient *client : clients) + client->showNotification(controller); +} + +void PlatformNotificationServiceQt::CloseNotification( + content::BrowserContext *browser_context, + const std::string ¬ification_id) +{ + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + ProfileQt *profile = static_cast<ProfileQt*>(browser_context); + + QSharedPointer<UserNotificationController> notificationController = + profile->profileAdapter()->ephemeralNotifications().take(QByteArray::fromStdString(notification_id)).lock(); + if (notificationController) + notificationController->closeNotification(); +} + +void PlatformNotificationServiceQt::ClosePersistentNotification( + content::BrowserContext *browser_context, + const std::string ¬ification_id) +{ + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + ProfileQt *profile = static_cast<ProfileQt*>(browser_context); + + QSharedPointer<UserNotificationController> notificationController = + profile->profileAdapter()->persistentNotifications().take(QByteArray::fromStdString(notification_id)); + if (notificationController) + notificationController->closeNotification(); +} + +void PlatformNotificationServiceQt::GetDisplayedNotifications( + content::BrowserContext *browser_context, + const DisplayedNotificationsCallback &callback) +{ + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + ProfileQt *profile = static_cast<ProfileQt *>(browser_context); + + std::unique_ptr<std::set<std::string>> movableStdStringSet = std::make_unique<std::set<std::string>>(); + auto it = profile->profileAdapter()->persistentNotifications().constBegin(); + const auto end = profile->profileAdapter()->persistentNotifications().constEnd(); + while (it != end) { + if (it.value()->isShown()) + movableStdStringSet->insert(it.key().toStdString()); + ++it; + } + + callback.Run(std::move(movableStdStringSet), true /* supports_synchronization */); +} + +int64_t PlatformNotificationServiceQt::ReadNextPersistentNotificationId(content::BrowserContext *browser_context) +{ + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + auto prefs = static_cast<ProfileQt *>(browser_context)->GetPrefs(); + int64_t nextId = prefs->GetInteger(prefs::kNotificationNextPersistentId) + 1; + prefs->SetInt64(prefs::kNotificationNextPersistentId, nextId); + return nextId; +} + +} // namespace QtWebEngineCore diff --git a/src/core/platform_notification_service_qt.h b/src/core/platform_notification_service_qt.h new file mode 100644 index 000000000..66cee9ed0 --- /dev/null +++ b/src/core/platform_notification_service_qt.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 PLATFORM_NOTIFICATION_SERVICE_QT_H +#define PLATFORM_NOTIFICATION_SERVICE_QT_H + +#include "content/public/browser/platform_notification_service.h" + +namespace QtWebEngineCore { + +class PlatformNotificationServiceQt : public content::PlatformNotificationService { +public: + PlatformNotificationServiceQt(); + ~PlatformNotificationServiceQt() override; + + // Displays the notification described in |notification_data| to the user. A + // closure through which the notification can be closed will be stored in the + // |cancel_callback| argument. This method must be called on the UI thread. + void DisplayNotification(content::BrowserContext* browser_context, + const std::string& notification_id, + const GURL& origin, + const blink::PlatformNotificationData& notificationData, + const blink::NotificationResources& notificationResources) override; + + // Displays the persistent notification described in |notification_data| to + // the user. This method must be called on the UI thread. + void DisplayPersistentNotification(content::BrowserContext* browser_context, + const std::string& notification_id, + const GURL& service_worker_origin, + const GURL& origin, + const blink::PlatformNotificationData& notification_data, + const blink::NotificationResources& notification_resources) override; + + // Closes the notification identified by |notification_id|. + // This method must be called on the UI thread. + void CloseNotification(content::BrowserContext* browser_context, const std::string& notification_id) override; + + // Closes the persistent notification identified by |persistent_notification_id|. + // This method must be called on the UI thread. + void ClosePersistentNotification(content::BrowserContext* browser_context, const std::string& notification_id) override; + + // Retrieves the ids of all currently displaying notifications and + // posts |callback| with the result. + void GetDisplayedNotifications(content::BrowserContext* browser_context, const DisplayedNotificationsCallback& callback) override; + + // Reads the value of the next persistent notification ID from the profile and + // increments the value, as it is called once per notification write. + virtual int64_t ReadNextPersistentNotificationId(content::BrowserContext* browser_context) override; + + // Records a given notification to UKM. + virtual void RecordNotificationUkmEvent(content::BrowserContext*, const content::NotificationDatabaseData&) override { } +}; + +} // namespace QtWebEngineCore + +#endif // PLATFORM_NOTIFICATION_SERVICE_QT_H diff --git a/src/core/profile_adapter.cpp b/src/core/profile_adapter.cpp index 48a6906e9..8ddeb5b8f 100644 --- a/src/core/profile_adapter.cpp +++ b/src/core/profile_adapter.cpp @@ -83,12 +83,12 @@ namespace QtWebEngineCore { ProfileAdapter::ProfileAdapter(const QString &storageName): m_name(storageName) , m_offTheRecord(storageName.isEmpty()) + , m_downloadPath(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)) , m_httpCacheType(DiskHttpCache) , m_persistentCookiesPolicy(AllowPersistentCookies) , m_visitedLinksPolicy(TrackVisitedLinksOnDisk) , m_httpCacheMaxSize(0) , m_pageRequestInterceptors(0) - , m_downloadPath(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)) { WebEngineContext::current()->addProfileAdapter(this); // creation of profile requires webengine context diff --git a/src/core/profile_adapter.h b/src/core/profile_adapter.h index 3b1b9bc91..482835010 100644 --- a/src/core/profile_adapter.h +++ b/src/core/profile_adapter.h @@ -69,8 +69,9 @@ QT_FORWARD_DECLARE_CLASS(QObject) namespace QtWebEngineCore { -class ProfileAdapterClient; +class UserNotificationController; class DownloadManagerDelegateQt; +class ProfileAdapterClient; class ProfileQt; class UserResourceControllerHost; class VisitedLinksManagerQt; @@ -153,10 +154,11 @@ public: enum PermissionType { UnsupportedPermission = 0, GeolocationPermission = 1, -// Reserved: -// NotificationPermission = 2, + NotificationPermission = 2, AudioCapturePermission = 3, VideoCapturePermission = 4, + ClipboardRead = 5, + ClipboardWrite = 6, }; HttpCacheType httpCacheType() const; @@ -198,6 +200,11 @@ public: void removePageRequestInterceptor(); bool hasPageRequestInterceptor() const { return m_pageRequestInterceptors > 0; } + QHash<QByteArray, QWeakPointer<UserNotificationController>> &ephemeralNotifications() + { return m_ephemeralNotifications; } + QHash<QByteArray, QSharedPointer<UserNotificationController>> &persistentNotifications() + { return m_persistentNotifications; } + private: void updateCustomUrlSchemeHandlers(); void resetVisitedLinksManager(); @@ -222,6 +229,9 @@ private: PersistentCookiesPolicy m_persistentCookiesPolicy; VisitedLinksPolicy m_visitedLinksPolicy; QHash<QByteArray, QPointer<QWebEngineUrlSchemeHandler>> m_customUrlSchemeHandlers; + QHash<QByteArray, QWeakPointer<UserNotificationController>> m_ephemeralNotifications; + QHash<QByteArray, QSharedPointer<UserNotificationController>> m_persistentNotifications; + QList<ProfileAdapterClient*> m_clients; int m_httpCacheMaxSize; int m_pageRequestInterceptors; diff --git a/src/core/profile_adapter_client.h b/src/core/profile_adapter_client.h index 4711f8bcf..0309200b4 100644 --- a/src/core/profile_adapter_client.h +++ b/src/core/profile_adapter_client.h @@ -52,12 +52,14 @@ #define PROFILE_ADAPTER_CLIENT_H #include "qtwebenginecoreglobal_p.h" +#include <QSharedPointer> #include <QString> #include <QUrl> namespace QtWebEngineCore { class WebContentsAdapterClient; +class UserNotificationController; class QWEBENGINECORE_PRIVATE_EXPORT ProfileAdapterClient { @@ -143,6 +145,8 @@ public: virtual void downloadRequested(DownloadItemInfo &info) = 0; virtual void downloadUpdated(const DownloadItemInfo &info) = 0; virtual void useForGlobalCertificateVerificationChanged() {} + virtual void showNotification(QSharedPointer<UserNotificationController> &) { } + static QString downloadInterruptReasonToString(DownloadInterruptReason reason); }; diff --git a/src/core/profile_io_data_qt.cpp b/src/core/profile_io_data_qt.cpp index 83886ca54..01a4e98aa 100644 --- a/src/core/profile_io_data_qt.cpp +++ b/src/core/profile_io_data_qt.cpp @@ -49,6 +49,7 @@ #include "content/public/common/content_features.h" #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h" #include "chrome/browser/net/chrome_mojo_proxy_resolver_factory.h" +#include "components/proxy_config/pref_proxy_config_tracker_impl.h" #include "net/cert/cert_verifier.h" #include "net/cert/ct_log_verifier.h" #include "net/cert/ct_policy_enforcer.h" @@ -77,6 +78,7 @@ #include "services/file/user_id_map.h" #include "services/network/proxy_service_mojo.h" +#include "net/client_cert_override.h" #include "net/cookie_monster_delegate_qt.h" #include "net/custom_protocol_handler.h" #include "net/network_delegate_qt.h" @@ -187,6 +189,9 @@ ProfileIODataQt::~ProfileIODataQt() #endif } + if (m_urlRequestContext && m_urlRequestContext->proxy_resolution_service()) + m_urlRequestContext->proxy_resolution_service()->OnShutdown(); + m_resourceContext.reset(); if (m_cookieDelegate) m_cookieDelegate->setCookieMonster(0); // this will let CookieMonsterDelegateQt be deleted @@ -194,6 +199,11 @@ ProfileIODataQt::~ProfileIODataQt() delete m_proxyConfigService.fetchAndStoreAcquire(0); } +QPointer<ProfileAdapter> ProfileIODataQt::profileAdapter() +{ + return m_profileAdapter; +} + void ProfileIODataQt::shutdownOnUIThread() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); @@ -233,10 +243,10 @@ void ProfileIODataQt::initializeOnIOThread() // this binds factory to io thread m_weakPtr = m_weakPtrFactory.GetWeakPtr(); QMutexLocker lock(&m_mutex); - m_initialized = true; generateAllStorage(); generateJobFactory(); setGlobalCertificateVerification(); + m_initialized = true; } void ProfileIODataQt::initializeOnUIThread() @@ -251,6 +261,7 @@ void ProfileIODataQt::initializeOnUIThread() protocolHandlerRegistry->CreateJobInterceptorFactory(); m_cookieDelegate = new CookieMonsterDelegateQt(); m_cookieDelegate->setClient(m_profile->profileAdapter()->cookieStore()); + createProxyConfig(); } void ProfileIODataQt::cancelAllUrlRequests() @@ -286,11 +297,12 @@ void ProfileIODataQt::generateStorage() // We must stop all requests before deleting their backends. if (m_storage) { - m_cookieDelegate->setCookieMonster(0); - m_storage->set_cookie_store(0); + m_urlRequestContext->proxy_resolution_service()->OnShutdown(); + m_cookieDelegate->setCookieMonster(nullptr); + m_storage->set_cookie_store(nullptr); cancelAllUrlRequests(); // we need to get rid of dangling pointer due to coming storage deletion - m_urlRequestContext->set_http_transaction_factory(0); + m_urlRequestContext->set_http_transaction_factory(nullptr); m_httpNetworkSession.reset(); m_transportSecurityPersister.reset(); } @@ -310,29 +322,23 @@ void ProfileIODataQt::generateStorage() m_storage->set_cert_verifier(std::move(cert_verifier)); std::unique_ptr<net::MultiLogCTVerifier> ct_verifier(new net::MultiLogCTVerifier()); -// FIXME: -// ct_verifier->AddLogs(net::ct::CreateLogVerifiersForKnownLogs()); + std::vector<scoped_refptr<const net::CTLogVerifier>> ct_logs; + for (const auto &ct_log : certificate_transparency::GetKnownLogs()) { + scoped_refptr<const net::CTLogVerifier> log_verifier = + net::CTLogVerifier::Create(std::string(ct_log.log_key, ct_log.log_key_length), + ct_log.log_name, + ct_log.log_dns_domain); + if (!log_verifier) + continue; + ct_logs.push_back(std::move(log_verifier)); + } + ct_verifier->AddLogs(ct_logs); m_storage->set_cert_transparency_verifier(std::move(ct_verifier)); m_storage->set_ct_policy_enforcer(base::WrapUnique(new net::DefaultCTPolicyEnforcer())); - - std::unique_ptr<net::HostResolver> host_resolver(net::HostResolver::CreateDefaultResolver(NULL)); - - // The System Proxy Resolver has issues on Windows with unconfigured network cards, - // which is why we want to use the v8 one - if (!m_dhcpPacFileFetcherFactory) - m_dhcpPacFileFetcherFactory.reset(new net::DhcpPacFileFetcherFactory); - - proxy_resolver::mojom::ProxyResolverFactoryPtr proxyResolver(std::move(m_proxyResolverFactoryInterface)); - m_storage->set_proxy_resolution_service(network::CreateProxyResolutionServiceUsingMojoFactory( - std::move(proxyResolver), - std::unique_ptr<net::ProxyConfigService>(proxyConfigService), - net::PacFileFetcherImpl::CreateWithFileUrlSupport(m_urlRequestContext.get()), - m_dhcpPacFileFetcherFactory->Create(m_urlRequestContext.get()), - host_resolver.get(), - nullptr /* NetLog */, - m_networkDelegate.get())); - + m_storage->set_host_resolver(net::HostResolver::CreateDefaultResolver(NULL)); m_storage->set_ssl_config_service(std::make_unique<net::SSLConfigServiceDefaults>()); + m_storage->set_http_auth_handler_factory(net::HttpAuthHandlerFactory::CreateDefault( + m_urlRequestContext->host_resolver())); m_storage->set_transport_security_state(std::make_unique<net::TransportSecurityState>()); if (!m_dataPath.isEmpty()) { @@ -348,20 +354,23 @@ void ProfileIODataQt::generateStorage() background_task_runner); }; - if (!m_httpAuthPreferences) - m_httpAuthPreferences.reset(new net::HttpAuthPreferences()); - - m_storage->set_http_auth_handler_factory(net::HttpAuthHandlerFactory::CreateDefault( - host_resolver.get(), - m_httpAuthPreferences.get() -#if (defined(OS_POSIX) && !defined(OS_ANDROID)) || defined(OS_FUCHSIA) - , std::string() /* gssapi library name */ -#endif - )); m_storage->set_http_server_properties(std::unique_ptr<net::HttpServerProperties>( new net::HttpServerPropertiesImpl)); - // Give |m_storage| ownership at the end in case it's |mapped_host_resolver|. - m_storage->set_host_resolver(std::move(host_resolver)); + + // The System Proxy Resolver has issues on Windows with unconfigured network cards, + // which is why we want to use the v8 one + if (!m_dhcpPacFileFetcherFactory) + m_dhcpPacFileFetcherFactory.reset(new net::DhcpPacFileFetcherFactory); + + proxy_resolver::mojom::ProxyResolverFactoryPtr proxyResolver(std::move(m_proxyResolverFactoryInterface)); + m_storage->set_proxy_resolution_service(network::CreateProxyResolutionServiceUsingMojoFactory( + std::move(proxyResolver), + std::unique_ptr<net::ProxyConfigService>(proxyConfigService), + net::PacFileFetcherImpl::CreateWithFileUrlSupport(m_urlRequestContext.get()), + m_dhcpPacFileFetcherFactory->Create(m_urlRequestContext.get()), + m_urlRequestContext->host_resolver(), + nullptr /* NetLog */, + m_urlRequestContext->network_delegate())); } @@ -369,10 +378,8 @@ void ProfileIODataQt::generateCookieStore() { Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); Q_ASSERT(m_urlRequestContext); - Q_ASSERT(m_storage); QMutexLocker lock(&m_mutex); - m_updateCookieStore = false; scoped_refptr<net::SQLiteChannelIDStore> channel_id_db; if (!m_channelIdPath.isEmpty() && m_persistentCookiesPolicy != ProfileAdapter::NoPersistentCookies) { @@ -386,11 +393,6 @@ void ProfileIODataQt::generateCookieStore() base::WrapUnique(new net::ChannelIDService( new net::DefaultChannelIDStore(channel_id_db.get())))); - // Unset it first to get a chance to destroy and flush the old cookie store before - // opening a new on possibly the same file. - m_cookieDelegate->setCookieMonster(0); - m_storage->set_cookie_store(0); - std::unique_ptr<net::CookieStore> cookieStore; switch (m_persistentCookiesPolicy) { case ProfileAdapter::NoPersistentCookies: @@ -430,11 +432,6 @@ void ProfileIODataQt::generateCookieStore() const std::vector<std::string> cookieableSchemes(kCookieableSchemes, kCookieableSchemes + arraysize(kCookieableSchemes)); cookieMonster->SetCookieableSchemes(cookieableSchemes); - - if (!m_updateAllStorage && m_updateHttpCache) { - // HttpCache needs to be regenerated when we generate a new channel id service - generateHttpCache(); - } } void ProfileIODataQt::generateUserAgent() @@ -444,8 +441,6 @@ void ProfileIODataQt::generateUserAgent() Q_ASSERT(m_storage); QMutexLocker lock(&m_mutex); - m_updateUserAgent = false; - m_storage->set_http_user_agent_settings(std::unique_ptr<net::HttpUserAgentSettings>( new net::StaticHttpUserAgentSettings(m_httpAcceptLanguage.toStdString(), m_httpUserAgent.toStdString()))); @@ -458,10 +453,6 @@ void ProfileIODataQt::generateHttpCache() Q_ASSERT(m_storage); QMutexLocker lock(&m_mutex); - m_updateHttpCache = false; - - if (m_updateCookieStore) - generateCookieStore(); net::HttpCache::DefaultBackend* main_backend = 0; switch (m_httpCacheType) { @@ -628,6 +619,38 @@ void ProfileIODataQt::setFullConfiguration() m_dataPath = m_profileAdapter->dataPath(); } +void ProfileIODataQt::requestStorageGeneration() { + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + QMutexLocker lock(&m_mutex); + if (m_initialized && !m_updateAllStorage) { + m_updateAllStorage = true; + createProxyConfig(); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&ProfileIODataQt::generateAllStorage, m_weakPtr)); + } +} + +// TODO(miklocek): mojofy ProxyConfigServiceQt +void ProfileIODataQt::createProxyConfig() +{ + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + QMutexLocker lock(&m_mutex); + // We must create the proxy config service on the UI loop on Linux because it + // must synchronously run on the glib message loop. This will be passed to + // the URLRequestContextStorage on the IO thread in GetURLRequestContext(). + Q_ASSERT(m_proxyConfigService == 0); + net::ProxyConfigWithAnnotation initialConfig; + ProxyPrefs::ConfigState initialConfigState = PrefProxyConfigTrackerImpl::ReadPrefConfig( + m_profileAdapter->profile()->GetPrefs(), &initialConfig); + m_proxyConfigService = + new ProxyConfigServiceQt( + net::ProxyResolutionService::CreateSystemProxyConfigService( + base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::IO})), + initialConfig, initialConfigState); + //pass interface to io thread + m_proxyResolverFactoryInterface = ChromeMojoProxyResolverFactory::CreateWithStrongBinding().PassInterface(); +} + void ProfileIODataQt::updateStorageSettings() { Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); @@ -640,23 +663,7 @@ void ProfileIODataQt::updateStorageSettings() file::ForgetServiceUserIdUserDirAssociation(userId); file::AssociateServiceUserIdWithUserDir(userId, toFilePath(m_profileAdapter->dataPath())); } - - if (!m_updateAllStorage) { - m_updateAllStorage = true; - // We must create the proxy config service on the UI loop on Linux because it - // must synchronously run on the glib message loop. This will be passed to - // the URLRequestContextStorage on the IO thread in GetURLRequestContext(). - Q_ASSERT(m_proxyConfigService == 0); - m_proxyConfigService = - new ProxyConfigServiceQt( - net::ProxyResolutionService::CreateSystemProxyConfigService( - base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::IO}))); - //pass interface to io thread - m_proxyResolverFactoryInterface = ChromeMojoProxyResolverFactory::CreateWithStrongBinding().PassInterface(); - if (m_initialized) - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, - base::BindOnce(&ProfileIODataQt::generateAllStorage, m_weakPtr)); - } + requestStorageGeneration(); } void ProfileIODataQt::updateCookieStore() @@ -666,13 +673,7 @@ void ProfileIODataQt::updateCookieStore() m_persistentCookiesPolicy = m_profileAdapter->persistentCookiesPolicy(); m_cookiesPath = m_profileAdapter->cookiesPath(); m_channelIdPath = m_profileAdapter->channelIdPath(); - - if (m_initialized && !m_updateAllStorage && !m_updateCookieStore) { - m_updateCookieStore = true; - m_updateHttpCache = true; - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, - base::BindOnce(&ProfileIODataQt::generateCookieStore, m_weakPtr)); - } + requestStorageGeneration(); } void ProfileIODataQt::updateUserAgent() @@ -681,12 +682,7 @@ void ProfileIODataQt::updateUserAgent() QMutexLocker lock(&m_mutex); m_httpAcceptLanguage = m_profileAdapter->httpAcceptLanguage(); m_httpUserAgent = m_profileAdapter->httpUserAgent(); - - if (m_initialized && !m_updateAllStorage && !m_updateUserAgent) { - m_updateUserAgent = true; - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, - base::BindOnce(&ProfileIODataQt::generateUserAgent, m_weakPtr)); - } + requestStorageGeneration(); } void ProfileIODataQt::updateHttpCache() @@ -705,12 +701,7 @@ void ProfileIODataQt::updateHttpCache() content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB | content::BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB); } - - if (m_initialized && !m_updateAllStorage && !m_updateHttpCache) { - m_updateHttpCache = true; - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, - base::BindOnce(&ProfileIODataQt::generateHttpCache, m_weakPtr)); - } + requestStorageGeneration(); } void ProfileIODataQt::updateJobFactory() @@ -742,6 +733,11 @@ QWebEngineUrlRequestInterceptor *ProfileIODataQt::acquireInterceptor() return m_requestInterceptor; } +QWebEngineUrlRequestInterceptor *ProfileIODataQt::requestInterceptor() +{ + return m_requestInterceptor; +} + bool ProfileIODataQt::hasPageInterceptors() { // used in NetworkDelegateQt::OnBeforeURLRequest @@ -775,4 +771,15 @@ void ProfileIODataQt::updateUsedForGlobalCertificateVerification() base::BindOnce(&ProfileIODataQt::setGlobalCertificateVerification, m_weakPtr)); } +std::unique_ptr<net::ClientCertStore> ProfileIODataQt::CreateClientCertStore() +{ + return std::unique_ptr<net::ClientCertStore>(new ClientCertOverrideStore()); +} + +// static +ProfileIODataQt *ProfileIODataQt::FromResourceContext(content::ResourceContext *resource_context) +{ + return static_cast<ResourceContextQt *>(resource_context)->m_io_data; +} + } // namespace QtWebEngineCore diff --git a/src/core/profile_io_data_qt.h b/src/core/profile_io_data_qt.h index 23497c476..b983b3a99 100644 --- a/src/core/profile_io_data_qt.h +++ b/src/core/profile_io_data_qt.h @@ -51,6 +51,7 @@ #include <QtCore/QMutex> namespace net { +class ClientCertStore; class DhcpPacFileFetcherFactory; class HttpAuthPreferences; class HttpNetworkSession; @@ -80,6 +81,7 @@ public: ProfileIODataQt(ProfileQt *profile); // runs on ui thread virtual ~ProfileIODataQt(); + QPointer<ProfileAdapter> profileAdapter(); content::ResourceContext *resourceContext(); net::URLRequestContext *urlRequestContext(); #if BUILDFLAG(ENABLE_EXTENSIONS) @@ -105,6 +107,7 @@ public: // Used in NetworkDelegateQt::OnBeforeURLRequest. QWebEngineUrlRequestInterceptor *acquireInterceptor(); void releaseInterceptor(); + QWebEngineUrlRequestInterceptor *requestInterceptor(); void setRequestContextData(content::ProtocolHandlerMap *protocolHandlers, content::URLRequestInterceptorScopedVector request_interceptors); @@ -115,9 +118,13 @@ public: void updateHttpCache(); // runs on ui thread void updateJobFactory(); // runs on ui thread void updateRequestInterceptor(); // runs on ui thread + void requestStorageGeneration(); //runs on ui thread + void createProxyConfig(); //runs on ui thread void updateUsedForGlobalCertificateVerification(); // runs on ui thread bool hasPageInterceptors(); + std::unique_ptr<net::ClientCertStore> CreateClientCertStore(); + static ProfileIODataQt *FromResourceContext(content::ResourceContext *resource_context); private: ProfileQt *m_profile; std::unique_ptr<net::URLRequestContextStorage> m_storage; @@ -152,10 +159,7 @@ private: int m_httpCacheMaxSize = 0; bool m_initialized = false; bool m_updateAllStorage = false; - bool m_updateCookieStore = false; - bool m_updateHttpCache = false; bool m_updateJobFactory = false; - bool m_updateUserAgent = false; bool m_ignoreCertificateErrors = false; bool m_useForGlobalCertificateVerification = false; bool m_hasPageInterceptors = false; diff --git a/src/core/profile_qt.cpp b/src/core/profile_qt.cpp index 68e933d85..3bfbcd3a2 100644 --- a/src/core/profile_qt.cpp +++ b/src/core/profile_qt.cpp @@ -41,6 +41,7 @@ #include "profile_adapter.h" #include "browsing_data_remover_delegate_qt.h" +#include "command_line_pref_store_qt.h" #include "download_manager_delegate_qt.h" #include "net/ssl_host_state_delegate_qt.h" #include "net/url_request_context_getter_qt.h" @@ -48,6 +49,7 @@ #include "qtwebenginecoreglobal_p.h" #include "type_conversion.h" #include "web_engine_library_info.h" +#include "web_engine_context.h" #include "base/time/time.h" #include "content/public/browser/browser_thread.h" @@ -62,9 +64,10 @@ #include "components/prefs/pref_service_factory.h" #include "components/prefs/pref_registry_simple.h" #include "components/user_prefs/user_prefs.h" +#include "components/proxy_config/pref_proxy_config_tracker_impl.h" +#include "chrome/common/pref_names.h" #if QT_CONFIG(webengine_spellchecker) #include "chrome/browser/spellchecker/spellcheck_service.h" -#include "chrome/common/pref_names.h" #include "components/spellcheck/browser/pref_names.h" #endif @@ -89,8 +92,10 @@ ProfileQt::ProfileQt(ProfileAdapter *profileAdapter) { PrefServiceFactory factory; factory.set_user_prefs(new InMemoryPrefStore); + factory.set_command_line_prefs(base::MakeRefCounted<CommandLinePrefStoreQt>( + WebEngineContext::commandLine())); PrefRegistrySimple *registry = new PrefRegistrySimple(); - + PrefProxyConfigTrackerImpl::RegisterPrefs(registry); #if QT_CONFIG(webengine_spellchecker) // Initial spellcheck settings registry->RegisterStringPref(prefs::kAcceptLanguages, std::string()); @@ -101,6 +106,7 @@ ProfileQt::ProfileQt(ProfileAdapter *profileAdapter) registry->RegisterBooleanPref(spellcheck::prefs::kSpellCheckUseSpellingService, false); #endif // QT_CONFIG(webengine_spellchecker) registry->RegisterBooleanPref(prefs::kShowInternalAccessibilityTree, false); + registry->RegisterIntegerPref(prefs::kNotificationNextPersistentId, 10000); #if BUILDFLAG(ENABLE_EXTENSIONS) registry->RegisterDictionaryPref(extensions::pref_names::kExtensions); diff --git a/src/core/qtwebengine.gni b/src/core/qtwebengine.gni index b7f10833a..7c0ca763e 100644 --- a/src/core/qtwebengine.gni +++ b/src/core/qtwebengine.gni @@ -27,6 +27,7 @@ deps = [ "//components/web_cache/browser", "//components/web_cache/renderer", "//components/spellcheck:buildflags", + "//components/proxy_config", "//content/public/app:browser", "//content", "//media:media_buildflags", diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp index 0217bed59..9791c8c46 100644 --- a/src/core/render_widget_host_view_qt.cpp +++ b/src/core/render_widget_host_view_qt.cpp @@ -40,7 +40,6 @@ #include "render_widget_host_view_qt.h" #include "browser_accessibility_manager_qt.h" -#include "chromium_overrides.h" #include "compositor/compositor.h" #include "qtwebenginecoreglobal_p.h" #include "render_widget_host_view_qt_delegate.h" @@ -67,7 +66,6 @@ #include "ui/events/gesture_detection/gesture_configuration.h" #include "ui/events/gesture_detection/gesture_provider_config_helper.h" #include "ui/events/gesture_detection/motion_event.h" -#include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/image/image_skia.h" #include "ui/touch_selection/touch_selection_controller.h" @@ -248,6 +246,22 @@ private: int index; }; +static content::ScreenInfo screenInfoFromQScreen(QScreen *screen) +{ + content::ScreenInfo r; + if (screen) { + r.device_scale_factor = screen->devicePixelRatio(); + r.depth_per_component = 8; + r.depth = screen->depth(); + r.is_monochrome = (r.depth == 1); + r.rect = toGfx(screen->geometry()); + r.available_rect = toGfx(screen->availableGeometry()); + } else { + r.device_scale_factor = qGuiApp->devicePixelRatio(); + } + return r; +} + RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost *widget) : content::RenderWidgetHostViewBase::RenderWidgetHostViewBase(widget) , m_gestureProvider(QtGestureProviderConfig(), this) @@ -264,7 +278,6 @@ RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost *widget , m_cursorPosition(0) , m_emptyPreviousSelection(true) , m_wheelAckPending(false) - , m_pendingResize(false) , m_mouseWheelPhaseHandler(this) // This frame-sink id is based on what RenderWidgetHostViewChildFrame does: , m_frameSinkId(base::checked_cast<uint32_t>(widget->GetProcess()->GetID()), @@ -303,6 +316,7 @@ RenderWidgetHostViewQt::~RenderWidgetHostViewQt() void RenderWidgetHostViewQt::setDelegate(RenderWidgetHostViewQtDelegate* delegate) { m_delegate.reset(delegate); + visualPropertiesChanged(); } void RenderWidgetHostViewQt::setAdapterClient(WebContentsAdapterClient *adapterClient) @@ -329,30 +343,16 @@ void RenderWidgetHostViewQt::InitAsFullscreen(content::RenderWidgetHostView*) { } -void RenderWidgetHostViewQt::SetSize(const gfx::Size& size) +void RenderWidgetHostViewQt::SetSize(const gfx::Size &sizeInDips) { - int width = size.width(); - int height = size.height(); - - m_delegate->resize(width,height); + m_delegate->resize(sizeInDips.width(), sizeInDips.height()); } -void RenderWidgetHostViewQt::SetBounds(const gfx::Rect& screenRect) +void RenderWidgetHostViewQt::SetBounds(const gfx::Rect &windowRectInDips) { - // This is called when webkit has sent us a Move message. - if (IsPopup()) - m_delegate->move(toQt(screenRect.origin())); - SetSize(screenRect.size()); -} - -gfx::Size RenderWidgetHostViewQt::GetCompositorViewportPixelSize() const -{ - if (!m_delegate || !m_delegate->window() || !m_delegate->window()->screen()) - return gfx::Size(); - - const QScreen* screen = m_delegate->window()->screen(); - gfx::SizeF size = toGfx(m_delegate->screenRect().size()); - return gfx::ToCeiledSize(gfx::ScaleSize(size, screen->devicePixelRatio())); + DCHECK(IsPopup()); + m_delegate->move(toQt(windowRectInDips.origin())); + m_delegate->resize(windowRectInDips.width(), windowRectInDips.height()); } gfx::NativeView RenderWidgetHostViewQt::GetNativeView() const @@ -431,10 +431,7 @@ bool RenderWidgetHostViewQt::IsShowing() // Retrieve the bounds of the View, in screen coordinates. gfx::Rect RenderWidgetHostViewQt::GetViewBounds() const { - QRectF p = m_delegate->contentsRect(); - gfx::Point p1(floor(p.x()), floor(p.y())); - gfx::Point p2(ceil(p.right()), ceil(p.bottom())); - return gfx::BoundingRect(p1, p2); + return m_viewRectInDips; } void RenderWidgetHostViewQt::UpdateBackgroundColor() @@ -588,15 +585,13 @@ void RenderWidgetHostViewQt::DisplayCursor(const content::WebCursor &webCursor) } #if defined(USE_AURA) if (auraType != ui::CursorType::kNull) { - QWindow *window = m_delegate->window(); - qreal windowDpr = window ? window->devicePixelRatio() : 1.0f; int resourceId; gfx::Point hotspot; // GetCursorDataFor only knows hotspots for 1x and 2x cursor images, in physical pixels. - qreal hotspotDpr = windowDpr <= 1.0f ? 1.0f : 2.0f; + qreal hotspotDpr = m_screenInfo.device_scale_factor <= 1.0f ? 1.0f : 2.0f; if (ui::GetCursorDataFor(ui::CursorSize::kNormal, auraType, hotspotDpr, &resourceId, &hotspot)) { if (const gfx::ImageSkia *imageSkia = ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resourceId)) { - QImage imageQt = toQImage(imageSkia->GetRepresentation(windowDpr)); + QImage imageQt = toQImage(imageSkia->GetRepresentation(m_screenInfo.device_scale_factor)); // Convert hotspot coordinates into device-independent pixels. qreal hotX = hotspot.x() / hotspotDpr; @@ -693,28 +688,16 @@ void RenderWidgetHostViewQt::SubmitCompositorFrame(const viz::LocalSurfaceId &lo m_adapterClient->updateScrollPosition(toQt(m_lastScrollOffset)); if (contentsSizeChanged) m_adapterClient->updateContentsSize(toQt(m_lastContentsSize)); - - if (m_pendingResize && host()) { - if (host()->SynchronizeVisualProperties()) - m_pendingResize = false; - } } void RenderWidgetHostViewQt::GetScreenInfo(content::ScreenInfo *results) const { - QWindow *window = m_delegate->window(); - if (!window) - return; - GetScreenInfoFromNativeWindow(window, results); + *results = m_screenInfo; } gfx::Rect RenderWidgetHostViewQt::GetBoundsInRootWindow() { - if (!m_delegate->window()) - return gfx::Rect(); - - QRect r = m_delegate->window()->frameGeometry(); - return gfx::Rect(r.x(), r.y(), r.width(), r.height()); + return m_windowRectInDips; } void RenderWidgetHostViewQt::ClearCompositorFrame() @@ -922,15 +905,7 @@ viz::ScopedSurfaceIdAllocator RenderWidgetHostViewQt::DidUpdateVisualProperties( void RenderWidgetHostViewQt::OnDidUpdateVisualPropertiesComplete(const cc::RenderFrameMetadata &metadata) { - if (metadata.local_surface_id) - m_localSurfaceIdAllocator.UpdateFromChild(*metadata.local_surface_id); - - m_localSurfaceIdAllocator.GenerateId(); - host()->SendScreenRects(); - if (m_pendingResize) { - if (host()->SynchronizeVisualProperties()) - m_pendingResize = false; - } + synchronizeVisualProperties(metadata.local_surface_id); } QSGNode *RenderWidgetHostViewQt::updatePaintNode(QSGNode *oldNode) @@ -938,13 +913,6 @@ QSGNode *RenderWidgetHostViewQt::updatePaintNode(QSGNode *oldNode) return m_compositor->updatePaintNode(oldNode, m_delegate.get()); } -void RenderWidgetHostViewQt::notifyResize() -{ - m_pendingResize = true; - if (host()->SynchronizeVisualProperties()) - m_pendingResize = false; -} - void RenderWidgetHostViewQt::notifyShown() { host()->WasShown(false); @@ -955,17 +923,26 @@ void RenderWidgetHostViewQt::notifyHidden() host()->WasHidden(); } -void RenderWidgetHostViewQt::windowBoundsChanged() +void RenderWidgetHostViewQt::visualPropertiesChanged() { - host()->SendScreenRects(); - if (m_delegate && m_delegate->window()) - host()->NotifyScreenInfoChanged(); -} + if (!m_delegate) + return; -void RenderWidgetHostViewQt::windowChanged() -{ - if (m_delegate && m_delegate->window()) - host()->NotifyScreenInfoChanged(); + gfx::Rect oldViewRect = m_viewRectInDips; + m_viewRectInDips = toGfx(m_delegate->viewGeometry().toAlignedRect()); + + gfx::Rect oldWindowRect = m_windowRectInDips; + QWindow *window = m_delegate->window(); + m_windowRectInDips = window ? toGfx(window->frameGeometry()) : gfx::Rect(); + + content::ScreenInfo oldScreenInfo = m_screenInfo; + m_screenInfo = screenInfoFromQScreen(window ? window->screen() : nullptr); + + if (m_viewRectInDips != oldViewRect || m_windowRectInDips != oldWindowRect) + host()->SendScreenRects(); + + if (m_viewRectInDips.size() != oldViewRect.size() || m_screenInfo != oldScreenInfo) + synchronizeVisualProperties(base::nullopt); } bool RenderWidgetHostViewQt::forwardEvent(QEvent *event) @@ -1749,4 +1726,14 @@ void RenderWidgetHostViewQt::OnRenderFrameMetadataChangedAfterActivation() } } +void RenderWidgetHostViewQt::synchronizeVisualProperties(const base::Optional<viz::LocalSurfaceId> &childSurfaceId) +{ + if (childSurfaceId) + m_localSurfaceIdAllocator.UpdateFromChild(*childSurfaceId); + else + m_localSurfaceIdAllocator.GenerateId(); + + host()->SynchronizeVisualProperties(); +} + } // namespace QtWebEngineCore diff --git a/src/core/render_widget_host_view_qt.h b/src/core/render_widget_host_view_qt.h index f7bd82f03..70b4f509e 100644 --- a/src/core/render_widget_host_view_qt.h +++ b/src/core/render_widget_host_view_qt.h @@ -124,7 +124,6 @@ public: void InitAsFullscreen(content::RenderWidgetHostView*) override; void SetSize(const gfx::Size& size) override; void SetBounds(const gfx::Rect&) override; - gfx::Size GetCompositorViewportPixelSize() const override; gfx::NativeView GetNativeView() const override; gfx::NativeViewAccessible GetNativeViewAccessible() override; void Focus() override; @@ -176,11 +175,9 @@ public: // Overridden from RenderWidgetHostViewQtDelegateClient. QSGNode *updatePaintNode(QSGNode *) override; - void notifyResize() override; void notifyShown() override; void notifyHidden() override; - void windowBoundsChanged() override; - void windowChanged() override; + void visualPropertiesChanged() override; bool forwardEvent(QEvent *) override; QVariant inputMethodQuery(Qt::InputMethodQuery query) override; void closePopup() override; @@ -234,13 +231,20 @@ private: void processMotionEvent(const ui::MotionEvent &motionEvent); void clearPreviousTouchMotionState(); QList<QTouchEvent::TouchPoint> mapTouchPointIds(const QList<QTouchEvent::TouchPoint> &inputPoints); - void updateNeedsBeginFramesInternal(); bool IsPopup() const; void selectionChanged(); content::RenderFrameHost *getFocusedFrameHost(); + void synchronizeVisualProperties(const base::Optional<viz::LocalSurfaceId> &childSurfaceId); + + // Geometry of the view in screen DIPs. + gfx::Rect m_viewRectInDips; + // Geometry of the window, including frame, in screen DIPs. + gfx::Rect m_windowRectInDips; + content::ScreenInfo m_screenInfo; + ui::FilteredGestureProvider m_gestureProvider; base::TimeDelta m_eventsToNowDelta; bool m_sendMotionActionDown; @@ -274,7 +278,6 @@ private: bool m_imeHasHiddenTextCapability; bool m_wheelAckPending; - bool m_pendingResize; QList<blink::WebMouseWheelEvent> m_pendingWheelEvents; content::MouseWheelPhaseHandler m_mouseWheelPhaseHandler; viz::FrameSinkId m_frameSinkId; diff --git a/src/core/render_widget_host_view_qt_delegate.h b/src/core/render_widget_host_view_qt_delegate.h index 5ce595502..e2c98360d 100644 --- a/src/core/render_widget_host_view_qt_delegate.h +++ b/src/core/render_widget_host_view_qt_delegate.h @@ -78,11 +78,9 @@ class QWEBENGINECORE_PRIVATE_EXPORT RenderWidgetHostViewQtDelegateClient { public: virtual ~RenderWidgetHostViewQtDelegateClient() { } virtual QSGNode *updatePaintNode(QSGNode *) = 0; - virtual void notifyResize() = 0; virtual void notifyShown() = 0; virtual void notifyHidden() = 0; - virtual void windowBoundsChanged() = 0; - virtual void windowChanged() = 0; + virtual void visualPropertiesChanged() = 0; virtual bool forwardEvent(QEvent *) = 0; virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) = 0; virtual void closePopup() = 0; @@ -92,8 +90,7 @@ class QWEBENGINECORE_PRIVATE_EXPORT RenderWidgetHostViewQtDelegate { public: virtual ~RenderWidgetHostViewQtDelegate() { } virtual void initAsPopup(const QRect&) = 0; - virtual QRectF screenRect() const = 0; - virtual QRectF contentsRect() const = 0; + virtual QRectF viewGeometry() const = 0; virtual void setKeyboardFocus() = 0; virtual bool hasKeyboardFocus() = 0; virtual void lockMouse() = 0; diff --git a/src/core/resource_context_qt.h b/src/core/resource_context_qt.h index 08359c79c..ccbe2c364 100644 --- a/src/core/resource_context_qt.h +++ b/src/core/resource_context_qt.h @@ -69,6 +69,7 @@ public: extensions::ExtensionSystemQt* GetExtensionSystem(); #endif // BUILDFLAG(ENABLE_EXTENSIONS) private: + friend class ProfileIODataQt; ProfileIODataQt* m_io_data; DISALLOW_COPY_AND_ASSIGN(ResourceContextQt); }; diff --git a/src/core/type_conversion.h b/src/core/type_conversion.h index 96b4ecadc..7b1f1b4d6 100644 --- a/src/core/type_conversion.h +++ b/src/core/type_conversion.h @@ -172,6 +172,11 @@ inline gfx::SizeF toGfx(const QSizeF& size) return gfx::SizeF(size.width(), size.height()); } +inline gfx::Rect toGfx(const QRect &rect) +{ + return gfx::Rect(rect.x(), rect.y(), rect.width(), rect.height()); +} + inline QSizeF toQt(const gfx::SizeF &size) { return QSizeF(size.width(), size.height()); diff --git a/src/core/user_notification_controller.cpp b/src/core/user_notification_controller.cpp new file mode 100644 index 000000000..82cb57e51 --- /dev/null +++ b/src/core/user_notification_controller.cpp @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "user_notification_controller.h" + +#include "type_conversion.h" + +#include "base/callback.h" +#include "content/public/browser/notification_event_dispatcher.h" +#include "third_party/blink/public/common/notifications/notification_resources.h" +#include "third_party/blink/public/common/notifications/platform_notification_data.h" +#include "ui/message_center/public/cpp/notification_delegate.h" + +#include <memory> + +namespace QtWebEngineCore { + +static Qt::LayoutDirection toDirection(blink::PlatformNotificationData::Direction direction) +{ + switch (direction) { + case blink::PlatformNotificationData::DIRECTION_LEFT_TO_RIGHT: + return Qt::LeftToRight; + case blink::PlatformNotificationData::DIRECTION_RIGHT_TO_LEFT: + return Qt::RightToLeft; + case blink::PlatformNotificationData::DIRECTION_AUTO: + default: + break; + } + return Qt::LayoutDirectionAuto; +} + +class UserNotificationControllerPrivate { +public: + UserNotificationControllerPrivate(const blink::PlatformNotificationData ¶ms, + const blink::NotificationResources &resources, + const GURL &origin) + : m_params(params) + , m_origin(origin) + , m_delegate(nullptr) + , m_resources(resources) + , m_client(nullptr) + , m_iconGenerated(false) + , m_imageGenerated(false) + , m_badgeGenerated(false) + , m_shown(false) + { } + + blink::PlatformNotificationData m_params; + GURL m_origin; + std::unique_ptr<UserNotificationController::Delegate> m_delegate; + blink::NotificationResources m_resources; + UserNotificationController::Client *m_client; + QIcon m_icon; + QImage m_image; + QImage m_badge; + bool m_iconGenerated; + bool m_imageGenerated; + bool m_badgeGenerated; + bool m_shown; +}; + + +UserNotificationController::UserNotificationController(const blink::PlatformNotificationData ¶ms, + const blink::NotificationResources &resources, + const GURL &origin, + Delegate *delegate) + : d(new UserNotificationControllerPrivate(params, resources, origin)) +{ + d->m_delegate.reset(delegate); +} + +UserNotificationController::~UserNotificationController() +{ + delete d; + d = nullptr; +} + +void UserNotificationController::notificationDisplayed() +{ + if (!d->m_shown) { + d->m_shown = true; + if (d->m_delegate) + d->m_delegate->shown(); + } +} + +void UserNotificationController::notificationClosed() +{ + d->m_shown = false; + if (d->m_delegate) + d->m_delegate->closed(true); +} + +void UserNotificationController::notificationClicked() +{ + if (d->m_delegate) + d->m_delegate->clicked(); +} + +void UserNotificationController::closeNotification() +{ + d->m_shown = false; + if (d->m_client) + d->m_client->notificationClosed(this); +} + +void UserNotificationController::setClient(UserNotificationController::Client* client) +{ + d->m_client = client; +} + +UserNotificationController::Client* UserNotificationController::client() +{ + return d->m_client; +} + +QUrl UserNotificationController::origin() const +{ + return toQt(d->m_origin); +} + +QIcon UserNotificationController::icon() const +{ + if (!d->m_iconGenerated) { + d->m_iconGenerated = true; + if (!d->m_resources.notification_icon.isNull()) { + QImage image = toQImage(d->m_resources.notification_icon); + if (!image.isNull()) + d->m_icon = QIcon(QPixmap::fromImage(std::move(image), Qt::NoFormatConversion)); + } + } + return d->m_icon; +} + +QImage UserNotificationController::image() const +{ + if (d->m_imageGenerated) + return d->m_image; + d->m_image = toQImage(d->m_resources.image); + d->m_imageGenerated = true; + return d->m_image; +} + +QImage UserNotificationController::badge() const +{ + if (d->m_badgeGenerated) + return d->m_badge; + d->m_badge = toQImage(d->m_resources.badge); + d->m_badgeGenerated = true; + return d->m_badge; +} + +QString UserNotificationController::title() const +{ + return toQt(d->m_params.title); +} + +QString UserNotificationController::body() const +{ + return toQt(d->m_params.body); +} + +QString UserNotificationController::tag() const +{ + return toQt(d->m_params.tag); +} + +QString UserNotificationController::language() const +{ + return toQt(d->m_params.lang); +} + +Qt::LayoutDirection UserNotificationController::direction() const +{ + return toDirection(d->m_params.direction); +} + +bool UserNotificationController::isShown() const +{ + return d->m_shown; +} + +} // namespace QtWebEngineCore diff --git a/src/core/user_notification_controller.h b/src/core/user_notification_controller.h new file mode 100644 index 000000000..840074843 --- /dev/null +++ b/src/core/user_notification_controller.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 DESKTOP_NOTIFICATION_CONTROLLER_H +#define DESKTOP_NOTIFICATION_CONTROLLER_H + +#include "qtwebenginecoreglobal.h" + +#include <QtCore/QSharedPointer> +#include <QtCore/QUrl> +#include <QtGui/QIcon> + +class GURL; + +namespace blink { + struct NotificationResources; + struct PlatformNotificationData; +} + +namespace QtWebEngineCore { + +class UserNotificationControllerPrivate; + +// Works as an accessor and owner of chromium objects related to showing desktop notifications. +class QWEBENGINECORE_EXPORT UserNotificationController : public QEnableSharedFromThis<UserNotificationController> { +public: + struct Delegate { + virtual ~Delegate() { } + virtual void shown() = 0; + virtual void clicked() = 0; + virtual void closed(bool byUser) = 0; + }; + + UserNotificationController(const blink::PlatformNotificationData ¶ms, + const blink::NotificationResources &resources, + const GURL &origin, + Delegate *delegate); + ~UserNotificationController(); + + // The notification was shown. + void notificationDisplayed(); + + // The notification was closed. + void notificationClosed(); + + // The user clicked on the notification. + void notificationClicked(); + + // Chromium requests to close the notification. + void closeNotification(); + + QUrl origin() const; + QIcon icon() const; + QImage image() const; + QImage badge() const; + QString title() const; + QString body() const; + QString tag() const; + QString language() const; + Qt::LayoutDirection direction() const; + + bool isShown() const; + + class Client { + public: + virtual ~Client() { } + virtual void notificationClosed(const UserNotificationController *) = 0; + }; + void setClient(Client *client); + Client* client(); + +private: + UserNotificationControllerPrivate *d; +}; + +} // namespace QtWebEngineCore + +#endif // DESKTOP_NOTIFICATION_CONTROLLER_H diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp index 0c884d265..fe31a5b51 100644 --- a/src/core/web_contents_adapter.cpp +++ b/src/core/web_contents_adapter.cpp @@ -644,13 +644,12 @@ void WebContentsAdapter::load(const QWebEngineHttpRequest &request) "HTTP-POST data can only be sent over HTTP(S) protocol")); return; } + params.post_data = network::ResourceRequestBody::CreateFromBytes( + (const char*)request.postData().constData(), + request.postData().length()); break; } - params.post_data = network::ResourceRequestBody::CreateFromBytes( - (const char*)request.postData().constData(), - request.postData().length()); - // convert the custom headers into the format that chromium expects QVector<QByteArray> headers = request.headers(); for (QVector<QByteArray>::const_iterator it = headers.cbegin(); it != headers.cend(); ++it) { @@ -1271,6 +1270,12 @@ void WebContentsAdapter::runGeolocationRequestCallback(const QUrl &securityOrigi m_profileAdapter->permissionRequestReply(securityOrigin, ProfileAdapter::GeolocationPermission, allowed); } +void WebContentsAdapter::runUserNotificationRequestCallback(const QUrl &securityOrigin, bool allowed) +{ + CHECK_INITIALIZED(); + m_profileAdapter->permissionRequestReply(securityOrigin, ProfileAdapter::NotificationPermission, allowed); +} + void WebContentsAdapter::grantMouseLockPermission(bool granted) { CHECK_INITIALIZED(); diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h index b4a01fb17..c6e4f2256 100644 --- a/src/core/web_contents_adapter.h +++ b/src/core/web_contents_adapter.h @@ -183,6 +183,7 @@ public: void grantMediaAccessPermission(const QUrl &securityOrigin, WebContentsAdapterClient::MediaRequestFlags flags); void runGeolocationRequestCallback(const QUrl &securityOrigin, bool allowed); void grantMouseLockPermission(bool granted); + void runUserNotificationRequestCallback(const QUrl &securityOrigin, bool allowed); void setBackgroundColor(const QColor &color); QAccessibleInterface *browserAccessible(); diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h index f4937f0d4..d155ed391 100644 --- a/src/core/web_contents_adapter_client.h +++ b/src/core/web_contents_adapter_client.h @@ -459,6 +459,7 @@ public: virtual void runMouseLockPermissionRequest(const QUrl &securityOrigin) = 0; virtual void runQuotaRequest(QWebEngineQuotaRequest) = 0; virtual void runRegisterProtocolHandlerRequest(QWebEngineRegisterProtocolHandlerRequest) = 0; + virtual void runUserNotificationPermissionRequest(const QUrl &securityOrigin) = 0; virtual WebEngineSettings *webEngineSettings() const = 0; RenderProcessTerminationStatus renderProcessExitStatus(int); virtual void renderProcessTerminated(RenderProcessTerminationStatus terminationStatus, int exitCode) = 0; diff --git a/src/core/web_contents_delegate_qt.cpp b/src/core/web_contents_delegate_qt.cpp index 963b89a3f..4fcf2944d 100644 --- a/src/core/web_contents_delegate_qt.cpp +++ b/src/core/web_contents_delegate_qt.cpp @@ -585,6 +585,11 @@ void WebContentsDelegateQt::requestGeolocationPermission(const QUrl &requestingO m_viewClient->runGeolocationPermissionRequest(requestingOrigin); } +void WebContentsDelegateQt::requestUserNotificationPermission(const QUrl &requestingOrigin) +{ + m_viewClient->runUserNotificationPermissionRequest(requestingOrigin); +} + extern WebContentsAdapterClient::NavigationType pageTransitionToNavigationType(ui::PageTransition transition); void WebContentsDelegateQt::launchExternalURL(const QUrl &url, ui::PageTransition page_transition, bool is_main_frame, bool has_user_gesture) diff --git a/src/core/web_contents_delegate_qt.h b/src/core/web_contents_delegate_qt.h index 40f585767..77f6cc389 100644 --- a/src/core/web_contents_delegate_qt.h +++ b/src/core/web_contents_delegate_qt.h @@ -148,6 +148,7 @@ public: void allowCertificateError(const QSharedPointer<CertificateErrorController> &); void selectClientCert(const QSharedPointer<ClientCertSelectController> &); void requestGeolocationPermission(const QUrl &requestingOrigin); + void requestUserNotificationPermission(const QUrl &requestingOrigin); void launchExternalURL(const QUrl &url, ui::PageTransition page_transition, bool is_main_frame, bool has_user_gesture); FaviconManager *faviconManager(); diff --git a/src/core/web_engine_context.cpp b/src/core/web_engine_context.cpp index 9bf767437..67639dc4f 100644 --- a/src/core/web_engine_context.cpp +++ b/src/core/web_engine_context.cpp @@ -361,48 +361,39 @@ WebEngineContext::WebEngineContext() base::TaskScheduler::Create("Browser"); m_contentRunner.reset(content::ContentMainRunner::Create()); m_browserRunner.reset(content::BrowserMainRunner::Create()); + #ifdef Q_OS_LINUX // Call qputenv before BrowserMainRunnerImpl::Initialize is called. // http://crbug.com/245466 qputenv("force_s3tc_enable", "true"); #endif - QWebEngineUrlScheme qrcScheme(QByteArrayLiteral("qrc")); - qrcScheme.setFlags(QWebEngineUrlScheme::SecureScheme - | QWebEngineUrlScheme::LocalAccessAllowed - | QWebEngineUrlScheme::ViewSourceAllowed); - QWebEngineUrlScheme::registerScheme(qrcScheme); + if (QWebEngineUrlScheme::schemeByName(QByteArrayLiteral("qrc")) == QWebEngineUrlScheme()) { + // User might have registered "qrc" already with different options. + QWebEngineUrlScheme qrcScheme(QByteArrayLiteral("qrc")); + qrcScheme.setFlags(QWebEngineUrlScheme::SecureScheme + | QWebEngineUrlScheme::LocalAccessAllowed + | QWebEngineUrlScheme::ViewSourceAllowed); + QWebEngineUrlScheme::registerScheme(qrcScheme); + } + + QWebEngineUrlScheme::lockSchemes(); // Allow us to inject javascript like any webview toolkit. content::RenderFrameHost::AllowInjectingJavaScriptForAndroidWebView(); - base::CommandLine::CreateEmpty(); - base::CommandLine* parsedCommandLine = base::CommandLine::ForCurrentProcess(); QStringList appArgs = QCoreApplication::arguments(); - if (qEnvironmentVariableIsSet(kChromiumFlagsEnv)) { - appArgs = appArgs.mid(0, 1); // Take application name and drop the rest - appArgs.append(QString::fromLocal8Bit(qgetenv(kChromiumFlagsEnv)).split(' ')); - } - bool enableWebGLSoftwareRendering = - appArgs.removeAll(QStringLiteral("--enable-webgl-software-rendering")); + bool enableWebGLSoftwareRendering = appArgs.contains(QStringLiteral("--enable-webgl-software-rendering")); bool useEmbeddedSwitches = false; #if defined(QTWEBENGINE_EMBEDDED_SWITCHES) - useEmbeddedSwitches = !appArgs.removeAll(QStringLiteral("--disable-embedded-switches")); + useEmbeddedSwitches = !appArgs.contains(QStringLiteral("--disable-embedded-switches")); #else - useEmbeddedSwitches = appArgs.removeAll(QStringLiteral("--enable-embedded-switches")); + useEmbeddedSwitches = appArgs.contains(QStringLiteral("--enable-embedded-switches")); #endif - base::CommandLine::StringVector argv; - argv.resize(appArgs.size()); -#if defined(Q_OS_WIN) - for (int i = 0; i < appArgs.size(); ++i) - argv[i] = toString16(appArgs[i]); -#else - for (int i = 0; i < appArgs.size(); ++i) - argv[i] = appArgs[i].toStdString(); -#endif - parsedCommandLine->InitFromArgv(argv); + + base::CommandLine* parsedCommandLine = commandLine(); parsedCommandLine->AppendSwitchPath(switches::kBrowserSubprocessPath, WebEngineLibraryInfo::getPath(content::CHILD_PROCESS_EXE)); @@ -665,4 +656,34 @@ gpu::SyncPointManager *WebEngineContext::syncPointManager() return s_syncPointManager.load(); } +base::CommandLine* WebEngineContext::commandLine() { + if (base::CommandLine::CreateEmpty()) { + base::CommandLine* parsedCommandLine = base::CommandLine::ForCurrentProcess(); + QStringList appArgs = QCoreApplication::arguments(); + if (qEnvironmentVariableIsSet(kChromiumFlagsEnv)) { + appArgs = appArgs.mid(0, 1); // Take application name and drop the rest + appArgs.append(QString::fromLocal8Bit(qgetenv(kChromiumFlagsEnv)).split(' ')); + } +#ifdef Q_OS_WIN + appArgs.removeAll(QStringLiteral("--enable-webgl-software-rendering")); +#endif + appArgs.removeAll(QStringLiteral("--disable-embedded-switches")); + appArgs.removeAll(QStringLiteral("--enable-embedded-switches")); + + base::CommandLine::StringVector argv; + argv.resize(appArgs.size()); +#if defined(Q_OS_WIN) + for (int i = 0; i < appArgs.size(); ++i) + argv[i] = toString16(appArgs[i]); +#else + for (int i = 0; i < appArgs.size(); ++i) + argv[i] = appArgs[i].toStdString(); +#endif + parsedCommandLine->InitFromArgv(argv); + return parsedCommandLine; + } else { + return base::CommandLine::ForCurrentProcess(); + } +} + } // namespace diff --git a/src/core/web_engine_context.h b/src/core/web_engine_context.h index ad02ddf4d..4dc5251cc 100644 --- a/src/core/web_engine_context.h +++ b/src/core/web_engine_context.h @@ -47,6 +47,7 @@ namespace base { class RunLoop; +class CommandLine; } namespace content { @@ -95,6 +96,7 @@ public: void addProfileAdapter(ProfileAdapter *profileAdapter); void removeProfileAdapter(ProfileAdapter *profileAdapter); void destroy(); + static base::CommandLine* commandLine(); static gpu::SyncPointManager *syncPointManager(); diff --git a/src/webengine/api/qquickwebengineprofile.cpp b/src/webengine/api/qquickwebengineprofile.cpp index 4448d44d1..cf9dae90a 100644 --- a/src/webengine/api/qquickwebengineprofile.cpp +++ b/src/webengine/api/qquickwebengineprofile.cpp @@ -46,6 +46,7 @@ #include "qquickwebenginesettings_p.h" #include "qquickwebengineview_p_p.h" #include "qwebenginecookiestore.h" +#include "qwebenginenotification.h" #include <QQmlEngine> @@ -150,6 +151,13 @@ ASSERT_ENUMS_MATCH(QQuickWebEngineDownloadItem::MimeHtmlSaveFormat, QtWebEngineC The \a download argument holds the state of the finished download instance. */ +/*! + \fn QQuickWebEngineProfile::userNotification(QWebEngineNotification *notification) + + This signal is emitted whenever there is a newly created user notification. + The \a notification argument holds the notification instance to query data and interact with. +*/ + QQuickWebEngineProfilePrivate::QQuickWebEngineProfilePrivate(ProfileAdapter *profileAdapter) : m_settings(new QQuickWebEngineSettings()) , m_profileAdapter(profileAdapter) @@ -285,6 +293,14 @@ void QQuickWebEngineProfilePrivate::useForGlobalCertificateVerificationChanged() Q_EMIT q->useForGlobalCertificateVerificationChanged(); } +void QQuickWebEngineProfilePrivate::showNotification(QSharedPointer<QtWebEngineCore::UserNotificationController> &controller) +{ + Q_Q(QQuickWebEngineProfile); + auto notification = new QWebEngineNotification(controller); + QQmlEngine::setObjectOwnership(notification, QQmlEngine::JavaScriptOwnership); + Q_EMIT q->userNotification(notification); +} + void QQuickWebEngineProfilePrivate::userScripts_append(QQmlListProperty<QQuickWebEngineScript> *p, QQuickWebEngineScript *script) { Q_ASSERT(p && p->data); @@ -365,6 +381,13 @@ void QQuickWebEngineProfilePrivate::userScripts_clear(QQmlListProperty<QQuickWeb */ /*! + \qmlsignal WebEngineProfile::userNotification(WebEngineNotification notification) + + This signal is emitted whenever there is a newly created user notification. + The \a notification argument holds the notification instance to query data and interact with. +*/ + +/*! Constructs a new profile with the parent \a parent. */ QQuickWebEngineProfile::QQuickWebEngineProfile(QObject *parent) @@ -922,20 +945,45 @@ void QQuickWebEngineProfile::clearHttpCache() d->profileAdapter()->clearHttpCache(); } - +#if QT_DEPRECATED_SINCE(5, 13) /*! Registers a request interceptor singleton \a interceptor to intercept URL requests. The profile does not take ownership of the pointer. + \obsolete + + Interceptors installed with this method will call + QWebEngineUrlRequestInterceptor::interceptRequest on the I/O thread. Therefore + the user has to provide thread-safe interaction with the other user classes. + Use setUrlRequestInterceptor instead. + \sa QWebEngineUrlRequestInterceptor + */ void QQuickWebEngineProfile::setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) { Q_D(QQuickWebEngineProfile); + interceptor->setProperty("deprecated", true); + d->profileAdapter()->setRequestInterceptor(interceptor); + qWarning("Use of deprecated not tread-safe setter, use setUrlRequestInterceptor instead."); +} +#endif + +/*! + Registers a request interceptor singleton \a interceptor to intercept URL requests. + + The profile does not take ownership of the pointer. + + \sa QWebEngineUrlRequestInfo QWebEngineUrlRequestInterceptor +*/ +void QQuickWebEngineProfile::setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) +{ + Q_D(QQuickWebEngineProfile); d->profileAdapter()->setRequestInterceptor(interceptor); } + /*! Returns the custom URL scheme handler register for the URL scheme \a scheme. */ diff --git a/src/webengine/api/qquickwebengineprofile.h b/src/webengine/api/qquickwebengineprofile.h index 1e2e3e030..ce3285ec9 100644 --- a/src/webengine/api/qquickwebengineprofile.h +++ b/src/webengine/api/qquickwebengineprofile.h @@ -55,6 +55,7 @@ class QQuickWebEngineProfilePrivate; class QQuickWebEngineScript; class QQuickWebEngineSettings; class QWebEngineCookieStore; +class QWebEngineNotification; class QWebEngineUrlRequestInterceptor; class QWebEngineUrlSchemeHandler; @@ -126,7 +127,10 @@ public: QWebEngineCookieStore *cookieStore() const; +#if QT_DEPRECATED_SINCE(5, 13) void setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor); +#endif + void setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor); const QWebEngineUrlSchemeHandler *urlSchemeHandler(const QByteArray &) const; void installUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *); @@ -169,6 +173,8 @@ Q_SIGNALS: void downloadRequested(QQuickWebEngineDownloadItem *download); void downloadFinished(QQuickWebEngineDownloadItem *download); + void userNotification(QWebEngineNotification *notification); + private: Q_DECLARE_PRIVATE(QQuickWebEngineProfile) QQuickWebEngineProfile(QQuickWebEngineProfilePrivate *, QObject *parent = Q_NULLPTR); diff --git a/src/webengine/api/qquickwebengineprofile_p.h b/src/webengine/api/qquickwebengineprofile_p.h index d59470f46..322ec0101 100644 --- a/src/webengine/api/qquickwebengineprofile_p.h +++ b/src/webengine/api/qquickwebengineprofile_p.h @@ -85,6 +85,8 @@ public: void useForGlobalCertificateVerificationChanged() override; + void showNotification(QSharedPointer<QtWebEngineCore::UserNotificationController> &controller) override; + // QQmlListPropertyHelpers static void userScripts_append(QQmlListProperty<QQuickWebEngineScript> *p, QQuickWebEngineScript *script); static int userScripts_count(QQmlListProperty<QQuickWebEngineScript> *p); diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp index 5e6a45924..e7b2d8c9c 100644 --- a/src/webengine/api/qquickwebengineview.cpp +++ b/src/webengine/api/qquickwebengineview.cpp @@ -320,6 +320,12 @@ void QQuickWebEngineViewPrivate::runGeolocationPermissionRequest(const QUrl &url Q_EMIT q->featurePermissionRequested(url, QQuickWebEngineView::Geolocation); } +void QQuickWebEngineViewPrivate::runUserNotificationPermissionRequest(const QUrl &url) +{ + Q_Q(QQuickWebEngineView); + Q_EMIT q->featurePermissionRequested(url, QQuickWebEngineView::Notifications); +} + void QQuickWebEngineViewPrivate::showColorDialog(QSharedPointer<ColorChooserController> controller) { Q_Q(QQuickWebEngineView); @@ -1550,6 +1556,9 @@ void QQuickWebEngineView::grantFeaturePermission(const QUrl &securityOrigin, QQu WebContentsAdapterClient::MediaDesktopAudioCapture | WebContentsAdapterClient::MediaDesktopVideoCapture)); break; + case Notifications: + d_ptr->adapter->runUserNotificationRequestCallback(securityOrigin, granted); + break; default: Q_UNREACHABLE(); } diff --git a/src/webengine/api/qquickwebengineview_p.h b/src/webengine/api/qquickwebengineview_p.h index 3a40abf0a..c851dcb8d 100644 --- a/src/webengine/api/qquickwebengineview_p.h +++ b/src/webengine/api/qquickwebengineview_p.h @@ -209,7 +209,8 @@ public: MediaAudioVideoCapture, Geolocation, DesktopVideoCapture, - DesktopAudioVideoCapture + DesktopAudioVideoCapture, + Notifications, }; Q_ENUM(Feature) diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h index 4e8657651..543d7b119 100644 --- a/src/webengine/api/qquickwebengineview_p_p.h +++ b/src/webengine/api/qquickwebengineview_p_p.h @@ -145,6 +145,7 @@ public: void allowCertificateError(const QSharedPointer<CertificateErrorController> &errorController) override; void selectClientCert(const QSharedPointer<ClientCertSelectController> &selectController) override; void runGeolocationPermissionRequest(QUrl const&) override; + void runUserNotificationPermissionRequest(QUrl const&) override; void renderProcessTerminated(RenderProcessTerminationStatus terminationStatus, int exitCode) override; void requestGeometryChange(const QRect &geometry, const QRect &frameGeometry) override; void updateScrollPosition(const QPointF &position) override; diff --git a/src/webengine/doc/src/qtwebengine-features.qdoc b/src/webengine/doc/src/qtwebengine-features.qdoc index 1dce17e08..64e9badb1 100644 --- a/src/webengine/doc/src/qtwebengine-features.qdoc +++ b/src/webengine/doc/src/qtwebengine-features.qdoc @@ -50,6 +50,7 @@ \li \l{Touch} \li \l{View Source} \li \l{WebRTC} + \li \l{Notifications} \endlist \section1 Audio and Video Codecs @@ -511,4 +512,13 @@ This feature can be tested by setting up a webcam or microphone and then opening \c https://test.webrtc.org/ in \l{WebEngine Widgets Simple Browser Example}{Simple Browser} or \l{WebEngine Quick Nano Browser}{Nano Browser}. + + \section1 Notifications + + Qt WebEngine supports JavaScript Web Notification API. + The application has to explicitly allow the feature by using + QWebEnginePage::Notifications or \l{WebEngineView::Feature} + {WebEngineView.Notifications}. + + Support for this feature was added in Qt 5.13.0. */ diff --git a/src/webengine/doc/src/webengineview_lgpl.qdoc b/src/webengine/doc/src/webengineview_lgpl.qdoc index d96aabc97..a6f2af39e 100644 --- a/src/webengine/doc/src/webengineview_lgpl.qdoc +++ b/src/webengine/doc/src/webengineview_lgpl.qdoc @@ -858,6 +858,8 @@ (Added in Qt 5.10) \value DesktopAudioVideoCapture Both audio and video output capture. (Added in Qt 5.10) + \value WebEnginView.Notifications + Web notifications for the end-user. \sa featurePermissionRequested(), grantFeaturePermission() */ diff --git a/src/webengine/plugin/plugin.cpp b/src/webengine/plugin/plugin.cpp index 0e63989ee..82a76760d 100644 --- a/src/webengine/plugin/plugin.cpp +++ b/src/webengine/plugin/plugin.cpp @@ -55,6 +55,7 @@ #include <QtWebEngine/private/qquickwebenginetouchhandleprovider_p_p.h> #include <QtWebEngine/private/qquickwebengineview_p.h> #include <QtWebEngine/private/qquickwebengineaction_p.h> +#include <QtWebEngineCore/qwebenginenotification.h> #include <QtWebEngineCore/qwebenginequotarequest.h> #include <QtWebEngineCore/qwebengineregisterprotocolhandlerrequest.h> @@ -161,7 +162,7 @@ public: tr("Cannot create a separate instance of WebEngineClientCertificateSelection")); qmlRegisterUncreatableType<QQuickWebEngineClientCertificateOption>(uri, 1, 9, "WebEngineClientCertificateOption", tr("Cannot create a separate instance of WebEngineClientCertificateOption")); - + qmlRegisterUncreatableType<QWebEngineNotification>(uri, 1, 9, "WebEngineNotification", msgUncreatableType("WebEngineNotification")); } private: diff --git a/src/webengine/render_widget_host_view_qt_delegate_quick.cpp b/src/webengine/render_widget_host_view_qt_delegate_quick.cpp index 2f65db97a..e756ee157 100644 --- a/src/webengine/render_widget_host_view_qt_delegate_quick.cpp +++ b/src/webengine/render_widget_host_view_qt_delegate_quick.cpp @@ -63,7 +63,7 @@ RenderWidgetHostViewQtDelegateQuick::RenderWidgetHostViewQtDelegateQuick(RenderW setFocus(true); setActiveFocusOnTab(true); -#ifdef Q_OS_OSX +#if defined(Q_OS_MACOS) && !defined(QT_NO_OPENGL) // Check that the default QSurfaceFormat OpenGL profile is compatible with the global OpenGL // shared context profile, otherwise this could lead to a nasty crash. QOpenGLContext *globalSharedContext = QOpenGLContext::globalShareContext(); @@ -99,19 +99,9 @@ void RenderWidgetHostViewQtDelegateQuick::initAsPopup(const QRect &r) setVisible(true); } -QRectF RenderWidgetHostViewQtDelegateQuick::screenRect() const +QRectF RenderWidgetHostViewQtDelegateQuick::viewGeometry() const { - QPointF pos = mapToScene(QPointF(0,0)); - return QRectF(pos.x(), pos.y(), width(), height()); -} - -QRectF RenderWidgetHostViewQtDelegateQuick::contentsRect() const -{ - QPointF scenePoint = mapToScene(QPointF(0, 0)); - QPointF screenPos; - if (window()) - screenPos = window()->mapToGlobal(scenePoint.toPoint()); - return QRectF(screenPos.x(), screenPos.y(), width(), height()); + return QRectF(mapToGlobal(QPointF(0, 0)), size()); } void RenderWidgetHostViewQtDelegateQuick::setKeyboardFocus() @@ -322,16 +312,7 @@ void RenderWidgetHostViewQtDelegateQuick::inputMethodEvent(QInputMethodEvent *ev void RenderWidgetHostViewQtDelegateQuick::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { QQuickItem::geometryChanged(newGeometry, oldGeometry); - - if (window()) { - const QPointF globalPos = QQuickItem::mapToGlobal(position()); - if (globalPos != m_lastGlobalPos) { - m_lastGlobalPos = globalPos; - m_client->windowBoundsChanged(); - } - } - - m_client->notifyResize(); + m_client->visualPropertiesChanged(); } void RenderWidgetHostViewQtDelegateQuick::itemChange(ItemChange change, const ItemChangeData &value) @@ -347,8 +328,7 @@ void RenderWidgetHostViewQtDelegateQuick::itemChange(ItemChange change, const It if (!m_isPopup) m_windowConnections.append(connect(value.window, SIGNAL(closing(QQuickCloseEvent *)), SLOT(onHide()))); } - - m_client->windowChanged(); + m_client->visualPropertiesChanged(); } else if (change == QQuickItem::ItemVisibleHasChanged) { if (!m_isPopup && !value.boolValue) onHide(); @@ -362,9 +342,7 @@ QSGNode *RenderWidgetHostViewQtDelegateQuick::updatePaintNode(QSGNode *oldNode, void RenderWidgetHostViewQtDelegateQuick::onWindowPosChanged() { - if (window()) - m_lastGlobalPos = QQuickItem::mapToGlobal(position()); - m_client->windowBoundsChanged(); + m_client->visualPropertiesChanged(); } void RenderWidgetHostViewQtDelegateQuick::onHide() diff --git a/src/webengine/render_widget_host_view_qt_delegate_quick.h b/src/webengine/render_widget_host_view_qt_delegate_quick.h index d4d64804a..4edf37cff 100644 --- a/src/webengine/render_widget_host_view_qt_delegate_quick.h +++ b/src/webengine/render_widget_host_view_qt_delegate_quick.h @@ -59,8 +59,7 @@ public: ~RenderWidgetHostViewQtDelegateQuick(); void initAsPopup(const QRect&) override; - QRectF screenRect() const override; - QRectF contentsRect() const override; + QRectF viewGeometry() const override; void setKeyboardFocus() override; bool hasKeyboardFocus() override; void lockMouse() override; @@ -113,7 +112,6 @@ private: RenderWidgetHostViewQtDelegateClient *m_client; QList<QMetaObject::Connection> m_windowConnections; bool m_isPopup; - QPointF m_lastGlobalPos; QQuickWebEngineView *m_view = nullptr; }; diff --git a/src/webengine/render_widget_host_view_qt_delegate_quickwindow.cpp b/src/webengine/render_widget_host_view_qt_delegate_quickwindow.cpp index d3c88148e..3c6c743e0 100644 --- a/src/webengine/render_widget_host_view_qt_delegate_quickwindow.cpp +++ b/src/webengine/render_widget_host_view_qt_delegate_quickwindow.cpp @@ -62,14 +62,9 @@ void RenderWidgetHostViewQtDelegateQuickWindow::initAsPopup(const QRect &screenR show(); } -QRectF RenderWidgetHostViewQtDelegateQuickWindow::screenRect() const +QRectF RenderWidgetHostViewQtDelegateQuickWindow::viewGeometry() const { - return QRectF(x(), y(), width(), height()); -} - -QRectF RenderWidgetHostViewQtDelegateQuickWindow::contentsRect() const -{ - return geometry(); + return m_realDelegate->viewGeometry(); } void RenderWidgetHostViewQtDelegateQuickWindow::show() diff --git a/src/webengine/render_widget_host_view_qt_delegate_quickwindow.h b/src/webengine/render_widget_host_view_qt_delegate_quickwindow.h index 36e4ddd8a..039f6102a 100644 --- a/src/webengine/render_widget_host_view_qt_delegate_quickwindow.h +++ b/src/webengine/render_widget_host_view_qt_delegate_quickwindow.h @@ -56,8 +56,7 @@ public: ~RenderWidgetHostViewQtDelegateQuickWindow(); void initAsPopup(const QRect&) override; - QRectF screenRect() const override; - QRectF contentsRect() const override; + QRectF viewGeometry() const override; void setKeyboardFocus() override {} bool hasKeyboardFocus() override { return false; } void lockMouse() override {} diff --git a/src/webenginewidgets/api/qwebenginenotificationpresenter.cpp b/src/webenginewidgets/api/qwebenginenotificationpresenter.cpp new file mode 100644 index 000000000..c15a80373 --- /dev/null +++ b/src/webenginewidgets/api/qwebenginenotificationpresenter.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "qwebenginenotificationpresenter_p.h" + +#include <QSystemTrayIcon> + +QT_BEGIN_NAMESPACE + +DefaultNotificationPresenter::DefaultNotificationPresenter(QObject *parent) : QObject(parent) +{ +#ifndef QT_NO_SYSTEMTRAYICON + m_systemTrayIcon = new QSystemTrayIcon(this); + connect(m_systemTrayIcon, &QSystemTrayIcon::messageClicked, this, &DefaultNotificationPresenter::messageClicked); +#endif +} + +DefaultNotificationPresenter::~DefaultNotificationPresenter() +{ +} + +void DefaultNotificationPresenter::show(const QWebEngineNotification ¬ification) +{ + if (!m_activeNotification.isNull()) + m_activeNotification.close(); + m_activeNotification = notification; +#ifndef QT_NO_SYSTEMTRAYICON + if (m_systemTrayIcon) { + m_systemTrayIcon->show(); + QIcon icon = notification.icon(); + if (!icon.isNull()) + m_systemTrayIcon->showMessage(notification.title(), notification.message(), icon); + else + m_systemTrayIcon->showMessage(notification.title(), notification.message()); + notification.show(); + connect(&m_activeNotification, &QWebEngineNotification::closed, this, &DefaultNotificationPresenter::closeNotification); + } +#endif +} + +void DefaultNotificationPresenter::messageClicked() +{ + if (!m_activeNotification.isNull()) + m_activeNotification.click(); +} + +void DefaultNotificationPresenter::closeNotification() +{ +#ifndef QT_NO_SYSTEMTRAYICON + const QWebEngineNotification *canceled = static_cast<const QWebEngineNotification *>(QObject::sender()); + if (m_systemTrayIcon && canceled->matches(m_activeNotification)) + m_systemTrayIcon->hide(); +#endif +} + +void defaultNotificationPresenter(const QWebEngineNotification ¬ification) +{ + static DefaultNotificationPresenter *presenter = nullptr; + if (!presenter) + presenter = new DefaultNotificationPresenter(); + presenter->show(notification); +} + + +QT_END_NAMESPACE diff --git a/src/webenginewidgets/api/qwebenginenotificationpresenter_p.h b/src/webenginewidgets/api/qwebenginenotificationpresenter_p.h new file mode 100644 index 000000000..a66dbc1b2 --- /dev/null +++ b/src/webenginewidgets/api/qwebenginenotificationpresenter_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 QWEBENGINENOTIFICATIONPRESENTER_P_H +#define QWEBENGINENOTIFICATIONPRESENTER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtWebEngineCore/QWebEngineNotification> + +#include <QtCore/QObject> + +QT_BEGIN_NAMESPACE + +class QSystemTrayIcon; + +class DefaultNotificationPresenter : public QObject { + Q_OBJECT +public: + DefaultNotificationPresenter(QObject *parent = nullptr); + virtual ~DefaultNotificationPresenter(); + + void show(const QWebEngineNotification ¬ification); + +private Q_SLOTS: + void messageClicked(); + void closeNotification(); + +private: + QSystemTrayIcon *m_systemTrayIcon; + QWebEngineNotification m_activeNotification; +}; + +void defaultNotificationPresenter(const QWebEngineNotification ¬ification); + +QT_END_NAMESPACE + +#endif // QWEBENGINENOTIFICATIONPRESENTER_P_H diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp index 3bc1a0b80..a1ddf88d7 100644 --- a/src/webenginewidgets/api/qwebenginepage.cpp +++ b/src/webenginewidgets/api/qwebenginepage.cpp @@ -54,6 +54,7 @@ #include "qwebenginefullscreenrequest.h" #include "qwebenginehistory.h" #include "qwebenginehistory_p.h" +#include "qwebenginenotification.h" #include "qwebengineprofile.h" #include "qwebengineprofile_p.h" #include "qwebenginequotarequest.h" @@ -62,6 +63,7 @@ #include "qwebenginesettings.h" #include "qwebengineview.h" #include "qwebengineview_p.h" +#include "user_notification_controller.h" #include "render_widget_host_view_qt_delegate_widget.h" #include "web_contents_adapter.h" #include "web_engine_settings.h" @@ -538,6 +540,12 @@ void QWebEnginePagePrivate::runRegisterProtocolHandlerRequest(QWebEngineRegister Q_EMIT q->registerProtocolHandlerRequested(request); } +void QWebEnginePagePrivate::runUserNotificationPermissionRequest(const QUrl &securityOrigin) +{ + Q_Q(QWebEnginePage); + Q_EMIT q->featurePermissionRequested(securityOrigin, QWebEnginePage::Notifications); +} + QObject *QWebEnginePagePrivate::accessibilityParentObject() { return view; @@ -1797,7 +1805,7 @@ void QWebEnginePagePrivate::printRequested() \sa QWebEngineUrlRequestInfo, QWebEngineProfile::setRequestInterceptor() */ -void QWebEnginePage::setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) +void QWebEnginePage::setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) { Q_D(QWebEnginePage); bool hadInterceptorChanged = bool(d->requestInterceptor) != bool(interceptor); @@ -1874,6 +1882,7 @@ void QWebEnginePage::setFeaturePermission(const QUrl &securityOrigin, QWebEngine d->adapter->grantMouseLockPermission(true); break; case Notifications: + d->adapter->runUserNotificationRequestCallback(securityOrigin, true); break; } } else { // if (policy == PermissionDeniedByUser) @@ -1892,6 +1901,7 @@ void QWebEnginePage::setFeaturePermission(const QUrl &securityOrigin, QWebEngine d->adapter->grantMouseLockPermission(false); break; case Notifications: + d->adapter->runUserNotificationRequestCallback(securityOrigin, false); break; } } @@ -2372,6 +2382,10 @@ void QWebEnginePage::printToPdf(const QString &filePath, const QPageLayout &page The \a resultCallback must take a const reference to a QByteArray as parameter. If printing was successful, this byte array will contain the PDF data, otherwise, the byte array will be empty. + \warning We guarantee that the callback (\a resultCallback) is always called, but it might be done + during page destruction. When QWebEnginePage is deleted, the callback is triggered with an invalid + value and it is not safe to use the corresponding QWebEnginePage or QWebEngineView instance inside it. + \since 5.7 */ void QWebEnginePage::printToPdf(const QWebEngineCallback<const QByteArray&> &resultCallback, const QPageLayout &pageLayout) @@ -2404,6 +2418,11 @@ void QWebEnginePage::printToPdf(const QWebEngineCallback<const QByteArray&> &res The \a resultCallback must take a boolean as parameter. If printing was successful, this boolean will have the value \c true, otherwise, its value will be \c false. + + \warning We guarantee that the callback (\a resultCallback) is always called, but it might be done + during page destruction. When QWebEnginePage is deleted, the callback is triggered with an invalid + value and it is not safe to use the corresponding QWebEnginePage or QWebEngineView instance inside it. + \since 5.8 */ void QWebEnginePage::print(QPrinter *printer, const QWebEngineCallback<bool> &resultCallback) diff --git a/src/webenginewidgets/api/qwebenginepage.h b/src/webenginewidgets/api/qwebenginepage.h index 4fd195074..4956877a9 100644 --- a/src/webenginewidgets/api/qwebenginepage.h +++ b/src/webenginewidgets/api/qwebenginepage.h @@ -77,8 +77,6 @@ class QWEBENGINEWIDGETS_EXPORT QWebEnginePage : public QObject { Q_OBJECT Q_PROPERTY(QString selectedText READ selectedText) Q_PROPERTY(bool hasSelection READ hasSelection) - - // Ex-QWebFrame properties Q_PROPERTY(QUrl requestedUrl READ requestedUrl) Q_PROPERTY(qreal zoomFactor READ zoomFactor WRITE setZoomFactor) Q_PROPERTY(QString title READ title) @@ -187,9 +185,7 @@ public: Q_ENUM(NavigationType) enum Feature { -#ifndef Q_QDOC Notifications = 0, -#endif Geolocation = 1, MediaAudioCapture = 2, MediaVideoCapture, @@ -306,7 +302,7 @@ public: void setDevToolsPage(QWebEnginePage *page); QWebEnginePage *devToolsPage() const; - void setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor); + void setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor); const QWebEngineContextMenuData &contextMenuData() const; diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h index bf2f43f80..d597383e2 100644 --- a/src/webenginewidgets/api/qwebenginepage_p.h +++ b/src/webenginewidgets/api/qwebenginepage_p.h @@ -132,6 +132,7 @@ public: void authenticationRequired(QSharedPointer<QtWebEngineCore::AuthenticationDialogController>) override; void runMediaAccessPermissionRequest(const QUrl &securityOrigin, MediaRequestFlags requestFlags) override; void runGeolocationPermissionRequest(const QUrl &securityOrigin) override; + void runUserNotificationPermissionRequest(const QUrl &securityOrigin) override; void runMouseLockPermissionRequest(const QUrl &securityOrigin) override; void runQuotaRequest(QWebEngineQuotaRequest) override; void runRegisterProtocolHandlerRequest(QWebEngineRegisterProtocolHandlerRequest) override; diff --git a/src/webenginewidgets/api/qwebengineprofile.cpp b/src/webenginewidgets/api/qwebengineprofile.cpp index 7e80f9720..74dc14da0 100644 --- a/src/webenginewidgets/api/qwebengineprofile.cpp +++ b/src/webenginewidgets/api/qwebengineprofile.cpp @@ -43,6 +43,7 @@ #include "qwebenginecookiestore.h" #include "qwebenginedownloaditem.h" #include "qwebenginedownloaditem_p.h" +#include "qwebenginenotificationpresenter_p.h" #include "qwebenginepage.h" #include "qwebenginepage_p.h" #include "qwebenginesettings.h" @@ -140,6 +141,12 @@ using QtWebEngineCore::ProfileAdapter; Both session and persistent cookies are saved to and restored from disk. */ +void QWebEngineProfilePrivate::showNotification(QSharedPointer<QtWebEngineCore::UserNotificationController> ¬ification) +{ + if (m_notificationPresenter) + m_notificationPresenter(QWebEngineNotification(notification)); +} + /*! \fn QWebEngineProfile::downloadRequested(QWebEngineDownloadItem *download) @@ -550,19 +557,43 @@ QWebEngineCookieStore* QWebEngineProfile::cookieStore() return d->profileAdapter()->cookieStore(); } - +#if QT_DEPRECATED_SINCE(5, 13) /*! Registers a request interceptor singleton \a interceptor to intercept URL requests. The profile does not take ownership of the pointer. + \obsolete + + Interceptors installed with this method will call + QWebEngineUrlRequestInterceptor::interceptRequest on the I/O thread. Therefore + the user has to provide thread-safe interaction with the other user classes. + Use setUrlRequestInterceptor instead. + \since 5.6 \sa QWebEngineUrlRequestInfo -*/ +*/ void QWebEngineProfile::setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) { Q_D(QWebEngineProfile); + interceptor->setProperty("deprecated", true); + d->profileAdapter()->setRequestInterceptor(interceptor); + qWarning("Use of deprecated not tread-safe setter, use setUrlRequestInterceptor instead."); +} +#endif +/*! + Registers a request interceptor singleton \a interceptor to intercept URL requests. + + The profile does not take ownership of the pointer. + + \since 5.13 + \sa QWebEngineUrlRequestInfo QWebEngineUrlRequestInterceptor +*/ + +void QWebEngineProfile::setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) +{ + Q_D(QWebEngineProfile); d->profileAdapter()->setRequestInterceptor(interceptor); } @@ -610,6 +641,27 @@ QWebEngineScriptCollection *QWebEngineProfile::scripts() const } /*! + Sets the function \a notificationPresenter as responsible for presenting sent notifications. + + \since 5.13 + \sa QWebEngineNotification +*/ +void QWebEngineProfile::setNotificationPresenter(const std::function<void(const QWebEngineNotification &)> ¬ificationPresenter) +{ + Q_D(QWebEngineProfile); + d->m_notificationPresenter = notificationPresenter; +} + +/*! + \overload +*/ +void QWebEngineProfile::setNotificationPresenter(std::function<void(const QWebEngineNotification &)> &¬ificationPresenter) +{ + Q_D(QWebEngineProfile); + d->m_notificationPresenter = std::move(notificationPresenter); +} + +/*! Returns the default profile. The default profile uses the storage name "Default". @@ -621,6 +673,8 @@ QWebEngineProfile *QWebEngineProfile::defaultProfile() static QWebEngineProfile* profile = new QWebEngineProfile( new QWebEngineProfilePrivate(ProfileAdapter::createDefaultProfileAdapter()), ProfileAdapter::globalQObjectRoot()); + if (!profile->d_ptr->m_notificationPresenter) + profile->setNotificationPresenter(&defaultNotificationPresenter); return profile; } diff --git a/src/webenginewidgets/api/qwebengineprofile.h b/src/webenginewidgets/api/qwebengineprofile.h index 9fc509851..5ad999c00 100644 --- a/src/webenginewidgets/api/qwebengineprofile.h +++ b/src/webenginewidgets/api/qwebengineprofile.h @@ -46,12 +46,15 @@ #include <QtCore/qscopedpointer.h> #include <QtCore/qstring.h> +#include <functional> + QT_BEGIN_NAMESPACE class QObject; class QUrl; class QWebEngineCookieStore; class QWebEngineDownloadItem; +class QWebEngineNotification; class QWebEnginePage; class QWebEnginePagePrivate; class QWebEngineProfilePrivate; @@ -106,7 +109,10 @@ public: void setHttpCacheMaximumSize(int maxSize); QWebEngineCookieStore* cookieStore(); +#if QT_DEPRECATED_SINCE(5, 13) void setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor); +#endif + void setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor); void clearAllVisitedLinks(); void clearVisitedLinks(const QList<QUrl> &urls); @@ -134,6 +140,9 @@ public: QString downloadPath() const; void setDownloadPath(const QString &path); + void setNotificationPresenter(const std::function<void(const QWebEngineNotification &)> ¬ificationPresenter); + void setNotificationPresenter(std::function<void(const QWebEngineNotification &)> &¬ificationPresenter); + static QWebEngineProfile *defaultProfile(); Q_SIGNALS: diff --git a/src/webenginewidgets/api/qwebengineprofile_p.h b/src/webenginewidgets/api/qwebengineprofile_p.h index 9ff8df849..3dd024ffd 100644 --- a/src/webenginewidgets/api/qwebengineprofile_p.h +++ b/src/webenginewidgets/api/qwebengineprofile_p.h @@ -60,6 +60,8 @@ #include <QScopedPointer> #include <QSharedPointer> +#include <functional> + namespace QtWebEngineCore { class ProfileAdapter; } @@ -68,6 +70,7 @@ QT_BEGIN_NAMESPACE class QWebEngineBrowserContext; class QWebEngineProfilePrivate; +class QWebEngineNotification; class QWebEngineSettings; class QWebEngineProfilePrivate : public QtWebEngineCore::ProfileAdapterClient { @@ -84,12 +87,15 @@ public: void downloadRequested(DownloadItemInfo &info) override; void downloadUpdated(const DownloadItemInfo &info) override; + void showNotification(QSharedPointer<QtWebEngineCore::UserNotificationController> &) override; + private: QWebEngineProfile *q_ptr; QWebEngineSettings *m_settings; QPointer<QtWebEngineCore::ProfileAdapter> m_profileAdapter; QScopedPointer<QWebEngineScriptCollection> m_scriptCollection; QMap<quint32, QPointer<QWebEngineDownloadItem> > m_ongoingDownloads; + std::function<void(const QWebEngineNotification &)> m_notificationPresenter; }; QT_END_NAMESPACE diff --git a/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc b/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc index d012c678c..877bf7175 100644 --- a/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc +++ b/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc @@ -285,6 +285,8 @@ This enum describes the platform feature access categories that the user may be asked to grant or deny access to: + \value Notifications + Web notifications for the end-user. \value Geolocation Location hardware or service. \value MediaAudioCapture @@ -485,6 +487,10 @@ The \a resultCallback must take a boolean parameter. It will be called with a value of \c true if the \a subString was found; otherwise the callback value will be \c false. + \warning We guarantee that the callback (\a resultCallback) is always called, but it might be done + during page destruction. When QWebEnginePage is deleted, the callback is triggered with an invalid + value and it is not safe to use the corresponding QWebEnginePage or QWebEngineView instance inside it. + For example: \snippet qtwebengine_qwebenginepage_snippet.cpp 0 */ @@ -613,6 +619,10 @@ \note \a resultCallback can be any of a function pointer, a functor or a lambda, and it is expected to take a QString parameter. + \warning We guarantee that the callback (\a resultCallback) is always called, but it might be done + during page destruction. When QWebEnginePage is deleted, the callback is triggered with an invalid + value and it is not safe to use the corresponding QWebEnginePage or QWebEngineView instance inside it. + \sa setHtml(), toPlainText() */ @@ -624,6 +634,10 @@ \note \a resultCallback can be any of a function pointer, a functor or a lambda, and it is expected to take a QString parameter. + \warning We guarantee that the callback (\a resultCallback) is always called, but it might be done + during page destruction. When QWebEnginePage is deleted, the callback is triggered with an invalid + value and it is not safe to use the corresponding QWebEnginePage or QWebEngineView instance inside it. + \sa toHtml() */ @@ -743,6 +757,10 @@ \warning Do not execute lengthy routines in the callback function, because it might block the rendering of the web engine page. + \warning We guarantee that the callback (\a resultCallback) is always called, but it might be done + during page destruction. When QWebEnginePage is deleted, the callback is triggered with an invalid + value and it is not safe to use the corresponding QWebEnginePage or QWebEngineView instance inside it. + See scripts() for an alternative API to inject scripts. \sa QWebEngineScript::ScriptWorldId diff --git a/src/webenginewidgets/doc/src/qwebengineview_lgpl.qdoc b/src/webenginewidgets/doc/src/qwebengineview_lgpl.qdoc index 09346a446..501959ab9 100644 --- a/src/webenginewidgets/doc/src/qwebengineview_lgpl.qdoc +++ b/src/webenginewidgets/doc/src/qwebengineview_lgpl.qdoc @@ -252,6 +252,10 @@ \a resultCallback must take a boolean parameter. It will be called with a value of \c true if \a subString was found; otherwise the callback value will be \c false. + \warning We guarantee that the callback (\a resultCallback) is always called, but it might be done + during page destruction. When QWebEnginePage is deleted, the callback is triggered with an invalid + value and it is not safe to use the corresponding QWebEnginePage or QWebEngineView instance inside it. + \sa selectedText(), selectionChanged() */ diff --git a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp index 5b464a461..951360c05 100644 --- a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp +++ b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp @@ -233,15 +233,9 @@ void RenderWidgetHostViewQtDelegateWidget::closeEvent(QCloseEvent *event) m_client->closePopup(); } -QRectF RenderWidgetHostViewQtDelegateWidget::screenRect() const +QRectF RenderWidgetHostViewQtDelegateWidget::viewGeometry() const { - return QRectF(x(), y(), width(), height()); -} - -QRectF RenderWidgetHostViewQtDelegateWidget::contentsRect() const -{ - QPointF pos = mapToGlobal(QPoint(0, 0)); - return QRectF(pos.x(), pos.y(), width(), height()); + return QRectF(mapToGlobal(pos()), size()); } void RenderWidgetHostViewQtDelegateWidget::setKeyboardFocus() @@ -283,6 +277,7 @@ void RenderWidgetHostViewQtDelegateWidget::show() void RenderWidgetHostViewQtDelegateWidget::hide() { m_rootItem->setVisible(false); + QQuickWidget::hide(); } bool RenderWidgetHostViewQtDelegateWidget::isVisible() const @@ -378,14 +373,7 @@ QVariant RenderWidgetHostViewQtDelegateWidget::inputMethodQuery(Qt::InputMethodQ void RenderWidgetHostViewQtDelegateWidget::resizeEvent(QResizeEvent *resizeEvent) { QQuickWidget::resizeEvent(resizeEvent); - - const QPoint globalPos = mapToGlobal(pos()); - if (globalPos != m_lastGlobalPos) { - m_lastGlobalPos = globalPos; - m_client->windowBoundsChanged(); - } - - m_client->notifyResize(); + m_client->visualPropertiesChanged(); } void RenderWidgetHostViewQtDelegateWidget::showEvent(QShowEvent *event) @@ -401,7 +389,7 @@ void RenderWidgetHostViewQtDelegateWidget::showEvent(QShowEvent *event) m_windowConnections.append(connect(w, SIGNAL(xChanged(int)), SLOT(onWindowPosChanged()))); m_windowConnections.append(connect(w, SIGNAL(yChanged(int)), SLOT(onWindowPosChanged()))); } - m_client->windowChanged(); + m_client->visualPropertiesChanged(); m_client->notifyShown(); } @@ -497,8 +485,7 @@ bool RenderWidgetHostViewQtDelegateWidget::event(QEvent *event) void RenderWidgetHostViewQtDelegateWidget::onWindowPosChanged() { - m_lastGlobalPos = mapToGlobal(pos()); - m_client->windowBoundsChanged(); + m_client->visualPropertiesChanged(); } } // namespace QtWebEngineCore diff --git a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h index c1cd90093..a736aa5cf 100644 --- a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h +++ b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h @@ -66,8 +66,7 @@ public: ~RenderWidgetHostViewQtDelegateWidget(); void initAsPopup(const QRect&) override; - QRectF screenRect() const override; - QRectF contentsRect() const override; + QRectF viewGeometry() const override; void setKeyboardFocus() override; bool hasKeyboardFocus() override; void lockMouse() override; @@ -111,7 +110,6 @@ private: QScopedPointer<QQuickItem> m_rootItem; bool m_isPopup; QColor m_clearColor; - QPoint m_lastGlobalPos; QList<QMetaObject::Connection> m_windowConnections; QWebEnginePage *m_page = nullptr; QMetaObject::Connection m_parentDestroyedConnection; diff --git a/src/webenginewidgets/webenginewidgets.pro b/src/webenginewidgets/webenginewidgets.pro index 4669c2bce..d4fb40dc7 100644 --- a/src/webenginewidgets/webenginewidgets.pro +++ b/src/webenginewidgets/webenginewidgets.pro @@ -19,6 +19,7 @@ SOURCES = \ api/qwebenginedownloaditem.cpp \ api/qwebenginefullscreenrequest.cpp \ api/qwebenginehistory.cpp \ + api/qwebenginenotificationpresenter.cpp \ api/qwebenginepage.cpp \ api/qwebengineprofile.cpp \ api/qwebenginescript.cpp \ @@ -36,6 +37,7 @@ HEADERS = \ api/qwebenginedownloaditem_p.h \ api/qwebenginefullscreenrequest.h \ api/qwebenginehistory.h \ + api/qwebenginenotificationpresenter_p.h \ api/qwebenginepage.h \ api/qwebenginepage_p.h \ api/qwebengineprofile.h \ diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp index 7b7fec6f4..5629998fd 100644 --- a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp +++ b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp @@ -107,6 +107,7 @@ public: void interceptRequest(QWebEngineUrlRequestInfo &info) override { + QVERIFY(QThread::currentThread() == QCoreApplication::instance()->thread()); // Since 63 we also intercept some unrelated blob requests.. if (info.requestUrl().scheme() == QLatin1String("blob")) return; @@ -167,7 +168,7 @@ void tst_QWebEngineUrlRequestInterceptor::interceptRequest() QWebEngineProfile profile; profile.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); TestRequestInterceptor interceptor(/* intercept */ true); - profile.setRequestInterceptor(&interceptor); + profile.setUrlRequestInterceptor(&interceptor); QWebEnginePage page(&profile); QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); @@ -197,7 +198,7 @@ void tst_QWebEngineUrlRequestInterceptor::interceptRequest() // Make sure that registering an observer does not modify the request. TestRequestInterceptor observer(/* intercept */ false); - profile.setRequestInterceptor(&observer); + profile.setUrlRequestInterceptor(&observer); page.load(QUrl("qrc:///resources/__placeholder__")); QTRY_COMPARE(loadSpy.count(), 1); success = loadSpy.takeFirst().takeFirst(); @@ -230,7 +231,7 @@ void tst_QWebEngineUrlRequestInterceptor::ipv6HostEncoding() { QWebEngineProfile profile; LocalhostContentProvider contentProvider; - profile.setRequestInterceptor(&contentProvider); + profile.setUrlRequestInterceptor(&contentProvider); QWebEnginePage page(&profile); QSignalSpy spyLoadFinished(&page, SIGNAL(loadFinished(bool))); @@ -264,11 +265,11 @@ void tst_QWebEngineUrlRequestInterceptor::requestedUrl() profile.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); TestRequestInterceptor interceptor(/* intercept */ true); if (!interceptInPage) - profile.setRequestInterceptor(&interceptor); + profile.setUrlRequestInterceptor(&interceptor); QWebEnginePage page(&profile); if (interceptInPage) - page.setRequestInterceptor(&interceptor); + page.setUrlRequestInterceptor(&interceptor); QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); page.setUrl(QUrl("qrc:///resources/__placeholder__")); @@ -303,11 +304,11 @@ void tst_QWebEngineUrlRequestInterceptor::setUrlSameUrl() QWebEngineProfile profile; TestRequestInterceptor interceptor(/* intercept */ true); if (!interceptInPage) - profile.setRequestInterceptor(&interceptor); + profile.setUrlRequestInterceptor(&interceptor); QWebEnginePage page(&profile); if (interceptInPage) - page.setRequestInterceptor(&interceptor); + page.setUrlRequestInterceptor(&interceptor); QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); page.setUrl(QUrl("qrc:///resources/__placeholder__")); @@ -336,7 +337,7 @@ void tst_QWebEngineUrlRequestInterceptor::firstPartyUrl() { QWebEngineProfile profile; TestRequestInterceptor interceptor(/* intercept */ false); - profile.setRequestInterceptor(&interceptor); + profile.setUrlRequestInterceptor(&interceptor); QWebEnginePage page(&profile); QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); @@ -370,7 +371,7 @@ void tst_QWebEngineUrlRequestInterceptor::firstPartyUrlNestedIframes() QWebEngineProfile profile; TestRequestInterceptor interceptor(/* intercept */ false); - profile.setRequestInterceptor(&interceptor); + profile.setUrlRequestInterceptor(&interceptor); QWebEnginePage page(&profile); QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); @@ -431,7 +432,7 @@ void tst_QWebEngineUrlRequestInterceptor::requestInterceptorByResourceType() QWebEngineProfile profile; TestRequestInterceptor interceptor(/* intercept */ false); - profile.setRequestInterceptor(&interceptor); + profile.setUrlRequestInterceptor(&interceptor); QWebEnginePage page(&profile); QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); @@ -449,7 +450,7 @@ void tst_QWebEngineUrlRequestInterceptor::firstPartyUrlHttp() { QWebEngineProfile profile; TestRequestInterceptor interceptor(/* intercept */ false); - profile.setRequestInterceptor(&interceptor); + profile.setUrlRequestInterceptor(&interceptor); QWebEnginePage page(&profile); QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); diff --git a/tests/auto/quick/publicapi/tst_publicapi.cpp b/tests/auto/quick/publicapi/tst_publicapi.cpp index 4cbadfdaa..b50a7d782 100644 --- a/tests/auto/quick/publicapi/tst_publicapi.cpp +++ b/tests/auto/quick/publicapi/tst_publicapi.cpp @@ -35,6 +35,7 @@ #include <QtTest/QtTest> #include <QtWebEngine/QQuickWebEngineProfile> #include <QtWebEngine/QQuickWebEngineScript> +#include <QtWebEngineCore/QWebEngineNotification> #include <QtWebEngineCore/QWebEngineQuotaRequest> #include <QtWebEngineCore/QWebEngineRegisterProtocolHandlerRequest> #include <private/qquickwebengineview_p.h> @@ -82,6 +83,7 @@ static const QList<const QMetaObject *> typesToCheck = QList<const QMetaObject * << &QQuickWebEngineContextMenuRequest::staticMetaObject << &QWebEngineQuotaRequest::staticMetaObject << &QWebEngineRegisterProtocolHandlerRequest::staticMetaObject + << &QWebEngineNotification::staticMetaObject ; static QList<const char *> knownEnumNames = QList<const char *>(); @@ -286,6 +288,7 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineProfile.clearHttpCache() --> void" << "QQuickWebEngineProfile.downloadFinished(QQuickWebEngineDownloadItem*) --> void" << "QQuickWebEngineProfile.downloadRequested(QQuickWebEngineDownloadItem*) --> void" + << "QQuickWebEngineProfile.userNotification(QWebEngineNotification*) --> void" << "QQuickWebEngineProfile.httpAcceptLanguage --> QString" << "QQuickWebEngineProfile.httpAcceptLanguageChanged() --> void" << "QQuickWebEngineProfile.httpCacheMaximumSize --> int" @@ -564,6 +567,7 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.NewViewInTab --> NewViewDestination" << "QQuickWebEngineView.NewViewInWindow --> NewViewDestination" << "QQuickWebEngineView.NoErrorDomain --> ErrorDomain" + << "QQuickWebEngineView.Notifications --> Feature" << "QQuickWebEngineView.NoWebAction --> WebAction" << "QQuickWebEngineView.NormalTerminationStatus --> RenderProcessTerminationStatus" << "QQuickWebEngineView.Note --> PrintedPageSizeId" @@ -703,6 +707,20 @@ static const QStringList expectedAPI = QStringList() << "QWebEngineRegisterProtocolHandlerRequest.origin --> QUrl" << "QWebEngineRegisterProtocolHandlerRequest.reject() --> void" << "QWebEngineRegisterProtocolHandlerRequest.scheme --> QString" + << "QWebEngineNotification.origin --> QUrl" + << "QWebEngineNotification.icon --> QIcon" + << "QWebEngineNotification.title --> QString" + << "QWebEngineNotification.message --> QString" + << "QWebEngineNotification.tag --> QString" + << "QWebEngineNotification.language --> QString" + << "QWebEngineNotification.direction --> Direction" + << "QWebEngineNotification.LeftToRight --> Direction" + << "QWebEngineNotification.RightToLeft --> Direction" + << "QWebEngineNotification.DirectionAuto --> Direction" + << "QWebEngineNotification.show() --> void" + << "QWebEngineNotification.click() --> void" + << "QWebEngineNotification.close() --> void" + << "QWebEngineNotification.closed() --> void" ; static bool isCheckedEnum(const QByteArray &typeName) diff --git a/tests/auto/quick/qmltests/data/tst_notification.qml b/tests/auto/quick/qmltests/data/tst_notification.qml new file mode 100644 index 000000000..609a04f61 --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_notification.qml @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.0 +import QtWebEngine 1.9 + +TestWebEngineView { + id: view + width: 320 + height: 320 + + property bool permissionRequested: false + property bool grantPermission: false + + signal consoleMessage(string message) + + SignalSpy { + id: spyRequest + target: view + signalName: 'featurePermissionRequested' + } + + onFeaturePermissionRequested: { + if (feature === WebEngineView.Notifications) { + permissionRequested = true + view.grantFeaturePermission(securityOrigin, feature, grantPermission) + } + } + + TestCase { + name: 'WebEngineNotification' + when: windowShown + + function resolverUrl(html) { + return Qt.resolvedUrl('../../../shared/data/' + html) + } + + function init() { + permissionRequested = false + spyRequest.clear() + } + + function test_request_data() { + return [ + { tag: 'grant', grant: true, permission: 'granted' }, + { tag: 'deny', grant: false, permission: 'denied' }, + ] + } + + function test_request(data) { + grantPermission = data.grant + + view.url = resolverUrl('notification.html') + verify(view.waitForLoadSucceeded()) + + view.runJavaScript('resetPermission()') + let result = {} + + view.runJavaScript('getPermission()', function (permission) { result.permission = permission }) + tryCompare(result, 'permission', 'default') + + view.runJavaScript('requestPermission()') + spyRequest.wait() + verify(permissionRequested) + compare(spyRequest.count, 1) + + view.runJavaScript('getPermission()', function (permission) { result.permission = permission }) + tryCompare(result, 'permission', data.permission) + } + + function test_notification() { + grantPermission = true + + view.url = resolverUrl('notification.html') + view.waitForLoadSucceeded() + + view.runJavaScript('requestPermission()') + spyRequest.wait() + verify(permissionRequested) + + let title = 'Title', message = 'Message', notification = null + view.profile.userNotification.connect(function (n) { notification = n }) + + view.runJavaScript('sendNotification("' + title + '", "' + message + '")') + tryVerify(function () { return notification !== null }) + compare(notification.title, title) + compare(notification.message, message) + } + } +} diff --git a/tests/auto/quick/qmltests/qmltests.pro b/tests/auto/quick/qmltests/qmltests.pro index ad479cd31..00e884e11 100644 --- a/tests/auto/quick/qmltests/qmltests.pro +++ b/tests/auto/quick/qmltests/qmltests.pro @@ -67,6 +67,7 @@ OTHER_FILES += \ $$PWD/data/tst_navigationHistory.qml \ $$PWD/data/tst_navigationRequested.qml \ $$PWD/data/tst_newViewRequest.qml \ + $$PWD/data/tst_notification.qml \ $$PWD/data/tst_profile.qml \ $$PWD/data/tst_properties.qml \ $$PWD/data/tst_runJavaScript.qml \ diff --git a/tests/auto/quick/qquickwebengineview/BLACKLIST b/tests/auto/quick/qquickwebengineview/BLACKLIST index 5bb38576a..166a6894e 100644 --- a/tests/auto/quick/qquickwebengineview/BLACKLIST +++ b/tests/auto/quick/qquickwebengineview/BLACKLIST @@ -15,7 +15,3 @@ opensuse-leap [javascriptClipboard:canPaste] opensuse-leap - -[changeLocale] -* - diff --git a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp index d467cd8ae..9817e7d6c 100644 --- a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp +++ b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp @@ -1001,6 +1001,7 @@ void tst_QQuickWebEngineView::changeLocale() viewDE->setUrl(url); QVERIFY(waitForLoadFailed(viewDE.data())); + QTRY_VERIFY(!evaluateJavaScriptSync(viewDE.data(), "document.body").isNull()); QTRY_VERIFY(!evaluateJavaScriptSync(viewDE.data(), "document.body.innerText").isNull()); errorLines = evaluateJavaScriptSync(viewDE.data(), "document.body.innerText").toString().split(QRegularExpression("[\r\n]"), QString::SkipEmptyParts); QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("Die Website ist nicht erreichbar Die Server-IP-Adresse von non.existent wurde nicht gefunden.")); @@ -1010,9 +1011,10 @@ void tst_QQuickWebEngineView::changeLocale() viewEN->setUrl(url); QVERIFY(waitForLoadFailed(viewEN.data())); + QTRY_VERIFY(!evaluateJavaScriptSync(viewEN.data(), "document.body").isNull()); QTRY_VERIFY(!evaluateJavaScriptSync(viewEN.data(), "document.body.innerText").isNull()); errorLines = evaluateJavaScriptSync(viewEN.data(), "document.body.innerText").toString().split(QRegularExpression("[\r\n]"), QString::SkipEmptyParts); - QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("This site can\xE2\x80\x99t be reached")); + QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("This site can\xE2\x80\x99t be reached non.existent\xE2\x80\x99s server IP address could not be found.")); // Reset error page viewDE->setUrl(QUrl("about:blank")); @@ -1022,9 +1024,10 @@ void tst_QQuickWebEngineView::changeLocale() viewDE->setUrl(url); QVERIFY(waitForLoadFailed(viewDE.data())); + QTRY_VERIFY(!evaluateJavaScriptSync(viewDE.data(), "document.body").isNull()); QTRY_VERIFY(!evaluateJavaScriptSync(viewDE.data(), "document.body.innerText").isNull()); errorLines = evaluateJavaScriptSync(viewDE.data(), "document.body.innerText").toString().split(QRegularExpression("[\r\n]"), QString::SkipEmptyParts); - QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("Die Website ist nicht erreichbar")); + QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("Die Website ist nicht erreichbar Die Server-IP-Adresse von non.existent wurde nicht gefunden.")); } void tst_QQuickWebEngineView::userScripts() @@ -1110,6 +1113,39 @@ void tst_QQuickWebEngineView::javascriptClipboard() QCOMPARE(evaluateJavaScriptSync(view, "document.execCommand('paste')").toBool(), pasteResult); QCOMPARE(evaluateJavaScriptSync(view, "document.getElementById('myInput').value").toString(), (pasteResult ? QString("AnotherText") : QString("OriginalText"))); + + // Test settings on clipboard permissions + evaluateJavaScriptSync(view, + QStringLiteral( + "var accessGranted = false;" + "var accessDenied = false;" + "var accessPrompt = false;" + "navigator.permissions.query({name:'clipboard-write'})" + ".then(result => {" + "if (result.state == 'granted') accessGranted = true;" + "if (result.state == 'denied') accessDenied = true;" + "if (result.state == 'prompt') accessPrompt = true;" + "})")); + + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessGranted").toBool(), copyResult); + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessDenied").toBool(), !javascriptCanAccessClipboard); + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessPrompt").toBool(), false); + + evaluateJavaScriptSync(view, + QStringLiteral( + "accessGranted = false;" + "accessDenied = false;" + "accessPrompt = false;" + "navigator.permissions.query({name:'clipboard-read'})" + ".then(result => {" + "if (result.state == 'granted') accessGranted = true;" + "if (result.state == 'denied') accessDenied = true;" + "if (result.state == 'prompt') accessPrompt = true;" + "})")); + + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessGranted").toBool(), pasteResult); + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessDenied").toBool(), !javascriptCanAccessClipboard || !javascriptCanPaste); + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessPrompt").toBool(), false); } QTEST_MAIN(tst_QQuickWebEngineView) diff --git a/tests/auto/shared/data/notification.html b/tests/auto/shared/data/notification.html new file mode 100644 index 000000000..cadcbd942 --- /dev/null +++ b/tests/auto/shared/data/notification.html @@ -0,0 +1,70 @@ +<!doctype html> +<html> +<head> +<title>Desktop Notifications Demo</title> +<script> + function resetPermission() { document.Notification = 'default' } + + function getPermission() { return document.Notification } + + function sendNotification(title, body) { + let notification = new Notification(title, { body: body }) + notification.onclick = function() { console.info('onclick') } + notification.onclose = function() { console.info('onclose') } + notification.onerror = function(error) { console.info('onerror: ' + error) } + notification.onshow = function() { console.info('onshow') } + } + + function makeNotification() { + let title = document.getElementById("title").value + let body = document.getElementById("body").value + console.log('making notification:', title) + sendNotification(title, body) + } + + function requestPermission(callback) { + Notification.requestPermission().then(function (permission) { + document.Notification = permission + if (callback) + callback(permission) + }) + } + + function displayNotification() { + console.info('notifications are ' + document.Notification) + + let state = document.getElementById('state') + + if (document.Notification === 'denied') { + state.innerHTML = 'Notifications disabled' + } else if (document.Notification === 'granted') { + makeNotification() + state.innerHTML = 'notification created' + } else { + state.innerHTML = 'requesting permission...' + requestPermission(function (permission) { + console.info('notifications request: ' + permission) + if (permission === 'granted') { + makeNotification() + state.innerHTML = 'permission granted, notification created' + } else if (permission === 'denied') + state.innerHTML = 'Notifications are disabled' + }) + } + } + + document.addEventListener("DOMContentLoaded", function() { + document.Notification = Notification.permission + }) +</script> +</head> +<body> + <form name="NotificationForm" id="notificationForm"> + Title: <input type="text" id="title" placeholder="Notification title" value='sample title'><br> + Body: <input type="text" id="body" placeholder="Notification body" value='default body'><br> + <input type="button" value="Display Notification" onclick="displayNotification()"><br> + <input type="button" value="Reset Permission" onclick="resetPermission()"> + </form> + <div id='state'></div> +</body> +</html> diff --git a/tests/auto/widgets/origins/tst_origins.cpp b/tests/auto/widgets/origins/tst_origins.cpp index 61c7232b0..61d16bc8a 100644 --- a/tests/auto/widgets/origins/tst_origins.cpp +++ b/tests/auto/widgets/origins/tst_origins.cpp @@ -637,10 +637,7 @@ 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)); + // Unregistered schemes should not create SharedWorkers. QVERIFY(load(QSL("PathSyntax:/resources/sharedWorker.html"))); QTRY_VERIFY(eval(QSL("done")).toBool()); diff --git a/tests/auto/widgets/proxypac/proxypac.pro b/tests/auto/widgets/proxypac/proxypac.pro index 00ae90977..1c2958d3a 100644 --- a/tests/auto/widgets/proxypac/proxypac.pro +++ b/tests/auto/widgets/proxypac/proxypac.pro @@ -3,26 +3,8 @@ QT += webengine HEADERS += proxyserver.h SOURCES += proxyserver.cpp -# QTBUG-71229 -xgd_desktop.name=XDG_CURRENT_DESKTOP -xgd_desktop.value=KDE -QT_TOOL_ENV += xgd_desktop +proxy_pac.name = QTWEBENGINE_CHROMIUM_FLAGS +proxy_pac.value = --proxy-pac-url="file://$$PWD/proxy.pac" -kde_home.name=KDEHOME -kde_home.value=$$OUT_PWD -QT_TOOL_ENV += kde_home - -PROXY_CONFIG= \ - "[Proxy Settings]" \ - "Proxy Config Script=$$PWD/proxy.pac" \ - "ProxyType=2" - -mkpath($$OUT_PWD/share/config) -KDE_FILE = $$OUT_PWD/share/config/kioslaverc - -!build_pass { - write_file($$KDE_FILE, PROXY_CONFIG) -} - -QMAKE_DISTCLEAN += $$KDE_FILE +QT_TOOL_ENV += proxy_pac diff --git a/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp b/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp index b50036aa8..32823d30d 100644 --- a/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp +++ b/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp @@ -620,7 +620,6 @@ void tst_QWebEngineDownloadItem::downloadPage_data() void tst_QWebEngineDownloadItem::downloadPage() { - QSKIP("Unstable since Chromium 71"); QFETCH(QWebEngineDownloadItem::SavePageFormat, savePageFormat); // Set up HTTP server diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index 0b519fe8a..0504d39fa 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -49,6 +49,7 @@ #include <qwebenginedownloaditem.h> #include <qwebenginefullscreenrequest.h> #include <qwebenginehistory.h> +#include <qwebenginenotification.h> #include <qwebenginepage.h> #include <qwebengineprofile.h> #include <qwebenginequotarequest.h> @@ -195,6 +196,10 @@ private Q_SLOTS: void triggerActionWithoutMenu(); void dynamicFrame(); + void notificationRequest_data(); + void notificationRequest(); + void sendNotification(); + private: static QPoint elementCenter(QWebEnginePage *page, const QString &id); @@ -3202,6 +3207,96 @@ void tst_QWebEnginePage::dynamicFrame() QCOMPARE(toPlainTextSync(&page).trimmed(), QStringLiteral("foo")); } +struct NotificationPage : ConsolePage { + Q_OBJECT + const QWebEnginePage::PermissionPolicy policy; + +public: + NotificationPage(QWebEnginePage::PermissionPolicy ppolicy) : policy(ppolicy) { + connect(this, &QWebEnginePage::loadFinished, [load = spyLoad.ref()] (bool result) mutable { load(result); }); + + connect(this, &QWebEnginePage::featurePermissionRequested, + [this] (const QUrl &origin, QWebEnginePage::Feature feature) { + if (feature != QWebEnginePage::Notifications) + return; + if (spyRequest.wasCalled()) + QFAIL("request executed twise!"); + setFeaturePermission(origin, feature, policy); + spyRequest.ref()(origin); + }); + + load(QStringLiteral("qrc:///shared/notification.html")); + } + + CallbackSpy<bool> spyLoad; + CallbackSpy<QUrl> spyRequest; + + QString getPermission() { return evaluateJavaScriptSync(this, "getPermission()").toString(); } + void requestPermission() { runJavaScript("requestPermission()"); } + void resetPermission() { runJavaScript("resetPermission()"); } + void sendNotification(const QString &title, const QString &body) { + runJavaScript("sendNotification('" + title + "', '" + body + "')"); + } +}; + +void tst_QWebEnginePage::notificationRequest_data() +{ + QTest::addColumn<QWebEnginePage::PermissionPolicy>("policy"); + QTest::addColumn<QString>("permission"); + QTest::newRow("deny") << QWebEnginePage::PermissionDeniedByUser << "denied"; + QTest::newRow("grant") << QWebEnginePage::PermissionGrantedByUser << "granted"; +} + +void tst_QWebEnginePage::notificationRequest() +{ + QFETCH(QWebEnginePage::PermissionPolicy, policy); + QFETCH(QString, permission); + + NotificationPage page(policy); + QVERIFY(page.spyLoad.waitForResult()); + + page.resetPermission(); + QCOMPARE(page.getPermission(), "default"); + + page.requestPermission(); + page.spyRequest.waitForResult(); + QVERIFY(page.spyRequest.wasCalled()); + + QCOMPARE(page.getPermission(), permission); +} + +void tst_QWebEnginePage::sendNotification() +{ + NotificationPage page(QWebEnginePage::PermissionGrantedByUser); + QVERIFY(page.spyLoad.waitForResult()); + + page.resetPermission(); + page.requestPermission(); + auto origin = page.spyRequest.waitForResult(); + QVERIFY(page.spyRequest.wasCalled()); + QCOMPARE(page.getPermission(), "granted"); + + CallbackSpy<QWebEngineNotification> presenter; + page.profile()->setNotificationPresenter([callback = presenter.ref()] (const QWebEngineNotification ¬ification) mutable { callback(notification); }); + + QString title("Title"), message("Message"); + page.sendNotification(title, message); + + auto notification = presenter.waitForResult(); + QVERIFY(presenter.wasCalled()); + QVERIFY(!notification.isNull()); + QCOMPARE(notification.title(), title); + QCOMPARE(notification.message(), message); + QCOMPARE(notification.origin(), origin); + + notification.show(); + QTRY_VERIFY2(page.messages.contains("onshow"), page.messages.join("\n").toLatin1().constData()); + notification.click(); + QTRY_VERIFY2(page.messages.contains("onclick"), page.messages.join("\n").toLatin1().constData()); + notification.close(); + QTRY_VERIFY2(page.messages.contains("onclose"), page.messages.join("\n").toLatin1().constData()); +} + static QByteArrayList params = {QByteArrayLiteral("--use-fake-device-for-media-stream")}; W_QTEST_MAIN(tst_QWebEnginePage, params) diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc index 3bb88cbe1..757e151c1 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc @@ -23,4 +23,7 @@ <file>resources/bar.txt</file> <file>resources/path with spaces.txt</file> </qresource> +<qresource prefix='/shared'> + <file alias='notification.html'>../../shared/data/notification.html</file> +</qresource> </RCC> diff --git a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp index bf5d320b7..b0db4e974 100644 --- a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp +++ b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp @@ -31,6 +31,7 @@ #include <QtTest/QtTest> #include <QtWebEngineCore/qwebengineurlrequestinterceptor.h> #include <QtWebEngineCore/qwebengineurlrequestjob.h> +#include <QtWebEngineCore/qwebenginecookiestore.h> #include <QtWebEngineCore/qwebengineurlschemehandler.h> #include <QtWebEngineWidgets/qwebengineprofile.h> #include <QtWebEngineWidgets/qwebenginepage.h> @@ -59,6 +60,7 @@ private Q_SLOTS: void downloadItem(); void changePersistentPath(); void initiator(); + void qtbug_72299(); // this should be the last test }; void tst_QWebEngineProfile::init() @@ -629,5 +631,19 @@ void tst_QWebEngineProfile::initiator() QCOMPARE(handler.initiator, QUrl()); } +void tst_QWebEngineProfile::qtbug_72299() +{ + QWebEngineView view; + view.setUrl(QUrl("https://www.qt.io")); + view.show(); + QSignalSpy loadSpy(view.page(), SIGNAL(loadFinished(bool))); + view.page()->profile()->clearHttpCache(); + view.page()->profile()->setHttpCacheType(QWebEngineProfile::NoCache); + view.page()->profile()->cookieStore()->deleteAllCookies(); + view.page()->profile()->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000); +} + + QTEST_MAIN(tst_QWebEngineProfile) #include "tst_qwebengineprofile.moc" diff --git a/tests/auto/widgets/qwebengineview/BLACKLIST b/tests/auto/widgets/qwebengineview/BLACKLIST index e8758abcc..7c86a72d6 100644 --- a/tests/auto/widgets/qwebengineview/BLACKLIST +++ b/tests/auto/widgets/qwebengineview/BLACKLIST @@ -6,6 +6,3 @@ osx [textSelectionOutOfInputField] * - -[changeLocale] -* diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp index f2810101b..17177c7fb 100644 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp +++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp @@ -46,6 +46,7 @@ #include <QTcpSocket> #include <QStyle> #include <QtWidgets/qaction.h> +#include <QWebEngineProfile> #include <QtCore/qregularexpression.h> #define VERIFY_INPUTMETHOD_HINTS(actual, expect) \ @@ -194,6 +195,7 @@ private Q_SLOTS: void jsKeyboardEvent(); void deletePage(); void closeOpenerTab(); + void switchPage(); }; // This will be called before the first test function is executed. @@ -3174,5 +3176,26 @@ void tst_QWebEngineView::closeOpenerTab() QVERIFY(newView->focusProxy()->isVisible()); } +void tst_QWebEngineView::switchPage() +{ + QWebEngineProfile profile; + QWebEnginePage page1(&profile); + QWebEnginePage page2(&profile); + QSignalSpy loadFinishedSpy1(&page1, SIGNAL(loadFinished(bool))); + QSignalSpy loadFinishedSpy2(&page2, SIGNAL(loadFinished(bool))); + page1.setHtml("<html><body bgcolor=\"#000000\"></body></html>"); + page2.setHtml("<html><body bgcolor=\"#ffffff\"></body></html>"); + QTRY_VERIFY(loadFinishedSpy1.count() && loadFinishedSpy2.count()); + QWebEngineView webView; + webView.resize(300,300); + webView.show(); + webView.setPage(&page1); + QTRY_COMPARE(webView.grab().toImage().pixelColor(QPoint(150,150)), Qt::black); + webView.setPage(&page2); + QTRY_COMPARE(webView.grab().toImage().pixelColor(QPoint(150,150)), Qt::white); + webView.setPage(&page1); + QTRY_COMPARE(webView.grab().toImage().pixelColor(QPoint(150,150)), Qt::black); +} + QTEST_MAIN(tst_QWebEngineView) #include "tst_qwebengineview.moc" diff --git a/tests/auto/widgets/widgets.pro b/tests/auto/widgets/widgets.pro index eec8bb389..0addb9671 100644 --- a/tests/auto/widgets/widgets.pro +++ b/tests/auto/widgets/widgets.pro @@ -9,6 +9,7 @@ SUBDIRS += \ faviconmanager \ loadsignals \ origins \ + proxypac \ schemes \ shutdown \ qwebenginedownloaditem \ @@ -27,9 +28,6 @@ qtConfig(webengine-printing-and-pdf) { SUBDIRS += printing } -# QTBUG-71229 -linux:!boot2qt: SUBDIRS += proxypac - qtConfig(webengine-spellchecker):!cross_compile { !qtConfig(webengine-native-spellchecker) { SUBDIRS += spellchecking @@ -43,4 +41,4 @@ boot2qt: SUBDIRS -= accessibility defaultsurfaceformat devtools \ faviconmanager qwebenginepage qwebenginehistory \ qwebengineprofile qwebenginescript \ qwebengineview qwebenginedownloaditem qwebenginesettings \ - schemes origins loadsignals + schemes origins loadsignals proxypac diff --git a/tests/quicktestbrowser/ApplicationRoot.qml b/tests/quicktestbrowser/ApplicationRoot.qml index 980016535..e2248e350 100644 --- a/tests/quicktestbrowser/ApplicationRoot.qml +++ b/tests/quicktestbrowser/ApplicationRoot.qml @@ -53,6 +53,7 @@ QtObject { var newWindow = browserWindowComponent.createObject(root) newWindow.currentWebView.profile = profile profile.downloadRequested.connect(newWindow.onDownloadRequested) + profile.userNotification.connect(newWindow.onUserNotification) return newWindow } function createDialog(profile) { diff --git a/tests/quicktestbrowser/BrowserWindow.qml b/tests/quicktestbrowser/BrowserWindow.qml index 22f98e1c5..381e9c142 100644 --- a/tests/quicktestbrowser/BrowserWindow.qml +++ b/tests/quicktestbrowser/BrowserWindow.qml @@ -505,6 +505,18 @@ ApplicationWindow { download.accept() } + MessageDialog { + id: notificationDialog + width: 200 + standardButtons: StandardButton.Ok + } + + function onUserNotification(notification) { + notificationDialog.title = notification.title + notificationDialog.text = notification.origin.toString() + '\n' + notification.message + notificationDialog.open() + } + ZoomController { id: zoomController y: parent.mapFromItem(currentWebView, 0 , 0).y - 4 diff --git a/tests/quicktestbrowser/FeaturePermissionBar.qml b/tests/quicktestbrowser/FeaturePermissionBar.qml index 9c0b25966..500d13206 100644 --- a/tests/quicktestbrowser/FeaturePermissionBar.qml +++ b/tests/quicktestbrowser/FeaturePermissionBar.qml @@ -40,10 +40,24 @@ Rectangle { visible: false height: acceptButton.height + 4 - onRequestedFeatureChanged: { - message.text = securityOrigin + " wants to access " + message.textForFeature(requestedFeature); + + function textForFeature(feature) { + switch (feature) { + case WebEngineView.Geolocation: return 'Allow %1 to access your location information?' + case WebEngineView.MediaAudioCapture: return 'Allow %1 to access your microphone?' + case WebEngineView.MediaVideoCapture: return 'Allow %1 to access your webcam?' + case WebEngineView.MediaAudioVideoCapture: return 'Allow %1 to access your microphone and webcam?' + case WebEngineView.DesktopVideoCapture: return 'Allow %1 to capture video of your desktop?' + case WebEngineView.DesktopAudioVideoCapture: return 'Allow %1 to capture audio and video of your desktop?' + case WebEngineView.Notifications: return 'Allow %1 to show notification on your desktop?' + default: break + } + return 'Grant permission for %1 to unknown or unsupported feature [' + feature + ']?' } + onRequestedFeatureChanged: { + message.text = textForFeature(requestedFeature).arg(securityOrigin); + } RowLayout { anchors { @@ -54,17 +68,6 @@ Rectangle { Label { id: message Layout.fillWidth: true - - function textForFeature(feature) { - if (feature === WebEngineView.MediaAudioCapture) - return "your microphone" - if (feature === WebEngineView.MediaVideoCapture) - return "your camera" - if (feature === WebEngineView.MediaAudioVideoCapture) - return "your camera and microphone" - if (feature === WebEngineView.Geolocation) - return "your position" - } } Button { |
