diff options
208 files changed, 7356 insertions, 3197 deletions
diff --git a/.qmake.conf b/.qmake.conf index 3c71512ec..26852b80b 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -5,4 +5,4 @@ QTWEBENGINE_OUT_ROOT = $$shadowed($$PWD) load(qt_build_config) CONFIG += warning_clean -MODULE_VERSION = 5.12.2 +MODULE_VERSION = 5.13.0 diff --git a/config.tests/winversion/winversion.cpp b/config.tests/winversion/winversion.cpp index 4d117b945..e9a4cff59 100644 --- a/config.tests/winversion/winversion.cpp +++ b/config.tests/winversion/winversion.cpp @@ -26,7 +26,7 @@ ** ****************************************************************************/ -#if !defined(__clang__) && _MSC_FULL_VER < 191125507 +#if !defined(__clang__) && _MSC_FULL_VER < 191426428 #error unsupported Visual Studio version #endif diff --git a/configure.json b/configure.json index 40d6bfe56..60b7342f7 100644 --- a/configure.json +++ b/configure.json @@ -1,5 +1,6 @@ { "subconfigs": [ - "src/core" + "src/core", + "src/webengine" ] } diff --git a/examples/webengine/quicknanobrowser/ApplicationRoot.qml b/examples/webengine/quicknanobrowser/ApplicationRoot.qml index 67b686541..3bc571546 100644 --- a/examples/webengine/quicknanobrowser/ApplicationRoot.qml +++ b/examples/webengine/quicknanobrowser/ApplicationRoot.qml @@ -49,7 +49,7 @@ ****************************************************************************/ import QtQuick 2.1 -import QtWebEngine 1.2 +import QtWebEngine 1.9 QtObject { id: root @@ -57,6 +57,7 @@ QtObject { property QtObject defaultProfile: WebEngineProfile { storageName: "Profile" offTheRecord: false + useForGlobalCertificateVerification: true } property QtObject otrProfile: WebEngineProfile { diff --git a/examples/webengine/quicknanobrowser/BrowserWindow.qml b/examples/webengine/quicknanobrowser/BrowserWindow.qml index 8d62482da..c0cb4283f 100644 --- a/examples/webengine/quicknanobrowser/BrowserWindow.qml +++ b/examples/webengine/quicknanobrowser/BrowserWindow.qml @@ -57,7 +57,7 @@ import QtQuick.Controls.Styles 1.0 import QtQuick.Dialogs 1.2 import QtQuick.Layouts 1.0 import QtQuick.Window 2.1 -import QtWebEngine 1.7 +import QtWebEngine 1.9 ApplicationWindow { id: browserWindow @@ -490,6 +490,10 @@ ApplicationWindow { tabs.removeTab(tabs.currentIndex); } + onSelectClientCertificate: function(selection) { + selection.certificates[0].select(); + } + Timer { id: reloadTimer interval: 0 diff --git a/examples/webengine/webengine.pro b/examples/webengine/webengine.pro index b20b8f118..5ad620390 100644 --- a/examples/webengine/webengine.pro +++ b/examples/webengine/webengine.pro @@ -3,7 +3,8 @@ TEMPLATE=subdirs SUBDIRS += \ customdialogs \ minimal \ - quicknanobrowser + quicknanobrowser \ + webengineaction qtHaveModule(quickcontrols2) { SUBDIRS += \ diff --git a/examples/webengine/webengineaction/doc/images/webengineaction-example.png b/examples/webengine/webengineaction/doc/images/webengineaction-example.png Binary files differnew file mode 100644 index 000000000..2e34bbf63 --- /dev/null +++ b/examples/webengine/webengineaction/doc/images/webengineaction-example.png diff --git a/examples/webengine/webengineaction/doc/src/webengineaction.qdoc b/examples/webengine/webengineaction/doc/src/webengineaction.qdoc new file mode 100644 index 000000000..672f86f0f --- /dev/null +++ b/examples/webengine/webengineaction/doc/src/webengineaction.qdoc @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example webengine/webengineaction + \title WebEngine Action Example + \ingroup webengine-examples + \brief A simple browser implemented using WebEngineActions. + + \image webengineaction-example.png + + \e {WebEngine Action Example} demonstrates how to perform actions on a web page + using the \l{WebEngineAction} type. It shows the minimum amount of code needed + to bind browser functionalities to input elements and build up a custom context + menu. + + \include examples-run.qdocinc + + \section1 Working With Web Engine Actions + + An intended use of \l{WebEngineAction} is building a connection between UI + elements and browser commands. It can be added to menus and toolbars via + assigning its properties to the corresponding ones of the element. + + The \l{ToolButton} relies on the properties provided by a + \l{WebEngineAction}. Clicking the button triggers backwards navigation on the + originating \l{WebEngineView} of the action. + + \quotefromfile webengine/webengineaction/main.qml + \skipto ToolButton { + \printuntil } + + The simplest way to create custom context menus is enumerating the required + \l{WebEngineAction} types in a data model and instantiating \l{MenuItem} types + for them, for example using a \l{Repeater}. + + \quotefromfile webengine/webengineaction/main.qml + \skipto property Menu contextMenu: Menu { + \printuntil /^ {8}\}/ + + Assigning a \l{WebEngineAction} to multiple UI elements will keep them in sync. + As it can be seen in the picture above, if the browser engine disables a + navigation action, both corresponding menu items will be disabled. +*/ diff --git a/examples/webengine/webengineaction/main.cpp b/examples/webengine/webengineaction/main.cpp new file mode 100644 index 000000000..ce723a99b --- /dev/null +++ b/examples/webengine/webengineaction/main.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QGuiApplication> +#include <QQmlApplicationEngine> +#include <qtwebengineglobal.h> + +int main(int argc, char *argv[]) +{ + QCoreApplication::setOrganizationName("QtExamples"); + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QGuiApplication app(argc, argv); + + QtWebEngine::initialize(); + + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + + return app.exec(); +} diff --git a/examples/webengine/webengineaction/main.qml b/examples/webengine/webengineaction/main.qml new file mode 100644 index 000000000..a3933ee41 --- /dev/null +++ b/examples/webengine/webengineaction/main.qml @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Window 2.0 +import QtWebEngine 1.7 +import QtQuick.Controls 2.4 +import QtQuick.Layouts 1.11 + +ApplicationWindow { + id: window + visible: true + width: 800 + height: 600 + title: qsTr("WebEngineAction Example") + + header: ToolBar { + RowLayout { + anchors.fill: parent + + ToolButton { + property int itemAction: WebEngineView.Back + text: webEngineView.action(itemAction).text + enabled: webEngineView.action(itemAction).enabled + onClicked: webEngineView.action(itemAction).trigger() + icon.name: webEngineView.action(itemAction).iconName + display: AbstractButton.TextUnderIcon + } + + ToolButton { + property int itemAction: WebEngineView.Forward + text: webEngineView.action(itemAction).text + enabled: webEngineView.action(itemAction).enabled + onClicked: webEngineView.action(itemAction).trigger() + icon.name: webEngineView.action(itemAction).iconName + display: AbstractButton.TextUnderIcon + } + + ToolButton { + property int itemAction: webEngineView.loading ? WebEngineView.Stop : WebEngineView.Reload + text: webEngineView.action(itemAction).text + enabled: webEngineView.action(itemAction).enabled + onClicked: webEngineView.action(itemAction).trigger() + icon.name: webEngineView.action(itemAction).iconName + display: AbstractButton.TextUnderIcon + } + + TextField { + Layout.fillWidth: true + + text: webEngineView.url + selectByMouse: true + onEditingFinished: webEngineView.url = text + } + + ToolButton { + id: settingsButton + text: "Settings" + icon.name: "settings-configure" + display: AbstractButton.TextUnderIcon + onClicked: settingsMenu.open() + checked: settingsMenu.visible + + Menu { + id: settingsMenu + y: settingsButton.height + + MenuItem { + id: customContextMenuOption + checkable: true + checked: true + + text: "Custom context menu" + } + } + } + } + } + + WebEngineView { + id: webEngineView + url: "https://qt.io" + anchors.fill: parent + + Component.onCompleted: { + profile.downloadRequested.connect(function(download){ + download.accept(); + }) + } + + property Menu contextMenu: Menu { + Repeater { + model: [ + WebEngineView.Back, + WebEngineView.Forward, + WebEngineView.Reload, + WebEngineView.SavePage, + WebEngineView.Copy, + WebEngineView.Paste, + WebEngineView.Cut + ] + MenuItem { + text: webEngineView.action(modelData).text + enabled: webEngineView.action(modelData).enabled + onClicked: webEngineView.action(modelData).trigger() + icon.name: webEngineView.action(modelData).iconName + display: MenuItem.TextBesideIcon + } + } + } + + onContextMenuRequested: function(request) { + if (customContextMenuOption.checked) { + request.accepted = true; + contextMenu.popup(); + } + } + } +} diff --git a/examples/webengine/webengineaction/qml.qrc b/examples/webengine/webengineaction/qml.qrc new file mode 100644 index 000000000..5f6483ac3 --- /dev/null +++ b/examples/webengine/webengineaction/qml.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>main.qml</file> + </qresource> +</RCC> diff --git a/examples/webengine/webengineaction/webengineaction.pro b/examples/webengine/webengineaction/webengineaction.pro new file mode 100644 index 000000000..13cc4602d --- /dev/null +++ b/examples/webengine/webengineaction/webengineaction.pro @@ -0,0 +1,10 @@ +TEMPLATE = app + +QT += webengine + +SOURCES += main.cpp + +RESOURCES += qml.qrc + +target.path = $$[QT_INSTALL_EXAMPLES]/webengine/webengineaction +INSTALLS += target diff --git a/examples/webenginewidgets/simplebrowser/main.cpp b/examples/webenginewidgets/simplebrowser/main.cpp index 0e8f86c8f..7b77a4bd2 100644 --- a/examples/webenginewidgets/simplebrowser/main.cpp +++ b/examples/webenginewidgets/simplebrowser/main.cpp @@ -52,6 +52,7 @@ #include "browserwindow.h" #include "tabwidget.h" #include <QApplication> +#include <QWebEngineProfile> #include <QWebEngineSettings> QUrl commandLineUrlArgument() @@ -74,6 +75,8 @@ int main(int argc, char **argv) app.setWindowIcon(QIcon(QStringLiteral(":AppLogoColor.png"))); QWebEngineSettings::defaultSettings()->setAttribute(QWebEngineSettings::PluginsEnabled, true); + QWebEngineSettings::defaultSettings()->setAttribute(QWebEngineSettings::DnsPrefetchEnabled, true); + QWebEngineProfile::defaultProfile()->setUseForGlobalCertificateVerification(); QUrl url = commandLineUrlArgument(); diff --git a/mkspecs/features/gn_generator.prf b/mkspecs/features/gn_generator.prf index c1399a453..e6ae263ac 100644 --- a/mkspecs/features/gn_generator.prf +++ b/mkspecs/features/gn_generator.prf @@ -29,12 +29,12 @@ isEmpty(GN_FIND_MOCABLES_SCRIPT): GN_FIND_MOCABLES_SCRIPT = "//build/gn_find_moc # MOC SETUP GN_CONTENTS += "moc_source_h_files = exec_script(\"$$GN_FIND_MOCABLES_SCRIPT\"," -GN_CONTENTS += " [ \"$$_PRO_FILE_PWD_\"," +GN_CONTENTS += " [" for (headerfile, HEADERS): GN_CONTENTS += " \"$$GN_SRC_DIR/$$headerfile\"," GN_CONTENTS += " ], \"list lines\", [\"$$system_path($$_PRO_FILE_)\"]"\ ")" GN_CONTENTS += "moc_source_cpp_files = exec_script(\"$$GN_FIND_MOCABLES_SCRIPT\"," -GN_CONTENTS += " [ \"$$_PRO_FILE_PWD_\"," +GN_CONTENTS += " [" for (sourcefile, SOURCES): GN_CONTENTS += " \"$$GN_SRC_DIR/$$sourcefile\"," GN_CONTENTS += " ], \"list lines\", [\"$$system_path($$_PRO_FILE_)\"]"\ ")" diff --git a/src/3rdparty b/src/3rdparty -Subproject 323b45aa242e0b4e75689d67418ec124ba1ca81 +Subproject c677809fe785df63fe97608cb5a949302b2f0c6 diff --git a/src/core/accessibility_activation_observer.cpp b/src/core/accessibility_activation_observer.cpp new file mode 100644 index 000000000..75ad90c54 --- /dev/null +++ b/src/core/accessibility_activation_observer.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** 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 "accessibility_activation_observer.h" + +#ifndef QT_NO_ACCESSIBILITY + +#include "content/browser/accessibility/browser_accessibility_state_impl.h" + +namespace QtWebEngineCore { + +namespace { + +bool isAccessibilityEnabled() { + // On Linux accessibility is disabled by default due to performance issues, + // and can be re-enabled by setting the QTWEBENGINE_ENABLE_LINUX_ACCESSIBILITY environment + // variable. For details, see QTBUG-59922. +#ifdef Q_OS_LINUX + static bool accessibility_enabled + = qEnvironmentVariableIsSet("QTWEBENGINE_ENABLE_LINUX_ACCESSIBILITY"); +#else + const bool accessibility_enabled = true; +#endif + return accessibility_enabled; +} + +} // namespace + +AccessibilityActivationObserver::AccessibilityActivationObserver() +{ + if (isAccessibilityEnabled()) { + QAccessible::installActivationObserver(this); + if (QAccessible::isActive()) + content::BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility(); + } +} + +AccessibilityActivationObserver::~AccessibilityActivationObserver() +{ + QAccessible::removeActivationObserver(this); +} + +void AccessibilityActivationObserver::accessibilityActiveChanged(bool active) +{ + if (active) + content::BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility(); + else + content::BrowserAccessibilityStateImpl::GetInstance()->DisableAccessibility(); +} + +} // namespace QtWebEngineCore + +#endif // QT_NO_ACCESSIBILITY diff --git a/src/core/net/qrc_protocol_handler_qt.cpp b/src/core/accessibility_activation_observer.h index eb716f182..e42c83eb5 100644 --- a/src/core/net/qrc_protocol_handler_qt.cpp +++ b/src/core/accessibility_activation_observer.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** 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. @@ -37,27 +37,27 @@ ** ****************************************************************************/ -#include "qrc_protocol_handler_qt.h" -#include "url_request_qrc_job_qt.h" +#ifndef ACCESSIBILITY_ACTIVATION_OBSERVER_H +#define ACCESSIBILITY_ACTIVATION_OBSERVER_H -#include "net/base/net_errors.h" -#include "net/url_request/url_request.h" -#include "net/url_request/url_request_error_job.h" +#ifndef QT_NO_ACCESSIBILITY +#include <QtGui/qaccessible.h> namespace QtWebEngineCore { -const char kQrcSchemeQt[] = "qrc"; +class RenderWidgetHostViewQt; -QrcProtocolHandlerQt::QrcProtocolHandlerQt() +class AccessibilityActivationObserver : public QAccessible::ActivationObserver { -} +public: + AccessibilityActivationObserver(); + ~AccessibilityActivationObserver(); -net::URLRequestJob *QrcProtocolHandlerQt::MaybeCreateJob(net::URLRequest *request, net::NetworkDelegate *networkDelegate) const -{ - if (!networkDelegate) - return new net::URLRequestErrorJob(request, Q_NULLPTR, net::ERR_ACCESS_DENIED); - - return new URLRequestQrcJobQt(request, networkDelegate); -} + void accessibilityActiveChanged(bool active) override; +}; } // namespace QtWebEngineCore + +#endif // QT_NO_ACCESSIBILITY + +#endif // ACCESSIBILITY_ACTIVATION_OBSERVER_H diff --git a/src/core/api/core_api.pro b/src/core/api/core_api.pro index 38dc6b39d..b5bb93847 100644 --- a/src/core/api/core_api.pro +++ b/src/core/api/core_api.pro @@ -32,6 +32,7 @@ gcc: QMAKE_CXXFLAGS_WARN_ON = -Wno-unused-parameter HEADERS = \ qwebenginecallback.h \ qwebenginecallback_p.h \ + qwebengineclientcertificatestore.h \ qtwebenginecoreglobal.h \ qtwebenginecoreglobal_p.h \ qwebenginecookiestore.h \ diff --git a/src/core/api/qwebengineclientcertificatestore.h b/src/core/api/qwebengineclientcertificatestore.h new file mode 100644 index 000000000..6ba998465 --- /dev/null +++ b/src/core/api/qwebengineclientcertificatestore.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWEBENGINECLIENTCERTIFICATESTORE_H +#define QWEBENGINECLIENTCERTIFICATESTORE_H + +#include <QtWebEngineCore/qtwebenginecoreglobal.h> + +#include <QtNetwork/qsslcertificate.h> +#include <QtNetwork/qsslkey.h> + +QT_BEGIN_NAMESPACE + +#if QT_CONFIG(ssl) + +struct QWebEngineClientCertificateStoreData; + +class QWEBENGINECORE_EXPORT QWebEngineClientCertificateStore { + +public: + struct Entry { + QSslKey privateKey; + QSslCertificate certificate; + }; + + static QWebEngineClientCertificateStore *getInstance(); + void add(const QSslCertificate &certificate, const QSslKey &privateKey); + QList<Entry> toList() const; + void remove(Entry entry); + void clear(); + +private: + static QWebEngineClientCertificateStore *m_instance; + Q_DISABLE_COPY(QWebEngineClientCertificateStore) + + QWebEngineClientCertificateStore(); + ~QWebEngineClientCertificateStore(); + QWebEngineClientCertificateStoreData *d_ptr; +}; + +#endif // QT_CONFIG(ssl) + +QT_END_NAMESPACE + +#endif // QWebEngineClientCertificateStore_H diff --git a/src/core/api/qwebengineurlrequestinfo.cpp b/src/core/api/qwebengineurlrequestinfo.cpp index ea9081fc1..c3e5b5445 100644 --- a/src/core/api/qwebengineurlrequestinfo.cpp +++ b/src/core/api/qwebengineurlrequestinfo.cpp @@ -115,8 +115,11 @@ ASSERT_ENUMS_MATCH(QtWebEngineCore::WebContentsAdapterClient::OtherNavigation, Q \fn void QWebEngineUrlRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info) Reimplementing this virtual function makes it possible to intercept URL - requests. This function is executed on the IO thread, and therefore running - long tasks here will block networking. + 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. \a info contains the information about the URL request and will track internally whether its members have been altered. @@ -140,10 +143,17 @@ QWebEngineUrlRequestInfoPrivate::QWebEngineUrlRequestInfoPrivate(QWebEngineUrlRe /*! \internal */ +QWebEngineUrlRequestInfo::QWebEngineUrlRequestInfo(QWebEngineUrlRequestInfo &&p) + : d_ptr(p.d_ptr.take()) +{ +} + +/*! + \internal +*/ QWebEngineUrlRequestInfo::~QWebEngineUrlRequestInfo() { - } /*! @@ -260,6 +270,14 @@ bool QWebEngineUrlRequestInfo::changed() const } /*! + \internal +*/ +void QWebEngineUrlRequestInfo::resetChanged() +{ + d_ptr->changed = false; +} + +/*! Redirects this request to \a url. It is only possible to redirect requests that do not have payload data, such as GET requests. */ diff --git a/src/core/api/qwebengineurlrequestinfo.h b/src/core/api/qwebengineurlrequestinfo.h index 68c46dcf4..e1f9ca6ef 100644 --- a/src/core/api/qwebengineurlrequestinfo.h +++ b/src/core/api/qwebengineurlrequestinfo.h @@ -47,6 +47,7 @@ namespace QtWebEngineCore { class NetworkDelegateQt; +class URLRequestNotification; } QT_BEGIN_NAMESPACE @@ -104,10 +105,14 @@ public: private: friend class QtWebEngineCore::NetworkDelegateQt; + friend class QtWebEngineCore::URLRequestNotification; Q_DISABLE_COPY(QWebEngineUrlRequestInfo) Q_DECLARE_PRIVATE(QWebEngineUrlRequestInfo) + void resetChanged(); + QWebEngineUrlRequestInfo(QWebEngineUrlRequestInfoPrivate *p); + QWebEngineUrlRequestInfo(QWebEngineUrlRequestInfo &&p); ~QWebEngineUrlRequestInfo(); QScopedPointer<QWebEngineUrlRequestInfoPrivate> d_ptr; }; diff --git a/src/core/api/qwebengineurlrequestjob.cpp b/src/core/api/qwebengineurlrequestjob.cpp index c3541598b..41b43d42c 100644 --- a/src/core/api/qwebengineurlrequestjob.cpp +++ b/src/core/api/qwebengineurlrequestjob.cpp @@ -140,6 +140,15 @@ QUrl QWebEngineUrlRequestJob::initiator() const } /*! + \since 5.13 + Returns any HTTP headers added to the request. +*/ +const QMap<QByteArray, QByteArray> &QWebEngineUrlRequestJob::requestHeaders() const +{ + return d_ptr->requestHeaders(); +} + +/*! Replies to the request with \a device and the MIME type \a contentType. The user has to be aware that \a device will be used on another thread diff --git a/src/core/api/qwebengineurlrequestjob.h b/src/core/api/qwebengineurlrequestjob.h index 7ce8be7ec..55ec7c6d2 100644 --- a/src/core/api/qwebengineurlrequestjob.h +++ b/src/core/api/qwebengineurlrequestjob.h @@ -73,6 +73,7 @@ public: QUrl requestUrl() const; QByteArray requestMethod() const; QUrl initiator() const; + const QMap<QByteArray, QByteArray> &requestHeaders() const; void reply(const QByteArray &contentType, QIODevice *device); void fail(Error error); diff --git a/src/core/api/qwebengineurlschemehandler.cpp b/src/core/api/qwebengineurlschemehandler.cpp index 6f06b2c6e..2e93f4b73 100644 --- a/src/core/api/qwebengineurlschemehandler.cpp +++ b/src/core/api/qwebengineurlschemehandler.cpp @@ -111,7 +111,6 @@ QWebEngineUrlSchemeHandler::QWebEngineUrlSchemeHandler(QObject *parent) */ QWebEngineUrlSchemeHandler::~QWebEngineUrlSchemeHandler() { - Q_EMIT _q_destroyedUrlSchemeHandler(this); } /*! diff --git a/src/core/api/qwebengineurlschemehandler.h b/src/core/api/qwebengineurlschemehandler.h index 23fee4b95..549778561 100644 --- a/src/core/api/qwebengineurlschemehandler.h +++ b/src/core/api/qwebengineurlschemehandler.h @@ -60,11 +60,6 @@ public: virtual void requestStarted(QWebEngineUrlRequestJob*) = 0; -#ifndef Q_QDOC -Q_SIGNALS: - void _q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*); -#endif - private: Q_DISABLE_COPY(QWebEngineUrlSchemeHandler) }; diff --git a/src/core/authentication_dialog_controller.cpp b/src/core/authentication_dialog_controller.cpp index bd23d1768..1133b0bc1 100644 --- a/src/core/authentication_dialog_controller.cpp +++ b/src/core/authentication_dialog_controller.cpp @@ -40,7 +40,9 @@ #include "authentication_dialog_controller.h" #include "authentication_dialog_controller_p.h" +#include "base/task/post_task.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/browser_task_traits.h" namespace QtWebEngineCore { @@ -51,9 +53,10 @@ AuthenticationDialogControllerPrivate::AuthenticationDialogControllerPrivate(Log void AuthenticationDialogControllerPrivate::dialogFinished(bool accepted, const QString &user, const QString &password) { - content::BrowserThread::PostTask( - content::BrowserThread::IO, FROM_HERE, - base::Bind(&LoginDelegateQt::sendAuthToRequester, loginDelegate, accepted, user, password)); + base::PostTaskWithTraits( + FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&LoginDelegateQt::sendAuthToRequester, + loginDelegate, accepted, user, password)); } AuthenticationDialogController::AuthenticationDialogController(AuthenticationDialogControllerPrivate *dd) diff --git a/src/core/browser_accessibility_qt.cpp b/src/core/browser_accessibility_qt.cpp index a672ccc06..c906071f2 100644 --- a/src/core/browser_accessibility_qt.cpp +++ b/src/core/browser_accessibility_qt.cpp @@ -61,6 +61,11 @@ const BrowserAccessibilityQt *ToBrowserAccessibilityQt(const BrowserAccessibilit return static_cast<const BrowserAccessibilityQt *>(obj); } +QAccessibleInterface *toQAccessibleInterface(BrowserAccessibility *obj) +{ + return static_cast<BrowserAccessibilityQt *>(obj); +} + BrowserAccessibilityQt::BrowserAccessibilityQt() { QAccessible::registerAccessibleInterface(this); @@ -356,6 +361,8 @@ QAccessible::Role BrowserAccessibilityQt::role() const return QAccessible::EditableText; case ax::mojom::Role::kInputTime: return QAccessible::SpinBox; + case ax::mojom::Role::kKeyboard: + return QAccessible::NoRole; // FIXME case ax::mojom::Role::kLabelText: return QAccessible::StaticText; case ax::mojom::Role::kLayoutTable: diff --git a/src/core/browser_accessibility_qt.h b/src/core/browser_accessibility_qt.h index 345ee9862..decfc1e9d 100644 --- a/src/core/browser_accessibility_qt.h +++ b/src/core/browser_accessibility_qt.h @@ -146,6 +146,7 @@ public: }; const BrowserAccessibilityQt *ToBrowserAccessibilityQt(const BrowserAccessibility *obj); +QAccessibleInterface *toQAccessibleInterface(BrowserAccessibility *acc); } // namespace content diff --git a/src/core/browser_main_parts_qt.cpp b/src/core/browser_main_parts_qt.cpp index 8f39386d4..cfe9ea8bb 100644 --- a/src/core/browser_main_parts_qt.cpp +++ b/src/core/browser_main_parts_qt.cpp @@ -59,6 +59,12 @@ #include <QEventLoop> #include <QObject> #include <QTimerEvent> +#include <QtGui/qtgui-config.h> + +#if QT_CONFIG(opengl) +#include "ui/gl/gl_context.h" +#include <QOpenGLContext> +#endif #if defined(OS_MACOSX) #include "ui/base/idle/idle.h" @@ -156,8 +162,55 @@ protected: } private: + // Both Qt and Chromium keep track of the current GL context by using + // thread-local variables, and naturally they are completely unaware of each + // other. As a result, when a QOpenGLContext is made current, the previous + // gl::GLContext is not released, and vice-versa. This is fine as long as + // each thread uses exclusively either Qt or Chromium GL bindings, which is + // usually the case. + // + // The only exception is when the GL driver is considered thread-unsafe + // (QOpenGLContext::supportsThreadedOpenGL() is false), in which case we + // have to run all GL operations, including Chromium's GPU service, on the + // UI thread. Now the bindings are being mixed and both Qt and Chromium get + // quite confused regarding the current state of the surface. + // + // To get this to work we have to release the current QOpenGLContext before + // executing any tasks from Chromium's GPU service and the gl::GLContext + // afterwards. Since GPU service just posts tasks to the UI thread task + // runner, we'll have to instrument the entire UI thread message pump. + class ScopedGLContextChecker + { +#if QT_CONFIG(opengl) + public: + ScopedGLContextChecker() + { + if (!m_enabled) + return; + + if (QOpenGLContext *context = QOpenGLContext::currentContext()) + context->doneCurrent(); + } + + ~ScopedGLContextChecker() + { + if (!m_enabled) + return; + + if (gl::GLContext *context = gl::GLContext::GetCurrent()) + context->ReleaseCurrent(nullptr); + } + + private: + bool m_enabled = !QOpenGLContext::supportsThreadedOpenGL(); +#endif // QT_CONFIG(opengl) + }; + + void handleScheduledWork() { + ScopedGLContextChecker glContextChecker; + bool more_work_is_plausible = m_delegate->DoWork(); base::TimeTicks delayed_work_time; @@ -230,11 +283,9 @@ void BrowserMainPartsQt::ServiceManagerConnectionStarted(content::ServiceManager { ServiceQt::GetInstance()->InitConnector(); connection->GetConnector()->StartService(service_manager::Identity("qtwebengine")); - if (resource_coordinator::IsResourceCoordinatorEnabled()) { - m_processResourceCoordinator = std::make_unique<resource_coordinator::ProcessResourceCoordinator>(connection->GetConnector()); - m_processResourceCoordinator->SetLaunchTime(base::Time::Now()); - m_processResourceCoordinator->SetPID(base::Process::Current().Pid()); - } + m_processResourceCoordinator = std::make_unique<resource_coordinator::ProcessResourceCoordinator>(connection->GetConnector()); + m_processResourceCoordinator->SetLaunchTime(base::Time::Now()); + m_processResourceCoordinator->SetPID(base::Process::Current().Pid()); } } // namespace QtWebEngineCore diff --git a/src/core/chromium_overrides.cpp b/src/core/chromium_overrides.cpp index 841dcf4c9..c25d6dda9 100644 --- a/src/core/chromium_overrides.cpp +++ b/src/core/chromium_overrides.cpp @@ -59,6 +59,7 @@ #include <QWindow> #include <QFontDatabase> #include <QStringList> +#include <QLibraryInfo> #if defined(USE_AURA) && !defined(USE_OZONE) #include "ui/base/dragdrop/os_exchange_data.h" @@ -119,6 +120,14 @@ void WebContentsView::GetDefaultScreenInfo(content::ScreenInfo* results) QtWebEngineCore::GetScreenInfoFromNativeWindow(&dummy, results); } +#if defined(Q_OS_MACOS) +std::string getQtPrefix() +{ + const QString prefix = QLibraryInfo::location(QLibraryInfo::PrefixPath); + return prefix.toStdString(); +} +#endif + } // namespace content #if defined(USE_AURA) || defined(USE_OZONE) diff --git a/src/core/client_cert_override_key.cpp b/src/core/client_cert_override_key.cpp new file mode 100644 index 000000000..99ddf7466 --- /dev/null +++ b/src/core/client_cert_override_key.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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 "base/logging.h" +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "net/base/net_errors.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 { + +namespace { + +class SSLPlatformKeyOverride : public ThreadedSSLPrivateKey::Delegate { +public: + SSLPlatformKeyOverride(const QByteArray &sslKeyInBytes) + { + mem_ = BIO_new_mem_buf(sslKeyInBytes, -1); + key_ = PEM_read_bio_PrivateKey(mem_, NULL, 0, NULL); + } + + ~SSLPlatformKeyOverride() override { + if (key_) + EVP_PKEY_free(key_); + if (mem_) + BIO_free(mem_); + } + + 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; + if (!EVP_DigestSignInit(ctx.get(), &pctx, + SSL_get_signature_algorithm_digest(algorithm), + nullptr, key_)) { + return 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; + } + } + size_t sig_len = 0; + if (!EVP_DigestSign(ctx.get(), NULL, &sig_len, input.data(), input.size())) + return 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; + } + signature->resize(sig_len); + return 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, + }; + } + +private: + EVP_PKEY* key_; + BIO * mem_; + + DISALLOW_COPY_AND_ASSIGN(SSLPlatformKeyOverride); +}; + +} // namespace + +scoped_refptr<SSLPrivateKey> WrapOpenSSLPrivateKey(const QByteArray &sslKeyInBytes) { + if (sslKeyInBytes.isEmpty()) + return nullptr; + + return base::MakeRefCounted<ThreadedSSLPrivateKey>( + std::make_unique<SSLPlatformKeyOverride>(sslKeyInBytes), + GetSSLPlatformKeyTaskRunner()); +} + +} // namespace net + +#endif diff --git a/src/core/client_cert_override_key_p.h b/src/core/client_cert_override_key_p.h new file mode 100644 index 000000000..7ac610be4 --- /dev/null +++ b/src/core/client_cert_override_key_p.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CLIENT_CERT_OVERRIDE_KEY_P_H +#define CLIENT_CERT_OVERRIDE_KEY_P_H + +#include "net/ssl/ssl_private_key.h" + +#include <QByteArray> + +namespace net { + class SSLPrivateKey; + scoped_refptr<SSLPrivateKey> WrapOpenSSLPrivateKey(const QByteArray &sslKeyInBytes); +} // namespace net + +#endif diff --git a/src/core/client_cert_override_p.h b/src/core/client_cert_override_p.h new file mode 100644 index 000000000..b222bf810 --- /dev/null +++ b/src/core/client_cert_override_p.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CLIENT_CERT_OVERRIDE_P_H +#define CLIENT_CERT_OVERRIDE_P_H + +#include "net/ssl/client_cert_store.h" +#include "base/callback_forward.h" +#include "net/cert/x509_certificate.h" + +namespace net { +class SSLCertRequestInfo; +class ClientCertOverrideStore : public ClientCertStore +{ +public: + ClientCertOverrideStore(); + virtual ~ClientCertOverrideStore() override; + void GetClientCerts(const SSLCertRequestInfo &cert_request_info, + const ClientCertListCallback &callback) override; +private: + std::unique_ptr<net::ClientCertStore> getNativeStore(); +}; +} // namespace net + +#endif + + diff --git a/src/core/client_cert_select_controller.cpp b/src/core/client_cert_select_controller.cpp index 7d08d57c1..0baaf2bc5 100644 --- a/src/core/client_cert_select_controller.cpp +++ b/src/core/client_cert_select_controller.cpp @@ -48,6 +48,8 @@ #include "type_conversion.h" +#include <QDebug> + QT_BEGIN_NAMESPACE using namespace QtWebEngineCore; @@ -76,17 +78,40 @@ ClientCertSelectController::~ClientCertSelectController() void ClientCertSelectController::selectNone() { if (m_selected) { - qWarning() << "ClientCertSelectController::selectNone() certicate already selected"; + LOG(WARNING) << "ClientCertSelectController::selectNone() certificate already selected"; return; } m_selected = true; m_delegate->ContinueWithCertificate(nullptr, nullptr); } +void ClientCertSelectController::select(int index) +{ + if (m_selected) { + LOG(WARNING) << "ClientCertSelectController::select() certificate already selected"; + return; + } + for (auto &certInfo : m_clientCerts) { + if (index == 0) { + m_selected = true; + scoped_refptr<net::X509Certificate> cert = certInfo->certificate(); + net::ClientCertIdentity::SelfOwningAcquirePrivateKey( + std::move(certInfo), + base::Bind(&content::ClientCertificateDelegate::ContinueWithCertificate, + base::Passed(std::move(m_delegate)), std::move(cert))); + return; + } + std::vector<std::string> pem_encoded; + if (certInfo->certificate()->GetPEMEncodedChain(&pem_encoded)) + --index; + } + LOG(WARNING) << "ClientCertSelectController::select() index out of range:" << index; +} + void ClientCertSelectController::select(const QSslCertificate &certificate) { if (m_selected) { - qWarning() << "ClientCertSelectController::select() certicate already selected"; + LOG(WARNING) << "ClientCertSelectController::select() certificate already selected"; return; } QByteArray derCertificate = certificate.toDer(); @@ -103,19 +128,20 @@ void ClientCertSelectController::select(const QSslCertificate &certificate) return; } } - qWarning() << "ClientCertSelectController::select() - selected client certificate not recognized." - << " Selected certificate needs to be one of the offered"; + LOG(WARNING) << "ClientCertSelectController::select() - selected client certificate not recognized." + << " Selected certificate needs to be one of the offered"; } QVector<QSslCertificate> ClientCertSelectController::certificates() const { - QVector<QSslCertificate> out; + if (!m_certificates.isEmpty()) + return m_certificates; for (auto &cert : m_clientCerts) { std::vector<std::string> pem_encoded; if (cert->certificate()->GetPEMEncodedChain(&pem_encoded)) - out.append(QSslCertificate(QByteArray::fromStdString(pem_encoded.front()))); + m_certificates.append(QSslCertificate(QByteArray::fromStdString(pem_encoded.front()))); } - return out; + return m_certificates; } #endif // !defined(QT_NO_SSL) || QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) diff --git a/src/core/client_cert_select_controller.h b/src/core/client_cert_select_controller.h index 46324ee90..8f4f78d94 100644 --- a/src/core/client_cert_select_controller.h +++ b/src/core/client_cert_select_controller.h @@ -83,6 +83,7 @@ public: #if !defined(QT_NO_SSL) || QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) void selectNone(); void select(const QSslCertificate &certificate); + void select(int index); QVector<QSslCertificate> certificates() const; #endif @@ -91,6 +92,7 @@ private: QUrl m_hostAndPort; std::vector<std::unique_ptr<net::ClientCertIdentity>> m_clientCerts; std::unique_ptr<content::ClientCertificateDelegate> m_delegate; + mutable QVector<QSslCertificate> m_certificates; bool m_selected; }; diff --git a/src/core/color_chooser_controller.cpp b/src/core/color_chooser_controller.cpp index 26d675908..361ff93ba 100644 --- a/src/core/color_chooser_controller.cpp +++ b/src/core/color_chooser_controller.cpp @@ -43,6 +43,9 @@ #include "color_chooser_controller_p.h" #include "type_conversion.h" +#include <QColor> +#include <QVariant> + namespace QtWebEngineCore { ColorChooserControllerPrivate::ColorChooserControllerPrivate(content::WebContents *content, const QColor &color) diff --git a/src/core/color_chooser_controller.h b/src/core/color_chooser_controller.h index 4c1b81a9a..e5daa09a8 100644 --- a/src/core/color_chooser_controller.h +++ b/src/core/color_chooser_controller.h @@ -55,6 +55,9 @@ #include <QObject> +QT_FORWARD_DECLARE_CLASS(QColor) +QT_FORWARD_DECLARE_CLASS(QVariant) + namespace QtWebEngineCore { class ColorChooserControllerPrivate; diff --git a/src/core/chromium_gpu_helper.cpp b/src/core/compositor/chromium_gpu_helper.cpp index 92a8b13ed..71d0f3687 100644 --- a/src/core/chromium_gpu_helper.cpp +++ b/src/core/compositor/chromium_gpu_helper.cpp @@ -43,10 +43,14 @@ #include "chromium_gpu_helper.h" +// Some headers include the namespace ws, and can not coexist with +// Qt headers that include QTextStream, which includes most QSG headers +// via QMatrix4x4. +#include "content/browser/renderer_host/render_widget_host_impl.h" + // Including gpu/command_buffer headers before content/gpu headers makes sure that // guards are defined to prevent duplicate definition errors with forward declared // GL typedefs cascading through content header includes. -#include "gpu/command_buffer/service/sync_point_manager.h" #include "gpu/command_buffer/service/mailbox_manager.h" #include "gpu/command_buffer/service/texture_base.h" @@ -62,12 +66,6 @@ scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner() return content::GpuChildThread::instance()->main_thread_runner(); } -gpu::SyncPointManager *sync_point_manager() -{ - gpu::GpuChannelManager *gpuChannelManager = content::GpuChildThread::instance()->gpu_channel_manager(); - return gpuChannelManager->sync_point_manager(); -} - gpu::MailboxManager *mailbox_manager() { gpu::GpuChannelManager *gpuChannelManager = content::GpuChildThread::instance()->gpu_channel_manager(); @@ -85,6 +83,11 @@ unsigned int service_id(gpu::TextureBase *tex) return tex->service_id(); } +void ProgressFlingIfNeeded(content::RenderWidgetHost *host, const base::TimeTicks ¤t_time) +{ + content::RenderWidgetHostImpl::From(host)->ProgressFlingIfNeeded(current_time); +} + #ifdef Q_OS_QNX EGLStreamData eglstream_connect_consumer(gpu::Texture *tex) { diff --git a/src/core/chromium_gpu_helper.h b/src/core/compositor/chromium_gpu_helper.h index 21b764997..4086d12ab 100644 --- a/src/core/chromium_gpu_helper.h +++ b/src/core/compositor/chromium_gpu_helper.h @@ -46,11 +46,15 @@ namespace base { class SingleThreadTaskRunner; +class TimeTicks; +} + +namespace content { +class RenderWidgetHost; } namespace gpu { struct Mailbox; -class SyncPointManager; class MailboxManager; class TextureBase; } @@ -61,12 +65,13 @@ class TextureBase; // functions should only be forward-declared and considered as opaque types. scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner(); -gpu::SyncPointManager *sync_point_manager(); gpu::MailboxManager *mailbox_manager(); gpu::TextureBase* ConsumeTexture(gpu::MailboxManager *mailboxManager, unsigned target, const gpu::Mailbox& mailbox); unsigned int service_id(gpu::TextureBase *tex); +void ProgressFlingIfNeeded(content::RenderWidgetHost *host, const base::TimeTicks ¤t_time); + #ifdef Q_OS_QNX typedef void* EGLDisplay; typedef void* EGLStreamKHR; diff --git a/src/core/compositor.cpp b/src/core/compositor/compositor.cpp index f7a5e651c..0660c155b 100644 --- a/src/core/compositor.cpp +++ b/src/core/compositor/compositor.cpp @@ -39,26 +39,27 @@ #include "compositor.h" +#include "compositor_resource_tracker.h" #include "delegated_frame_node.h" -#include "render_widget_host_view_qt.h" +#include "base/task/post_task.h" #include "components/viz/common/resources/returned_resource.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h" namespace QtWebEngineCore { -Compositor::Compositor(RenderWidgetHostViewQt *hostView) - : m_chromiumCompositorData(new ChromiumCompositorData) - , m_view(hostView) +Compositor::Compositor(content::RenderWidgetHost *host) + : m_resourceTracker(new CompositorResourceTracker) + , m_host(host) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - base::SingleThreadTaskRunner *taskRunner = - content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::UI).get(); + m_taskRunner = base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::UI}); m_beginFrameSource = std::make_unique<viz::DelayBasedBeginFrameSource>( - std::make_unique<viz::DelayBasedTimeSource>(taskRunner), + std::make_unique<viz::DelayBasedTimeSource>(m_taskRunner.get()), viz::BeginFrameSource::kNotRestartableId); } @@ -67,13 +68,6 @@ Compositor::~Compositor() DCHECK_CURRENTLY_ON(content::BrowserThread::UI); } -void Compositor::setViewDelegate(RenderWidgetHostViewQtDelegate *viewDelegate) -{ - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - - m_viewDelegate = viewDelegate; -} - void Compositor::setFrameSinkClient(viz::mojom::CompositorFrameSinkClient *frameSinkClient) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); @@ -85,7 +79,7 @@ void Compositor::setFrameSinkClient(viz::mojom::CompositorFrameSinkClient *frame // should not be returned. // // TODO(juvaldma): Can there be a pending frame from the old client? - m_resourcesToRelease.clear(); + m_resourceTracker->returnResources(); m_frameSinkClient = frameSinkClient; } @@ -104,21 +98,19 @@ void Compositor::setNeedsBeginFrames(bool needsBeginFrames) m_needsBeginFrames = needsBeginFrames; } -void Compositor::submitFrame(viz::CompositorFrame frame) +void Compositor::submitFrame(viz::CompositorFrame frame, base::OnceClosure callback) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - DCHECK(!m_havePendingFrame); - - m_chromiumCompositorData->frameDevicePixelRatio = frame.metadata.device_scale_factor; - m_chromiumCompositorData->previousFrameData = std::move(m_chromiumCompositorData->frameData); - m_chromiumCompositorData->frameData = std::move(frame); - m_havePendingFrame = true; + DCHECK(!m_submitCallback); - // Tell viewDelegate to call updatePaintNode() soon. - m_viewDelegate->update(); + m_pendingFrame = std::move(frame); + m_submitCallback = std::move(callback); + m_resourceTracker->submitResources( + m_pendingFrame, + base::BindOnce(&Compositor::runSubmitCallback, base::Unretained(this))); } -QSGNode *Compositor::updatePaintNode(QSGNode *oldNode) +QSGNode *Compositor::updatePaintNode(QSGNode *oldNode, RenderWidgetHostViewQtDelegate *viewDelegate) { // DCHECK_CURRENTLY_ON(content::BrowserThread::UI); // @@ -129,30 +121,42 @@ QSGNode *Compositor::updatePaintNode(QSGNode *oldNode) if (!frameNode) frameNode = new DelegatedFrameNode; - frameNode->commit(m_chromiumCompositorData.data(), &m_resourcesToRelease, m_viewDelegate); - - if (m_havePendingFrame) { - m_havePendingFrame = false; - content::BrowserThread::PostTask( - content::BrowserThread::UI, FROM_HERE, - base::BindOnce(&Compositor::notifyFrameCommitted, m_weakPtrFactory.GetWeakPtr())); + if (!m_updatePaintNodeShouldCommit) { + frameNode->commit(m_committedFrame, viz::CompositorFrame(), m_resourceTracker.get(), viewDelegate); + return frameNode; } - if (m_chromiumCompositorData->frameData.metadata.request_presentation_feedback) - content::BrowserThread::PostTask( - content::BrowserThread::UI, FROM_HERE, - base::BindOnce(&Compositor::sendPresentationFeedback, m_weakPtrFactory.GetWeakPtr(), m_chromiumCompositorData->frameData.metadata.frame_token)); + m_updatePaintNodeShouldCommit = false; + + if (m_committedFrame.metadata.request_presentation_feedback) + m_taskRunner->PostTask(FROM_HERE, + base::BindOnce(&Compositor::sendPresentationFeedback, m_weakPtrFactory.GetWeakPtr(), + m_committedFrame.metadata.frame_token)); + m_resourceTracker->commitResources(); + frameNode->commit(m_pendingFrame, m_committedFrame, m_resourceTracker.get(), viewDelegate); + m_committedFrame = std::move(m_pendingFrame); + m_pendingFrame = viz::CompositorFrame(); + + m_taskRunner->PostTask(FROM_HERE, + base::BindOnce(&Compositor::notifyFrameCommitted, m_weakPtrFactory.GetWeakPtr())); return frameNode; } +void Compositor::runSubmitCallback() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + m_updatePaintNodeShouldCommit = true; + std::move(m_submitCallback).Run(); +} + void Compositor::notifyFrameCommitted() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); m_beginFrameSource->DidFinishFrame(this); if (m_frameSinkClient) - m_frameSinkClient->DidReceiveCompositorFrameAck(m_resourcesToRelease); - m_resourcesToRelease.clear(); + m_frameSinkClient->DidReceiveCompositorFrameAck(m_resourceTracker->returnResources()); } void Compositor::sendPresentationFeedback(uint frame_token) @@ -165,7 +169,7 @@ bool Compositor::OnBeginFrameDerivedImpl(const viz::BeginFrameArgs &args) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - m_view->OnBeginFrame(args.frame_time); + ProgressFlingIfNeeded(m_host, args.frame_time); m_beginFrameSource->OnUpdateVSyncParameters(args.frame_time, args.interval); if (m_frameSinkClient) m_frameSinkClient->OnBeginFrame(args); diff --git a/src/core/compositor.h b/src/core/compositor/compositor.h index 7d7db5d04..69f4a530c 100644 --- a/src/core/compositor.h +++ b/src/core/compositor/compositor.h @@ -42,6 +42,10 @@ #include <base/memory/weak_ptr.h> #include <components/viz/common/frame_sinks/begin_frame_source.h> +#include <components/viz/common/quads/compositor_frame.h> + +#include <QtCore/qglobal.h> +#include <QtCore/qshareddata.h> #include <QtCore/qglobal.h> #include <QtCore/qshareddata.h> @@ -50,8 +54,10 @@ QT_BEGIN_NAMESPACE class QSGNode; QT_END_NAMESPACE +namespace content { +class RenderWidgetHost; +} namespace viz { -class CompositorFrame; struct ReturnedResource; namespace mojom { class CompositorFrameSinkClient; @@ -60,9 +66,8 @@ class CompositorFrameSinkClient; namespace QtWebEngineCore { -class RenderWidgetHostViewQt; +class CompositorResourceTracker; class RenderWidgetHostViewQtDelegate; -class ChromiumCompositorData; // Receives viz::CompositorFrames from child compositors and provides QSGNodes // to the Qt Quick renderer. @@ -72,10 +77,10 @@ class ChromiumCompositorData; // Step 1. A new CompositorFrame is received from child compositors and handed // off to submitFrame(). The new frame will start off in a pending state. // -// Step 2. Once the new frame is ready to be rendered, Compositor will call -// update() on the delegate. +// Step 2. Once the new frame is ready to be rendered, Compositor will notify +// the client by running the callback given to submitFrame(). // -// Step 3. Once the delegate is ready to render, updatePaintNode() should be +// Step 3. Once the client is ready to render, updatePaintNode() should be // called to receive the scene graph for the new frame. This call will commit // the pending frame. Until the next frame is ready, all subsequent calls to // updatePaintNode() will keep using this same committed frame. @@ -85,18 +90,17 @@ class ChromiumCompositorData; class Compositor final : private viz::BeginFrameObserverBase { public: - explicit Compositor(RenderWidgetHostViewQt *hostView); + explicit Compositor(content::RenderWidgetHost *host); ~Compositor() override; - void setViewDelegate(RenderWidgetHostViewQtDelegate *viewDelegate); void setFrameSinkClient(viz::mojom::CompositorFrameSinkClient *frameSinkClient); void setNeedsBeginFrames(bool needsBeginFrames); - void submitFrame(viz::CompositorFrame frame); - - QSGNode *updatePaintNode(QSGNode *oldNode); + void submitFrame(viz::CompositorFrame frame, base::OnceClosure callback); + QSGNode *updatePaintNode(QSGNode *oldNode, RenderWidgetHostViewQtDelegate *viewDelegate); private: + void runSubmitCallback(); void notifyFrameCommitted(); void sendPresentationFeedback(uint frame_token); @@ -104,15 +108,17 @@ private: bool OnBeginFrameDerivedImpl(const viz::BeginFrameArgs &args) override; void OnBeginFrameSourcePausedChanged(bool paused) override; - std::vector<viz::ReturnedResource> m_resourcesToRelease; - QExplicitlySharedDataPointer<ChromiumCompositorData> m_chromiumCompositorData; - RenderWidgetHostViewQt *m_view; - RenderWidgetHostViewQtDelegate *m_viewDelegate = nullptr; + viz::CompositorFrame m_committedFrame; + viz::CompositorFrame m_pendingFrame; + base::OnceClosure m_submitCallback; + std::unique_ptr<CompositorResourceTracker> m_resourceTracker; + content::RenderWidgetHost *m_host; std::unique_ptr<viz::SyntheticBeginFrameSource> m_beginFrameSource; viz::mojom::CompositorFrameSinkClient *m_frameSinkClient = nullptr; - bool m_havePendingFrame = false; + bool m_updatePaintNodeShouldCommit = false; bool m_needsBeginFrames = false; + scoped_refptr<base::SingleThreadTaskRunner> m_taskRunner; base::WeakPtrFactory<Compositor> m_weakPtrFactory{this}; DISALLOW_COPY_AND_ASSIGN(Compositor); diff --git a/src/core/compositor/compositor_resource.h b/src/core/compositor/compositor_resource.h new file mode 100644 index 000000000..c40ce3d5e --- /dev/null +++ b/src/core/compositor/compositor_resource.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef COMPOSITOR_RESOURCE_H +#define COMPOSITOR_RESOURCE_H + +#include <base/memory/ref_counted.h> +#include <components/viz/common/resources/transferable_resource.h> + +#include <QtCore/qglobal.h> +#include <QtGui/qtgui-config.h> + +#if QT_CONFIG(opengl) +# include "compositor_resource_fence.h" +#endif + +namespace viz { +class SharedBitmap; +} // namespace viz + +namespace QtWebEngineCore { + +using CompositorResourceId = quint32; + +// A resource (OpenGL texture or software shared bitmap). +// +// - Created by the CompositorResourceTracker from a newly submitted +// CompositorFrame's resource_list. +// +// - Until the frame is committed, its resources are in a 'pending' state and +// are inaccessible from outside the CompositorResourceTracker. +// +// - Once the frame is committed, its resources can be found via +// CompositorResourceTracker::findResource. +// +// - 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 +{ + CompositorResource(const viz::TransferableResource &tr) : viz::TransferableResource(tr) {} + + // Counts the number of times this resource has been encountered in + // CompositorFrames' resource lists. + // + // Corresponds to viz::ReturnedResource::count. + // + // Updated by CompositorResourceTracker on UI thread. + int import_count = 1; + + // Identifies the last frame that needed this resource. Used by + // CompositorResourceTracker to return unused resources back to child + // compositors. + // + // Updated by CompositorResourceTracker on UI thread. + quint32 last_used_for_frame = 0; + + // Bitmap (if is_software). + std::unique_ptr<viz::SharedBitmap> bitmap; + +#if QT_CONFIG(opengl) + // OpenGL texture id (if !is_software). + quint32 texture_id = 0; + + // Should be waited on before using the texture (non-null if !is_software). + scoped_refptr<CompositorResourceFence> texture_fence; +#endif // QT_CONFIG(opengl) +}; + +inline bool operator<(const CompositorResource &r1, const CompositorResource &r2) +{ + return r1.id < r2.id; +} + +inline bool operator<(const CompositorResource &r, CompositorResourceId id) +{ + return r.id < id; +} + +inline bool operator<(CompositorResourceId id, const CompositorResource &r) +{ + return id < r.id; +} + +} // namespace QtWebEngineCore + +#endif // !COMPOSITOR_RESOURCE_H diff --git a/src/core/compositor/compositor_resource_fence.cpp b/src/core/compositor/compositor_resource_fence.cpp new file mode 100644 index 000000000..7fc5fbfb2 --- /dev/null +++ b/src/core/compositor/compositor_resource_fence.cpp @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** 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 "compositor_resource_fence.h" + +#include "ui/gl/gl_context.h" + +#include <QtGui/qopenglcontext.h> + +#ifndef GL_TIMEOUT_IGNORED +#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull +#endif + +namespace QtWebEngineCore { + +void CompositorResourceFence::wait() +{ + if (!m_sync) + return; + + QOpenGLContext *context = QOpenGLContext::currentContext(); + Q_ASSERT(context); + + // Chromium uses its own GL bindings and stores in in thread local storage. + // For that reason, let chromium_gpu_helper.cpp contain the producing code that will run in the Chromium + // GPU thread, and put the sync consuming code here that will run in the QtQuick SG or GUI thread. + switch (m_sync.type) { + case gl::TransferableFence::NoSync: + break; + case gl::TransferableFence::EglSync: +#ifdef EGL_KHR_reusable_sync + { + static bool resolved = false; + static PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR = 0; + + if (!resolved) { + if (gl::GLSurfaceQt::HasEGLExtension("EGL_KHR_fence_sync")) + eglClientWaitSyncKHR = (PFNEGLCLIENTWAITSYNCKHRPROC)context->getProcAddress("eglClientWaitSyncKHR"); + resolved = true; + } + + if (eglClientWaitSyncKHR) + // FIXME: Use the less wasteful eglWaitSyncKHR once we have a device that supports EGL_KHR_wait_sync. + eglClientWaitSyncKHR(m_sync.egl.display, m_sync.egl.sync, 0, EGL_FOREVER_KHR); + } +#endif + break; + case gl::TransferableFence::ArbSync: + typedef void (QOPENGLF_APIENTRYP WaitSyncPtr)(GLsync sync, GLbitfield flags, GLuint64 timeout); + static WaitSyncPtr glWaitSync_ = 0; + if (!glWaitSync_) { + glWaitSync_ = (WaitSyncPtr)context->getProcAddress("glWaitSync"); + Q_ASSERT(glWaitSync_); + } + glWaitSync_(m_sync.arb.sync, 0, GL_TIMEOUT_IGNORED); + break; + } + + release(); +} + +void CompositorResourceFence::release() +{ + if (!m_sync) + return; + + QOpenGLContext *context = QOpenGLContext::currentContext(); + if (!context) + return; + + // Chromium uses its own GL bindings and stores in in thread local storage. + // For that reason, let chromium_gpu_helper.cpp contain the producing code that will run in the Chromium + // GPU thread, and put the sync consuming code here that will run in the QtQuick SG or GUI thread. + switch (m_sync.type) { + case gl::TransferableFence::NoSync: + break; + case gl::TransferableFence::EglSync: +#ifdef EGL_KHR_reusable_sync + { + static bool resolved = false; + static PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR = 0; + + if (!resolved) { + if (gl::GLSurfaceQt::HasEGLExtension("EGL_KHR_fence_sync")) + eglDestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC)context->getProcAddress("eglDestroySyncKHR"); + resolved = true; + } + + if (eglDestroySyncKHR) { + // FIXME: Use the less wasteful eglWaitSyncKHR once we have a device that supports EGL_KHR_wait_sync. + eglDestroySyncKHR(m_sync.egl.display, m_sync.egl.sync); + m_sync.reset(); + } + } +#endif + break; + case gl::TransferableFence::ArbSync: + typedef void (QOPENGLF_APIENTRYP DeleteSyncPtr)(GLsync sync); + static DeleteSyncPtr glDeleteSync_ = 0; + if (!glDeleteSync_) { + glDeleteSync_ = (DeleteSyncPtr)context->getProcAddress("glDeleteSync"); + Q_ASSERT(glDeleteSync_); + } + glDeleteSync_(m_sync.arb.sync); + m_sync.reset(); + break; + } + // If Chromium was able to create a sync, we should have been able to handle its type here too. + Q_ASSERT(!m_sync); +} + +// static +scoped_refptr<CompositorResourceFence> CompositorResourceFence::create() +{ + if (gl::GLContext::GetCurrent() && gl::GLFence::IsSupported()) { + std::unique_ptr<gl::GLFence> glFence{gl::GLFence::Create()}; + return base::MakeRefCounted<CompositorResourceFence>(glFence->Transfer()); + } + return nullptr; +} + +} // namespace QtWebEngineCore diff --git a/src/core/net/url_request_qrc_job_qt.h b/src/core/compositor/compositor_resource_fence.h index 11c130693..1c2ea3695 100644 --- a/src/core/net/url_request_qrc_job_qt.h +++ b/src/core/compositor/compositor_resource_fence.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** 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. @@ -37,40 +37,35 @@ ** ****************************************************************************/ -#ifndef URL_REQUEST_QRC_JOB_QT_H_ -#define URL_REQUEST_QRC_JOB_QT_H_ +#ifndef COMPOSITOR_RESOURCE_FENCE_H +#define COMPOSITOR_RESOURCE_FENCE_H -#include "net/url_request/url_request.h" -#include "net/url_request/url_request_job.h" - -#include <QFile> +#include <base/memory/ref_counted.h> +#include <ui/gl/gl_fence.h> namespace QtWebEngineCore { -// A request job that handles reading qrc file URLs -class URLRequestQrcJobQt : public net::URLRequestJob { - +// Sync object created on GPU thread and consumed on render thread. +class CompositorResourceFence final : public base::RefCountedThreadSafe<CompositorResourceFence> +{ public: - URLRequestQrcJobQt(net::URLRequest *request, net::NetworkDelegate *networkDelegate); - void Start() override; - void Kill() override; - int ReadRawData(net::IOBuffer* buf, int buf_size) override;; - bool GetMimeType(std::string *mimeType) const override; + REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE(); -protected: - virtual ~URLRequestQrcJobQt(); - // Get file mime type and try open file on a background thread. - void startGetHead(); + CompositorResourceFence() {} + CompositorResourceFence(const gl::TransferableFence &sync) : m_sync(sync) {}; + ~CompositorResourceFence() { release(); } -private: - qint64 m_remainingBytes; - QFile m_file; - std::string m_mimeType; - base::WeakPtrFactory<URLRequestQrcJobQt> m_weakFactory; + // May be used only by Qt Quick render thread. + void wait(); + void release(); - DISALLOW_COPY_AND_ASSIGN(URLRequestQrcJobQt); + // May be used only by GPU thread. + static scoped_refptr<CompositorResourceFence> create(); + +private: + gl::TransferableFence m_sync; }; } // namespace QtWebEngineCore -#endif // URL_REQUEST_QRC_JOB_QT_H_ +#endif // !COMPOSITOR_RESOURCE_FENCE_H diff --git a/src/core/compositor/compositor_resource_tracker.cpp b/src/core/compositor/compositor_resource_tracker.cpp new file mode 100644 index 000000000..6530f3249 --- /dev/null +++ b/src/core/compositor/compositor_resource_tracker.cpp @@ -0,0 +1,259 @@ +/**************************************************************************** +** +** 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 "compositor_resource_tracker.h" + +#include "chromium_gpu_helper.h" +#include "render_widget_host_view_qt_delegate.h" +#include "web_engine_context.h" + +#include "base/message_loop/message_loop.h" +#include "base/task/post_task.h" +#include "components/viz/common/quads/compositor_frame.h" +#include "components/viz/common/resources/returned_resource.h" +#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h" +#include "content/browser/browser_main_loop.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/gpu/content_gpu_client.h" +#include "gpu/command_buffer/service/mailbox_manager.h" +#include "gpu/command_buffer/service/sync_point_manager.h" + +namespace QtWebEngineCore { + +CompositorResourceTracker::CompositorResourceTracker() +{} + +CompositorResourceTracker::~CompositorResourceTracker() +{} + +void CompositorResourceTracker::submitResources(const viz::CompositorFrame &frame, base::OnceClosure callback) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + DCHECK(!m_submitCallback); + DCHECK(m_pendingResources.empty()); + DCHECK(m_pendingImports.empty()); + DCHECK(m_pendingResourceUpdates == 0); + + m_submitCallback = std::move(callback); + + m_pendingResources.reserve(frame.resource_list.size()); + m_pendingImports.reserve(frame.resource_list.size()); + + for (const viz::TransferableResource &transferableResource : frame.resource_list) { + auto it = m_committedResources.find(transferableResource.id); + if (it != m_committedResources.end()) + m_pendingImports.push_back(&*it); + else + m_pendingResources.emplace_back(transferableResource); + } + + if (m_pendingResources.empty()) { + scheduleRunSubmitCallback(); + return; + } + + m_pendingResourceUpdates = m_pendingResources.size(); + + std::vector<CompositorResource *> batch; + batch.reserve(m_pendingResources.size()); + + for (CompositorResource &resource : m_pendingResources) { + if (resource.is_software) + updateBitmap(&resource); + else if (!scheduleUpdateMailbox(&resource)) + batch.push_back(&resource); + } + + if (!batch.empty()) + scheduleUpdateMailboxes(std::move(batch)); +} + +void CompositorResourceTracker::commitResources() +{ + // DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + // + // This might be called from a Qt Quick render thread, but the UI thread + // will still be blocked for the duration of this call. + + DCHECK(m_pendingResourceUpdates == 0); + + for (CompositorResource *resource : m_pendingImports) + resource->import_count++; + m_pendingImports.clear(); + + m_committedResources.insert(std::make_move_iterator(m_pendingResources.begin()), + std::make_move_iterator(m_pendingResources.end())); + m_pendingResources.clear(); + + ++m_committedFrameId; +} + +std::vector<viz::ReturnedResource> CompositorResourceTracker::returnResources() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + std::vector<viz::ReturnedResource> returnedResources; + base::EraseIf(m_committedResources, [&](const CompositorResource &resource) { + if (resource.last_used_for_frame != m_committedFrameId) { + viz::ReturnedResource returnedResource; + returnedResource.id = resource.id; + returnedResource.count = resource.import_count; + returnedResources.push_back(std::move(returnedResource)); + return true; + } + return false; + }); + return returnedResources; +} + +const CompositorResource *CompositorResourceTracker::findResource(CompositorResourceId id) const +{ + auto it = m_committedResources.find(id); + DCHECK(it != m_committedResources.end()); + + const_cast<CompositorResource &>(*it).last_used_for_frame = m_committedFrameId; + + return &*it; +} + +void CompositorResourceTracker::updateBitmap(CompositorResource *resource) +{ + content::BrowserMainLoop *browserMainLoop = content::BrowserMainLoop::GetInstance(); + viz::ServerSharedBitmapManager *bitmapManager = browserMainLoop->GetServerSharedBitmapManager(); + + resource->bitmap = bitmapManager->GetSharedBitmapFromId( + resource->size, + viz::BGRA_8888, + resource->mailbox_holder.mailbox); + + if (--m_pendingResourceUpdates == 0) + scheduleRunSubmitCallback(); +} + +quint32 CompositorResourceTracker::consumeMailbox(const gpu::MailboxHolder &mailboxHolder) +{ +#if QT_CONFIG(opengl) + gpu::MailboxManager *mailboxManager = mailbox_manager(); + DCHECK(mailboxManager); + if (mailboxHolder.sync_token.HasData()) + mailboxManager->PullTextureUpdates(mailboxHolder.sync_token); + return service_id(mailboxManager->ConsumeTexture(mailboxHolder.mailbox)); +#else + NOTREACHED(); +#endif // QT_CONFIG(OPENGL) +} + +bool CompositorResourceTracker::scheduleUpdateMailbox(CompositorResource *resource) +{ +#if QT_CONFIG(opengl) + gpu::SyncPointManager *syncPointManager = WebEngineContext::syncPointManager(); + DCHECK(syncPointManager); + return syncPointManager->WaitOutOfOrder( + resource->mailbox_holder.sync_token, + base::BindOnce(&CompositorResourceTracker::updateMailbox, + m_weakPtrFactory.GetWeakPtr(), + resource)); +#else + NOTREACHED(); +#endif // QT_CONFIG(OPENGL) +} + +void CompositorResourceTracker::updateMailbox(CompositorResource *resource) +{ +#if QT_CONFIG(opengl) + resource->texture_id = consumeMailbox(resource->mailbox_holder); + resource->texture_fence = CompositorResourceFence::create(); + + if (--m_pendingResourceUpdates == 0) + scheduleRunSubmitCallback(); +#else + NOTREACHED(); +#endif // QT_CONFIG(OPENGL) +} + +void CompositorResourceTracker::scheduleUpdateMailboxes(std::vector<CompositorResource *> resources) +{ +#if QT_CONFIG(opengl) + scoped_refptr<base::SingleThreadTaskRunner> gpuTaskRunner = gpu_task_runner(); + DCHECK(gpuTaskRunner); + gpuTaskRunner->PostTask( + FROM_HERE, + base::BindOnce(&CompositorResourceTracker::updateMailboxes, + m_weakPtrFactory.GetWeakPtr(), + std::move(resources))); +#else + NOTREACHED(); +#endif // QT_CONFIG(OPENGL) +} + +void CompositorResourceTracker::updateMailboxes(std::vector<CompositorResource *> resources) +{ +#if QT_CONFIG(opengl) + for (CompositorResource *resource : resources) + resource->texture_id = consumeMailbox(resource->mailbox_holder); + + scoped_refptr<CompositorResourceFence> fence = CompositorResourceFence::create(); + + for (CompositorResource *resource : resources) + resource->texture_fence = fence; + + if ((m_pendingResourceUpdates -= resources.size()) == 0) + scheduleRunSubmitCallback(); +#else + NOTREACHED(); +#endif // QT_CONFIG(OPENGL) +} + +void CompositorResourceTracker::scheduleRunSubmitCallback() +{ + base::PostTaskWithTraits( + FROM_HERE, { content::BrowserThread::UI }, + base::BindOnce(&CompositorResourceTracker::runSubmitCallback, + m_weakPtrFactory.GetWeakPtr())); +} + +void CompositorResourceTracker::runSubmitCallback() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + std::move(m_submitCallback).Run(); +} + +} // namespace QtWebEngineCore diff --git a/src/core/compositor/compositor_resource_tracker.h b/src/core/compositor/compositor_resource_tracker.h new file mode 100644 index 000000000..887309395 --- /dev/null +++ b/src/core/compositor/compositor_resource_tracker.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef COMPOSITOR_RESOURCE_TRACKER_H +#define COMPOSITOR_RESOURCE_TRACKER_H + +#include "compositor_resource.h" +#include "locked_ptr.h" + +#include <base/callback.h> +#include <base/containers/flat_set.h> + +#include <atomic> +#include <vector> + +namespace viz { +class CompositorFrame; +struct ReturnedResource; +} // namespace viz + +namespace gpu { +struct MailboxHolder; +} // namespace gpu + +namespace QtWebEngineCore { + +// Ensures resources are not used before they are ready. +// +// The life cycle of a frame's resources: +// +// Step 1. A new CompositorFrame is received and given to submitResources(). +// The frame's resources will extracted and initialized to a pending state. +// +// Step 2. Once the new resources are ready to be committed, +// CompositorResourceTracker will notify the client by running the callback +// given to submitResources(). +// +// Step 3. Once the client is ready to render, commitResources() should be +// called. This will commit all the pending resources, making them available +// via findResource(). +// +// Step 4. Once all the resources have been used (via findResource()), +// returnResources() may be called to return a list of all the resources which +// were *not* used since the last commitResources(). Go to step 1. +class CompositorResourceTracker final +{ +public: + CompositorResourceTracker(); + ~CompositorResourceTracker(); + + void submitResources(const viz::CompositorFrame &frame, base::OnceClosure callback); + void commitResources(); + std::vector<viz::ReturnedResource> returnResources(); + + // The returned pointer is invalidated by the next call to commitFrame() or + // returnResources(). It should therefore not be stored in data structures + // but used immediately. + // + // Do not ask for resources which do not exist. + const CompositorResource *findResource(CompositorResourceId id) const; + +private: + void updateBitmap(CompositorResource *resource); + + quint32 consumeMailbox(const gpu::MailboxHolder &mailboxHolder); + + bool scheduleUpdateMailbox(CompositorResource *resource); + void updateMailbox(CompositorResource *resource); + + void scheduleUpdateMailboxes(std::vector<CompositorResource *> resources); + void updateMailboxes(std::vector<CompositorResource *> resources); + + void scheduleRunSubmitCallback(); + void runSubmitCallback(); + + base::flat_set<CompositorResource> m_committedResources; + std::vector<CompositorResource> m_pendingResources; + std::vector<CompositorResource *> m_pendingImports; + base::OnceClosure m_submitCallback; + std::atomic<size_t> m_pendingResourceUpdates{0}; + quint32 m_committedFrameId = 0; + + base::LockedPtrFactory<CompositorResourceTracker> m_weakPtrFactory{this}; + + DISALLOW_COPY_AND_ASSIGN(CompositorResourceTracker); +}; + +} // namespace QtWebEngineCore + +#endif // !COMPOSITOR_RESOURCE_TRACKER_H diff --git a/src/core/compositor/content_gpu_client_qt.cpp b/src/core/compositor/content_gpu_client_qt.cpp new file mode 100644 index 000000000..f934979a0 --- /dev/null +++ b/src/core/compositor/content_gpu_client_qt.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** 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 "content_gpu_client_qt.h" + +#include "web_engine_context.h" + +namespace QtWebEngineCore { + +ContentGpuClientQt::ContentGpuClientQt() +{ +} + +ContentGpuClientQt::~ContentGpuClientQt() +{ +} + +gpu::SyncPointManager *ContentGpuClientQt::GetSyncPointManager() +{ + return WebEngineContext::syncPointManager(); +} + +} // namespace diff --git a/src/core/compositor/content_gpu_client_qt.h b/src/core/compositor/content_gpu_client_qt.h new file mode 100644 index 000000000..d7ad43881 --- /dev/null +++ b/src/core/compositor/content_gpu_client_qt.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef CONTENT_GPU_CLIENT_QT_H +#define CONTENT_GPU_CLIENT_QT_H + +#include "content/public/gpu/content_gpu_client.h" + +namespace QtWebEngineCore { + +class ContentGpuClientQt : public content::ContentGpuClient { +public: + explicit ContentGpuClientQt(); + ~ContentGpuClientQt() override; + + // content::ContentGpuClient implementation. + gpu::SyncPointManager *GetSyncPointManager() override; +}; + +} + +#endif // CONTENT_GPU_CLIENT_QT_H diff --git a/src/core/delegated_frame_node.cpp b/src/core/compositor/delegated_frame_node.cpp index 8ac82dbf1..e2b6714a1 100644 --- a/src/core/delegated_frame_node.cpp +++ b/src/core/compositor/delegated_frame_node.cpp @@ -49,17 +49,14 @@ #include "delegated_frame_node.h" #include "chromium_gpu_helper.h" -#include "ozone/gl_surface_qt.h" #include "stream_video_node.h" #include "type_conversion.h" #include "yuv_video_node.h" +#include "compositor_resource_tracker.h" #include "base/bind.h" -#include "base/message_loop/message_loop.h" -#include "base/threading/thread_task_runner_handle.h" #include "cc/base/math_util.h" #include "components/viz/common/quads/compositor_frame.h" -#include "components/viz/common/quads/compositor_frame_metadata.h" #include "components/viz/common/quads/debug_border_draw_quad.h" #include "components/viz/common/quads/draw_quad.h" #include "components/viz/common/quads/render_pass_draw_quad.h" @@ -68,14 +65,8 @@ #include "components/viz/common/quads/texture_draw_quad.h" #include "components/viz/common/quads/tile_draw_quad.h" #include "components/viz/common/quads/yuv_video_draw_quad.h" -#include "components/viz/common/resources/returned_resource.h" -#include "components/viz/common/resources/transferable_resource.h" #include "components/viz/service/display/bsp_tree.h" #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h" -#include "content/browser/browser_main_loop.h" -#include "gpu/command_buffer/service/mailbox_manager.h" -#include "ui/gl/gl_context.h" -#include "ui/gl/gl_fence.h" #ifndef QT_NO_OPENGL # include <QOpenGLContext> @@ -93,10 +84,6 @@ #include <EGL/eglext.h> #endif -#ifndef GL_TIMEOUT_IGNORED -#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull -#endif - #ifndef GL_TEXTURE_RECTANGLE #define GL_TEXTURE_RECTANGLE 0x84F5 #endif @@ -131,7 +118,7 @@ namespace QtWebEngineCore { #ifndef QT_NO_OPENGL class MailboxTexture : public QSGTexture, protected QOpenGLFunctions { public: - MailboxTexture(const gpu::MailboxHolder &mailboxHolder, const QSize textureSize); + MailboxTexture(const CompositorResource *resource, bool hasAlphaChannel, int target = -1); ~MailboxTexture(); // QSGTexture: int textureId() const override { return m_textureId; } @@ -140,14 +127,9 @@ public: bool hasMipmaps() const override { return false; } void bind() override; - void setHasAlphaChannel(bool hasAlpha) { m_hasAlpha = hasAlpha; } - gpu::MailboxHolder &mailboxHolder() { return m_mailboxHolder; } - void fetchTexture(gpu::MailboxManager *mailboxManager); - void setTarget(GLenum target); - private: - gpu::MailboxHolder m_mailboxHolder; int m_textureId; + scoped_refptr<CompositorResourceFence> m_fence; QSize m_textureSize; bool m_hasAlpha; GLenum m_target; @@ -160,20 +142,6 @@ private: friend class DelegatedFrameNode; }; #endif // QT_NO_OPENGL -class ResourceHolder { -public: - ResourceHolder(const viz::TransferableResource &resource); - QSharedPointer<QSGTexture> initTexture(bool quadIsAllOpaque, RenderWidgetHostViewQtDelegate *apiDelegate = 0); - QSGTexture *texture() const { return m_texture.data(); } - viz::ReturnedResource returnResource(); - void incImportCount() { ++m_importCount; } - bool needsToFetch() const { return !m_resource.is_software && m_texture && !m_texture.data()->textureId(); } - -private: - QWeakPointer<QSGTexture> m_texture; - viz::TransferableResource m_resource; - int m_importCount; -}; class RectClipNode : public QSGClipNode { @@ -443,7 +411,9 @@ static QSGNode *buildLayerChain(QSGNode *chainParent, const viz::SharedQuadState } if (!layerState->quad_to_target_transform.IsIdentity()) { QSGTransformNode *transformNode = new QSGTransformNode; - transformNode->setMatrix(toQt(layerState->quad_to_target_transform.matrix())); + QMatrix4x4 qMatrix; + convertToQt(layerState->quad_to_target_transform.matrix(), qMatrix); + transformNode->setMatrix(qMatrix); layerChain->appendChildNode(transformNode); layerChain = transformNode; } @@ -457,99 +427,12 @@ static QSGNode *buildLayerChain(QSGNode *chainParent, const viz::SharedQuadState } #ifndef QT_NO_OPENGL -static void waitChromiumSync(gl::TransferableFence *sync) -{ - // Chromium uses its own GL bindings and stores in in thread local storage. - // For that reason, let chromium_gpu_helper.cpp contain the producing code that will run in the Chromium - // GPU thread, and put the sync consuming code here that will run in the QtQuick SG or GUI thread. - switch (sync->type) { - case gl::TransferableFence::NoSync: - break; - case gl::TransferableFence::EglSync: -#ifdef EGL_KHR_reusable_sync - { - static bool resolved = false; - static PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR = 0; - - if (!resolved) { - if (gl::GLSurfaceQt::HasEGLExtension("EGL_KHR_fence_sync")) { - QOpenGLContext *context = QOpenGLContext::currentContext(); - eglClientWaitSyncKHR = (PFNEGLCLIENTWAITSYNCKHRPROC)context->getProcAddress("eglClientWaitSyncKHR"); - } - resolved = true; - } - - if (eglClientWaitSyncKHR) - // FIXME: Use the less wasteful eglWaitSyncKHR once we have a device that supports EGL_KHR_wait_sync. - eglClientWaitSyncKHR(sync->egl.display, sync->egl.sync, 0, EGL_FOREVER_KHR); - } -#endif - break; - case gl::TransferableFence::ArbSync: - typedef void (QOPENGLF_APIENTRYP WaitSyncPtr)(GLsync sync, GLbitfield flags, GLuint64 timeout); - static WaitSyncPtr glWaitSync_ = 0; - if (!glWaitSync_) { - QOpenGLContext *context = QOpenGLContext::currentContext(); - glWaitSync_ = (WaitSyncPtr)context->getProcAddress("glWaitSync"); - Q_ASSERT(glWaitSync_); - } - glWaitSync_(sync->arb.sync, 0, GL_TIMEOUT_IGNORED); - break; - } -} - -static void deleteChromiumSync(gl::TransferableFence *sync) -{ - // Chromium uses its own GL bindings and stores in in thread local storage. - // For that reason, let chromium_gpu_helper.cpp contain the producing code that will run in the Chromium - // GPU thread, and put the sync consuming code here that will run in the QtQuick SG or GUI thread. - switch (sync->type) { - case gl::TransferableFence::NoSync: - break; - case gl::TransferableFence::EglSync: -#ifdef EGL_KHR_reusable_sync - { - static bool resolved = false; - static PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR = 0; - - if (!resolved) { - if (gl::GLSurfaceQt::HasEGLExtension("EGL_KHR_fence_sync")) { - QOpenGLContext *context = QOpenGLContext::currentContext(); - eglDestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC)context->getProcAddress("eglDestroySyncKHR"); - } - resolved = true; - } - - if (eglDestroySyncKHR) { - // FIXME: Use the less wasteful eglWaitSyncKHR once we have a device that supports EGL_KHR_wait_sync. - eglDestroySyncKHR(sync->egl.display, sync->egl.sync); - sync->reset(); - } - } -#endif - break; - case gl::TransferableFence::ArbSync: - typedef void (QOPENGLF_APIENTRYP DeleteSyncPtr)(GLsync sync); - static DeleteSyncPtr glDeleteSync_ = 0; - if (!glDeleteSync_) { - QOpenGLContext *context = QOpenGLContext::currentContext(); - glDeleteSync_ = (DeleteSyncPtr)context->getProcAddress("glDeleteSync"); - Q_ASSERT(glDeleteSync_); - } - glDeleteSync_(sync->arb.sync); - sync->reset(); - break; - } - // If Chromium was able to create a sync, we should have been able to handle its type here too. - Q_ASSERT(!*sync); -} - -MailboxTexture::MailboxTexture(const gpu::MailboxHolder &mailboxHolder, const QSize textureSize) - : m_mailboxHolder(mailboxHolder) - , m_textureId(0) - , m_textureSize(textureSize) - , m_hasAlpha(false) - , m_target(GL_TEXTURE_2D) +MailboxTexture::MailboxTexture(const CompositorResource *resource, bool hasAlphaChannel, int target) + : m_textureId(resource->texture_id) + , m_fence(resource->texture_fence) + , m_textureSize(toQt(resource->size)) + , m_hasAlpha(hasAlphaChannel) + , m_target(target >= 0 ? target : GL_TEXTURE_2D) #if defined(USE_OZONE) , m_ownsTexture(false) #endif @@ -580,6 +463,8 @@ MailboxTexture::~MailboxTexture() void MailboxTexture::bind() { + if (m_fence) + m_fence->wait(); glBindTexture(m_target, m_textureId); #ifdef Q_OS_QNX if (m_target == GL_TEXTURE_EXTERNAL_OES) { @@ -596,96 +481,7 @@ void MailboxTexture::bind() } #endif } - -void MailboxTexture::setTarget(GLenum target) -{ - m_target = target; -} - -void MailboxTexture::fetchTexture(gpu::MailboxManager *mailboxManager) -{ - gpu::TextureBase *tex = ConsumeTexture(mailboxManager, m_target, m_mailboxHolder.mailbox); - - // The texture might already have been deleted (e.g. when navigating away from a page). - if (tex) { - m_textureId = service_id(tex); -#ifdef Q_OS_QNX - if (m_target == GL_TEXTURE_EXTERNAL_OES) { - m_eglStreamData = eglstream_connect_consumer(tex); - } -#endif - } -} -#endif //QT_NO_OPENGL - -ResourceHolder::ResourceHolder(const viz::TransferableResource &resource) - : m_resource(resource) - , m_importCount(1) -{ -} - -QSharedPointer<QSGTexture> ResourceHolder::initTexture(bool quadNeedsBlending, RenderWidgetHostViewQtDelegate *apiDelegate) -{ - QSharedPointer<QSGTexture> texture = m_texture.toStrongRef(); - if (!texture) { - if (m_resource.is_software) { - Q_ASSERT(apiDelegate); - std::unique_ptr<viz::SharedBitmap> sharedBitmap = - content::BrowserMainLoop::GetInstance()->GetServerSharedBitmapManager()->GetSharedBitmapFromId( - m_resource.size, viz::BGRA_8888, m_resource.mailbox_holder.mailbox); - // QSG interprets QImage::hasAlphaChannel meaning that a node should enable blending - // to draw it but Chromium keeps this information in the quads. - // The input format is currently always Format_ARGB32_Premultiplied, so assume that all - // alpha bytes are 0xff if quads aren't requesting blending and avoid the conversion - // from Format_ARGB32_Premultiplied to Format_RGB32 just to get hasAlphaChannel to - // return false. - QImage::Format format = quadNeedsBlending ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32; - QImage image = sharedBitmap - ? QImage(sharedBitmap->pixels(), m_resource.size.width(), m_resource.size.height(), format) - : QImage(m_resource.size.width(), m_resource.size.height(), format); - texture.reset(apiDelegate->createTextureFromImage(image.copy())); - } else { -#ifndef QT_NO_OPENGL - texture.reset(new MailboxTexture(m_resource.mailbox_holder, toQt(m_resource.size))); - static_cast<MailboxTexture *>(texture.data())->setHasAlphaChannel(quadNeedsBlending); -#else - Q_UNREACHABLE(); -#endif - } - if (m_resource.filter == GL_NEAREST) - texture->setFiltering(QSGTexture::Nearest); - else if (m_resource.filter == GL_LINEAR) - texture->setFiltering(QSGTexture::Linear); - else { - // Depends on qtdeclarative fix, see QTBUG-71322 -#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 1) - texture->setFiltering(QSGTexture::Linear); -#else - texture->setFiltering(QSGTexture::Nearest); -#endif - } - m_texture = texture; - } - // All quads using a resource should request the same blending state. - Q_ASSERT(texture->hasAlphaChannel() || !quadNeedsBlending); - return texture; -} - -viz::ReturnedResource ResourceHolder::returnResource() -{ - viz::ReturnedResource returned; - // The ResourceProvider ensures that the resource isn't used by the parent compositor's GL - // context in the GPU process by inserting a sync point to be waited for by the child - // compositor's GL context. We don't need this since we are triggering the delegated frame - // ack directly from our rendering thread. At this point (in updatePaintNode) we know that - // a frame that was compositing any of those resources has already been swapped and we thus - // don't need to use this mechanism. - returned.id = m_resource.id; - returned.count = m_importCount; - m_importCount = 0; - return returned; -} - +#endif // !QT_NO_OPENGL RectClipNode::RectClipNode(const QRectF &rect) : m_geometry(QSGGeometry::defaultAttributes_Point2D(), 4) @@ -697,9 +493,8 @@ RectClipNode::RectClipNode(const QRectF &rect) } DelegatedFrameNode::DelegatedFrameNode() - : m_numPendingSyncPoints(0) #if defined(USE_OZONE) && !defined(QT_NO_OPENGL) - , m_contextShared(true) + : m_contextShared(true) #endif { setFlag(UsePreprocess); @@ -725,22 +520,6 @@ DelegatedFrameNode::~DelegatedFrameNode() void DelegatedFrameNode::preprocess() { -#ifndef QT_NO_OPENGL - // With the threaded render loop the GUI thread has been unlocked at this point. - // We can now wait for the Chromium GPU thread to produce textures that will be - // rendered on our quads and fetch the IDs from the mailboxes we were given. - QList<MailboxTexture *> mailboxesToFetch; - typedef QHash<unsigned, QSharedPointer<ResourceHolder> >::const_iterator ResourceHolderIterator; - ResourceHolderIterator end = m_chromiumCompositorData->resourceHolders.constEnd(); - for (ResourceHolderIterator it = m_chromiumCompositorData->resourceHolders.constBegin(); it != end ; ++it) { - if ((*it)->needsToFetch()) - mailboxesToFetch.append(static_cast<MailboxTexture *>((*it)->texture())); - } - - if (!mailboxesToFetch.isEmpty()) - fetchAndSyncMailboxes(mailboxesToFetch); -#endif - // Then render any intermediate RenderPass in order. typedef QPair<int, QSharedPointer<QSGLayer> > Pair; for (const Pair &pair : qAsConst(m_sgObjects.renderPassLayers)) { @@ -767,8 +546,8 @@ static bool areSharedQuadStatesEqual(const viz::SharedQuadState *layerState, // Compares if the frame data that we got from the Chromium Compositor is // *structurally* equivalent to the one of the previous frame. // If it is, we will just reuse and update the old nodes where necessary. -static bool areRenderPassStructuresEqual(viz::CompositorFrame *frameData, - viz::CompositorFrame *previousFrameData) +static bool areRenderPassStructuresEqual(const viz::CompositorFrame *frameData, + const viz::CompositorFrame *previousFrameData) { if (!previousFrameData) return false; @@ -820,12 +599,12 @@ static bool areRenderPassStructuresEqual(viz::CompositorFrame *frameData, return true; } -void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, - std::vector<viz::ReturnedResource> *resourcesToRelease, +void DelegatedFrameNode::commit(const viz::CompositorFrame &pendingFrame, + const viz::CompositorFrame &committedFrame, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate) { - m_chromiumCompositorData = chromiumCompositorData; - viz::CompositorFrame* frameData = &m_chromiumCompositorData->frameData; + const viz::CompositorFrame* frameData = &pendingFrame; if (frameData->render_pass_list.empty()) return; @@ -833,27 +612,11 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, // countering the scale of devicePixel-scaled tiles when rendering them // to the final surface. QMatrix4x4 matrix; - const float devicePixelRatio = m_chromiumCompositorData->frameDevicePixelRatio; + const float devicePixelRatio = frameData->metadata.device_scale_factor; matrix.scale(1 / devicePixelRatio, 1 / devicePixelRatio); if (QSGTransformNode::matrix() != matrix) setMatrix(matrix); - QHash<unsigned, QSharedPointer<ResourceHolder> > resourceCandidates; - qSwap(m_chromiumCompositorData->resourceHolders, resourceCandidates); - - // A frame's resource_list only contains the new resources to be added to the scene. Quads can - // still reference resources that were added in previous frames. Add them to the list of - // candidates to be picked up by quads, it's then our responsibility to return unused resources - // to the producing child compositor. - for (unsigned i = 0; i < frameData->resource_list.size(); ++i) { - const viz::TransferableResource &res = frameData->resource_list.at(i); - if (QSharedPointer<ResourceHolder> resource = resourceCandidates.value(res.id)) - resource->incImportCount(); - else - resourceCandidates[res.id] = QSharedPointer<ResourceHolder>(new ResourceHolder(res)); - } - - frameData->resource_list.clear(); QScopedPointer<DelegatedNodeTreeHandler> nodeHandler; const QSizeF viewportSizeInPt = apiDelegate->screenRect().size(); @@ -867,26 +630,22 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, // Additionally, because we clip (i.e. don't build scene graph nodes for) quads outside // of the visible area, we also have to rebuild the tree whenever the window is resized. const bool buildNewTree = - !areRenderPassStructuresEqual(frameData, &m_chromiumCompositorData->previousFrameData) || + !areRenderPassStructuresEqual(frameData, &committedFrame) || m_sceneGraphNodes.empty() || viewportSize != m_previousViewportSize; - m_chromiumCompositorData->previousFrameData = viz::CompositorFrame(); - SGObjects previousSGObjects; - QVector<QSharedPointer<QSGTexture> > textureStrongRefs; if (buildNewTree) { // Keep the old objects in scope to hold a ref on layers, resources and textures // that we can re-use. Destroy the remaining objects before returning. - qSwap(m_sgObjects, previousSGObjects); + qSwap(m_sgObjects, m_previousSGObjects); // Discard the scene graph nodes from the previous frame. while (QSGNode *oldChain = firstChild()) delete oldChain; m_sceneGraphNodes.clear(); nodeHandler.reset(new DelegatedNodeTreeCreator(&m_sceneGraphNodes, apiDelegate)); } else { - // Save the texture strong refs so they only go out of scope when the method returns and - // the new vector of texture strong refs has been filled. - qSwap(m_sgObjects.textureStrongRefs, textureStrongRefs); + qSwap(m_sgObjects.bitmapTextures, m_previousSGObjects.bitmapTextures); + qSwap(m_sgObjects.mailboxTextures, m_previousSGObjects.mailboxTextures); nodeHandler.reset(new DelegatedNodeTreeUpdater(&m_sceneGraphNodes)); } // The RenderPasses list is actually a tree where a parent RenderPass is connected @@ -906,7 +665,7 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, if (pass != rootRenderPass) { QSharedPointer<QSGLayer> rpLayer; if (buildNewTree) { - rpLayer = findRenderPassLayer(pass->id, previousSGObjects.renderPassLayers); + rpLayer = findRenderPassLayer(pass->id, m_previousSGObjects.renderPassLayers); if (!rpLayer) { rpLayer = QSharedPointer<QSGLayer>(apiDelegate->createLayer()); // Avoid any premature texture update since we need to wait @@ -935,7 +694,7 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, } if (scissorRect.IsEmpty()) { - holdResources(pass, resourceCandidates); + holdResources(pass, resourceTracker); continue; } @@ -961,13 +720,13 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, targetRect.Intersect(quadState->clip_rect); targetRect.Intersect(scissorRect); if (targetRect.IsEmpty()) { - holdResources(quad, resourceCandidates); + holdResources(quad, resourceTracker); continue; } if (quadState->sorting_context_id != currentSortingContextId) { flushPolygons(&polygonQueue, renderPassChain, - nodeHandler.data(), resourceCandidates, apiDelegate); + nodeHandler.data(), resourceTracker, apiDelegate); currentSortingContextId = quadState->sorting_context_id; } @@ -989,28 +748,23 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, } handleQuad(quad, currentLayerChain, - nodeHandler.data(), resourceCandidates, apiDelegate); + nodeHandler.data(), resourceTracker, apiDelegate); } flushPolygons(&polygonQueue, renderPassChain, - nodeHandler.data(), resourceCandidates, apiDelegate); + nodeHandler.data(), resourceTracker, apiDelegate); } - // Send resources of remaining candidates back to the child compositors so that - // they can be freed or reused. - typedef QHash<unsigned, QSharedPointer<ResourceHolder> >::const_iterator - ResourceHolderIterator; - ResourceHolderIterator end = resourceCandidates.constEnd(); - for (ResourceHolderIterator it = resourceCandidates.constBegin(); it != end ; ++it) - resourcesToRelease->push_back((*it)->returnResource()); + copyMailboxTextures(); m_previousViewportSize = viewportSize; + m_previousSGObjects = SGObjects(); } void DelegatedFrameNode::flushPolygons( base::circular_deque<std::unique_ptr<viz::DrawPolygon>> *polygonQueue, QSGNode *renderPassChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate) { if (polygonQueue->empty()) @@ -1030,7 +784,7 @@ void DelegatedFrameNode::flushPolygons( polygon->TransformToLayerSpace(inverseTransform); handlePolygon(polygon, currentLayerChain, - nodeHandler, resourceCandidates, apiDelegate); + nodeHandler, resourceTracker, apiDelegate); }; viz::BspTree(polygonQueue).TraverseWithActionHandler(&actionHandler); @@ -1040,20 +794,20 @@ void DelegatedFrameNode::handlePolygon( const viz::DrawPolygon *polygon, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate) { const viz::DrawQuad *quad = polygon->original_ref(); if (!polygon->is_split()) { handleQuad(quad, currentLayerChain, - nodeHandler, resourceCandidates, apiDelegate); + nodeHandler, resourceTracker, apiDelegate); } else { std::vector<gfx::QuadF> clipRegionList; polygon->ToQuads2D(&clipRegionList); for (const auto & clipRegion : clipRegionList) handleClippedQuad(quad, clipRegion, currentLayerChain, - nodeHandler, resourceCandidates, apiDelegate); + nodeHandler, resourceTracker, apiDelegate); } } @@ -1062,7 +816,7 @@ void DelegatedFrameNode::handleClippedQuad( const gfx::QuadF &clipRegion, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate) { if (currentLayerChain) { @@ -1080,21 +834,21 @@ void DelegatedFrameNode::handleClippedQuad( currentLayerChain = clipNode; } handleQuad(quad, currentLayerChain, - nodeHandler, resourceCandidates, apiDelegate); + nodeHandler, resourceTracker, apiDelegate); } void DelegatedFrameNode::handleQuad( const viz::DrawQuad *quad, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate) { switch (quad->material) { case viz::DrawQuad::RENDER_PASS: { const viz::RenderPassDrawQuad *renderPassQuad = viz::RenderPassDrawQuad::MaterialCast(quad); if (!renderPassQuad->mask_texture_size.IsEmpty()) { - ResourceHolder *resource = findAndHoldResource(renderPassQuad->mask_resource_id(), resourceCandidates); + const CompositorResource *resource = findAndHoldResource(renderPassQuad->mask_resource_id(), resourceTracker); Q_UNUSED(resource); // FIXME: QTBUG-67652 } QSGLayer *layer = @@ -1107,7 +861,7 @@ void DelegatedFrameNode::handleQuad( } case viz::DrawQuad::TEXTURE_CONTENT: { const viz::TextureDrawQuad *tquad = viz::TextureDrawQuad::MaterialCast(quad); - ResourceHolder *resource = findAndHoldResource(tquad->resource_id(), resourceCandidates); + const CompositorResource *resource = findAndHoldResource(tquad->resource_id(), resourceTracker); QSGTexture *texture = initAndHoldTexture(resource, quad->ShouldDrawWithBlending(), apiDelegate); QSizeF textureSize; @@ -1158,7 +912,7 @@ void DelegatedFrameNode::handleQuad( } case viz::DrawQuad::TILED_CONTENT: { const viz::TileDrawQuad *tquad = viz::TileDrawQuad::MaterialCast(quad); - ResourceHolder *resource = findAndHoldResource(tquad->resource_id(), resourceCandidates); + const CompositorResource *resource = findAndHoldResource(tquad->resource_id(), resourceTracker); nodeHandler->setupTextureContentNode( initAndHoldTexture(resource, quad->ShouldDrawWithBlending(), apiDelegate), toQt(quad->rect), toQt(tquad->tex_coord_rect), @@ -1168,17 +922,17 @@ void DelegatedFrameNode::handleQuad( } case viz::DrawQuad::YUV_VIDEO_CONTENT: { const viz::YUVVideoDrawQuad *vquad = viz::YUVVideoDrawQuad::MaterialCast(quad); - ResourceHolder *yResource = - findAndHoldResource(vquad->y_plane_resource_id(), resourceCandidates); - ResourceHolder *uResource = - findAndHoldResource(vquad->u_plane_resource_id(), resourceCandidates); - ResourceHolder *vResource = - findAndHoldResource(vquad->v_plane_resource_id(), resourceCandidates); - ResourceHolder *aResource = 0; + const CompositorResource *yResource = + findAndHoldResource(vquad->y_plane_resource_id(), resourceTracker); + const CompositorResource *uResource = + findAndHoldResource(vquad->u_plane_resource_id(), resourceTracker); + const CompositorResource *vResource = + findAndHoldResource(vquad->v_plane_resource_id(), resourceTracker); + const CompositorResource *aResource = nullptr; // This currently requires --enable-vp8-alpha-playback and // needs a video with alpha data to be triggered. if (vquad->a_plane_resource_id()) - aResource = findAndHoldResource(vquad->a_plane_resource_id(), resourceCandidates); + aResource = findAndHoldResource(vquad->a_plane_resource_id(), resourceTracker); nodeHandler->setupYUVVideoNode( initAndHoldTexture(yResource, quad->ShouldDrawWithBlending()), @@ -1194,14 +948,13 @@ void DelegatedFrameNode::handleQuad( } case viz::DrawQuad::STREAM_VIDEO_CONTENT: { const viz::StreamVideoDrawQuad *squad = viz::StreamVideoDrawQuad::MaterialCast(quad); - ResourceHolder *resource = findAndHoldResource(squad->resource_id(), resourceCandidates); + const CompositorResource *resource = findAndHoldResource(squad->resource_id(), resourceTracker); MailboxTexture *texture = static_cast<MailboxTexture *>( - initAndHoldTexture(resource, quad->ShouldDrawWithBlending())); - // since this is not default TEXTURE_2D type - texture->setTarget(GL_TEXTURE_EXTERNAL_OES); + initAndHoldTexture(resource, quad->ShouldDrawWithBlending(), apiDelegate, GL_TEXTURE_EXTERNAL_OES)); - nodeHandler->setupStreamVideoNode(texture, toQt(squad->rect), toQt(squad->matrix.matrix()), - currentLayerChain); + QMatrix4x4 qMatrix; + convertToQt(squad->matrix.matrix(), qMatrix); + nodeHandler->setupStreamVideoNode(texture, toQt(squad->rect), qMatrix, currentLayerChain); break; #endif // GL_OES_EGL_image_external #endif // QT_NO_OPENGL @@ -1213,75 +966,100 @@ void DelegatedFrameNode::handleQuad( } } -ResourceHolder *DelegatedFrameNode::findAndHoldResource(unsigned resourceId, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates) +const CompositorResource *DelegatedFrameNode::findAndHoldResource(unsigned resourceId, const CompositorResourceTracker *resourceTracker) { - // ResourceHolders must survive when the scene graph destroys our node branch - QSharedPointer<ResourceHolder> &resource = m_chromiumCompositorData->resourceHolders[resourceId]; - if (!resource) - resource = candidates.take(resourceId); - Q_ASSERT(resource); - return resource.data(); + return resourceTracker->findResource(resourceId); } -void DelegatedFrameNode::holdResources(const viz::DrawQuad *quad, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates) +void DelegatedFrameNode::holdResources(const viz::DrawQuad *quad, const CompositorResourceTracker *resourceTracker) { for (auto resource : quad->resources) - findAndHoldResource(resource, candidates); + findAndHoldResource(resource, resourceTracker); } -void DelegatedFrameNode::holdResources(const viz::RenderPass *pass, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates) +void DelegatedFrameNode::holdResources(const viz::RenderPass *pass, const CompositorResourceTracker *resourceTracker) { for (const auto &quad : pass->quad_list) - holdResources(quad, candidates); + holdResources(quad, resourceTracker); } -QSGTexture *DelegatedFrameNode::initAndHoldTexture(ResourceHolder *resource, bool quadIsAllOpaque, RenderWidgetHostViewQtDelegate *apiDelegate) +template<class Container, class Key> +inline auto &findTexture(Container &map, Container &previousMap, const Key &key) { - // QSGTextures must be destroyed in the scene graph thread as part of the QSGNode tree, - // so we can't store them with the ResourceHolder in m_chromiumCompositorData. - // Hold them through a QSharedPointer solely on the root DelegatedFrameNode of the web view - // and access them through a QWeakPointer from the resource holder to find them later. - m_sgObjects.textureStrongRefs.append(resource->initTexture(quadIsAllOpaque, apiDelegate)); - return m_sgObjects.textureStrongRefs.last().data(); + auto &value = map[key]; + if (value) + return value; + value = previousMap[key]; + return value; } -void DelegatedFrameNode::fetchAndSyncMailboxes(QList<MailboxTexture *> &mailboxesToFetch) +QSGTexture *DelegatedFrameNode::initAndHoldTexture(const CompositorResource *resource, bool hasAlphaChannel, RenderWidgetHostViewQtDelegate *apiDelegate, int target) { -#ifndef QT_NO_OPENGL - QList<gl::TransferableFence> transferredFences; - { - QMutexLocker lock(&m_mutex); - QVector<MailboxTexture *> mailboxesToPull; - mailboxesToPull.reserve(mailboxesToFetch.size()); - - gpu::SyncPointManager *syncPointManager = sync_point_manager(); - scoped_refptr<base::SingleThreadTaskRunner> gpuTaskRunner = gpu_task_runner(); - Q_ASSERT(m_numPendingSyncPoints == 0); - m_numPendingSyncPoints = mailboxesToFetch.count(); - for (MailboxTexture *mailboxTexture : qAsConst(mailboxesToFetch)) { - gpu::SyncToken &syncToken = mailboxTexture->mailboxHolder().sync_token; - const auto task = base::Bind(&DelegatedFrameNode::pullTexture, this, mailboxTexture); - if (!syncPointManager->WaitOutOfOrder(syncToken, std::move(task))) - mailboxesToPull.append(mailboxTexture); - } - if (!mailboxesToPull.isEmpty()) { - auto task = base::BindOnce(&DelegatedFrameNode::pullTextures, this, std::move(mailboxesToPull)); - gpuTaskRunner->PostTask(FROM_HERE, std::move(task)); - } - - m_mailboxesFetchedWaitCond.wait(&m_mutex); - m_textureFences.swap(transferredFences); + QSGTexture::Filtering filtering; + + if (resource->filter == GL_NEAREST) + filtering = QSGTexture::Nearest; + else if (resource->filter == GL_LINEAR) + filtering = QSGTexture::Linear; + else { + // Depends on qtdeclarative fix, see QTBUG-71322 +#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 1) + filtering = QSGTexture::Linear; +#else + filtering = QSGTexture::Nearest; +#endif } - for (gl::TransferableFence sync : qAsConst(transferredFences)) { - // We need to wait on the fences on the Qt current context, and - // can therefore not use GLFence routines that uses a different - // concept of current context. - waitChromiumSync(&sync); - deleteChromiumSync(&sync); + if (resource->is_software) { + QSharedPointer<QSGTexture> &texture = + findTexture(m_sgObjects.bitmapTextures, m_previousSGObjects.bitmapTextures, resource->id); + if (texture) + return texture.data(); + texture = createBitmapTexture(resource, hasAlphaChannel, apiDelegate); + texture->setFiltering(filtering); + return texture.data(); + } else { + QSharedPointer<MailboxTexture> &texture = + findTexture(m_sgObjects.mailboxTextures, m_previousSGObjects.mailboxTextures, resource->id); + if (texture) + return texture.data(); + texture = createMailboxTexture(resource, hasAlphaChannel, target); + texture->setFiltering(filtering); + return texture.data(); } +} -#if defined(USE_OZONE) && !defined(QT_NO_OPENGL) +QSharedPointer<QSGTexture> DelegatedFrameNode::createBitmapTexture(const CompositorResource *resource, bool hasAlphaChannel, RenderWidgetHostViewQtDelegate *apiDelegate) +{ + Q_ASSERT(apiDelegate); + viz::SharedBitmap *sharedBitmap = resource->bitmap.get(); + gfx::Size size = resource->size; + + // QSG interprets QImage::hasAlphaChannel meaning that a node should enable blending + // to draw it but Chromium keeps this information in the quads. + // The input format is currently always Format_ARGB32_Premultiplied, so assume that all + // alpha bytes are 0xff if quads aren't requesting blending and avoid the conversion + // from Format_ARGB32_Premultiplied to Format_RGB32 just to get hasAlphaChannel to + // return false. + QImage::Format format = hasAlphaChannel ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32; + QImage image = sharedBitmap + ? QImage(sharedBitmap->pixels(), size.width(), size.height(), format) + : QImage(size.width(), size.height(), format); + return QSharedPointer<QSGTexture>(apiDelegate->createTextureFromImage(image.copy())); +} + +QSharedPointer<MailboxTexture> DelegatedFrameNode::createMailboxTexture(const CompositorResource *resource, bool hasAlphaChannel, int target) +{ +#ifndef QT_NO_OPENGL + return QSharedPointer<MailboxTexture>::create(resource, hasAlphaChannel, target); +#else + Q_UNREACHABLE(); +#endif +} + +void DelegatedFrameNode::copyMailboxTextures() +{ +#if !defined(QT_NO_OPENGL) && defined(USE_OZONE) // Workaround when context is not shared QTBUG-48969 // Make slow copy between two contexts. if (!m_contextShared) { @@ -1296,13 +1074,17 @@ void DelegatedFrameNode::fetchAndSyncMailboxes(QList<MailboxTexture *> &mailboxe GLuint fbo = 0; funcs->glGenFramebuffers(1, &fbo); - for (MailboxTexture *mailboxTexture : qAsConst(mailboxesToFetch)) { + for (const QSharedPointer<MailboxTexture> &mailboxTexture : qAsConst(m_sgObjects.mailboxTextures)) { + if (mailboxTexture->m_ownsTexture) + continue; + // Read texture into QImage from shared context. // Switch to shared context. sharedContext->makeCurrent(m_offsurface.data()); funcs = sharedContext->functions(); QImage img(mailboxTexture->textureSize(), QImage::Format_RGBA8888_Premultiplied); funcs->glBindFramebuffer(GL_FRAMEBUFFER, fbo); + mailboxTexture->m_fence->wait(); funcs->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mailboxTexture->m_textureId, 0); GLenum status = funcs->glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { @@ -1336,69 +1118,6 @@ void DelegatedFrameNode::fetchAndSyncMailboxes(QList<MailboxTexture *> &mailboxe currentContext->makeCurrent(surface); } #endif -#else - Q_UNUSED(mailboxesToFetch) -#endif //QT_NO_OPENGL -} - - -void DelegatedFrameNode::pullTextures(DelegatedFrameNode *frameNode, const QVector<MailboxTexture *> textures) -{ -#ifndef QT_NO_OPENGL - gpu::MailboxManager *mailboxManager = mailbox_manager(); - for (MailboxTexture *texture : textures) { - gpu::SyncToken &syncToken = texture->mailboxHolder().sync_token; - if (syncToken.HasData()) - mailboxManager->PullTextureUpdates(syncToken); - texture->fetchTexture(mailboxManager); - --frameNode->m_numPendingSyncPoints; - } - - fenceAndUnlockQt(frameNode); -#else - Q_UNUSED(frameNode) - Q_UNUSED(textures) -#endif -} - -void DelegatedFrameNode::pullTexture(DelegatedFrameNode *frameNode, MailboxTexture *texture) -{ -#ifndef QT_NO_OPENGL - gpu::MailboxManager *mailboxManager = mailbox_manager(); - gpu::SyncToken &syncToken = texture->mailboxHolder().sync_token; - if (syncToken.HasData()) - mailboxManager->PullTextureUpdates(syncToken); - texture->fetchTexture(mailboxManager); - --frameNode->m_numPendingSyncPoints; - - fenceAndUnlockQt(frameNode); -#else - Q_UNUSED(frameNode) - Q_UNUSED(texture) -#endif -} - -void DelegatedFrameNode::fenceAndUnlockQt(DelegatedFrameNode *frameNode) -{ -#ifndef QT_NO_OPENGL - if (!!gl::GLContext::GetCurrent() && gl::GLFence::IsSupported()) { - // Create a fence on the Chromium GPU-thread and context - std::unique_ptr<gl::GLFence> fence = gl::GLFence::Create(); - // But transfer it to something generic since we need to read it using Qt's OpenGL. - frameNode->m_textureFences.append(fence->Transfer()); - } - if (frameNode->m_numPendingSyncPoints == 0) - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::Bind(&DelegatedFrameNode::unlockQt, frameNode)); -#else - Q_UNUSED(frameNode) -#endif -} - -void DelegatedFrameNode::unlockQt(DelegatedFrameNode *frameNode) -{ - QMutexLocker lock(&frameNode->m_mutex); - // Signal preprocess() the textures are ready - frameNode->m_mailboxesFetchedWaitCond.wakeOne(); } } // namespace QtWebEngineCore diff --git a/src/core/delegated_frame_node.h b/src/core/compositor/delegated_frame_node.h index e37ad08a3..34e4ba029 100644 --- a/src/core/delegated_frame_node.h +++ b/src/core/compositor/delegated_frame_node.h @@ -43,15 +43,10 @@ #include "base/containers/circular_deque.h" #include "components/viz/common/quads/compositor_frame.h" #include "components/viz/common/quads/render_pass.h" -#include "components/viz/common/resources/transferable_resource.h" -#include "gpu/command_buffer/service/sync_point_manager.h" -#include "ui/gl/gl_fence.h" -#include <QMutex> -#include <QSGNode> -#include <QSharedData> -#include <QSharedPointer> -#include <QWaitCondition> + +#include <QtCore/QSharedPointer> #include <QtGui/QOffscreenSurface> +#include <QtQuick/QSGTransformNode> #include "chromium_gpu_helper.h" #include "render_widget_host_view_qt_delegate.h" @@ -72,77 +67,60 @@ class DrawPolygon; namespace QtWebEngineCore { +class CompositorResource; +class CompositorResourceTracker; class DelegatedNodeTreeHandler; class MailboxTexture; -class ResourceHolder; - -// Separating this data allows another DelegatedFrameNode to reconstruct the QSGNode tree from the mailbox textures -// and render pass information. -class ChromiumCompositorData : public QSharedData { -public: - ChromiumCompositorData() : frameDevicePixelRatio(1) { } - QHash<unsigned, QSharedPointer<ResourceHolder> > resourceHolders; - viz::CompositorFrame frameData; - viz::CompositorFrame previousFrameData; - qreal frameDevicePixelRatio; -}; class DelegatedFrameNode : public QSGTransformNode { public: DelegatedFrameNode(); ~DelegatedFrameNode(); - void preprocess(); - void commit(ChromiumCompositorData *chromiumCompositorData, std::vector<viz::ReturnedResource> *resourcesToRelease, RenderWidgetHostViewQtDelegate *apiDelegate); + void preprocess() override; + void commit(const viz::CompositorFrame &pendingFrame, const viz::CompositorFrame &committedFrame, const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate); private: void flushPolygons(base::circular_deque<std::unique_ptr<viz::DrawPolygon> > *polygonQueue, QSGNode *renderPassChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate); void handlePolygon( const viz::DrawPolygon *polygon, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate); void handleClippedQuad( const viz::DrawQuad *quad, const gfx::QuadF &clipRegion, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate); void handleQuad( const viz::DrawQuad *quad, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate); - void fetchAndSyncMailboxes(QList<MailboxTexture *> &mailboxesToFetch); - // Making those callbacks static bypasses base::Bind's ref-counting requirement - // of the this pointer when the callback is a method. - static void pullTexture(DelegatedFrameNode *frameNode, MailboxTexture *mailbox); - static void pullTextures(DelegatedFrameNode *frameNode, const QVector<MailboxTexture *> mailboxes); - static void fenceAndUnlockQt(DelegatedFrameNode *frameNode); - static void unlockQt(DelegatedFrameNode *frameNode); - - ResourceHolder *findAndHoldResource(unsigned resourceId, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates); - void holdResources(const viz::DrawQuad *quad, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates); - void holdResources(const viz::RenderPass *pass, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates); - QSGTexture *initAndHoldTexture(ResourceHolder *resource, bool quadIsAllOpaque, RenderWidgetHostViewQtDelegate *apiDelegate = 0); - - QExplicitlySharedDataPointer<ChromiumCompositorData> m_chromiumCompositorData; + + const CompositorResource *findAndHoldResource(unsigned resourceId, const CompositorResourceTracker *resourceTracker); + void holdResources(const viz::DrawQuad *quad, const CompositorResourceTracker *resourceTracker); + void holdResources(const viz::RenderPass *pass, const CompositorResourceTracker *resourceTracker); + QSGTexture *initAndHoldTexture(const CompositorResource *resource, bool hasAlphaChannel, RenderWidgetHostViewQtDelegate *apiDelegate = 0, int target = -1); + QSharedPointer<QSGTexture> createBitmapTexture(const CompositorResource *resource, bool hasAlphaChannel, RenderWidgetHostViewQtDelegate *apiDelegate); + QSharedPointer<MailboxTexture> createMailboxTexture(const CompositorResource *resource, bool hasAlphaChannel, int target); + + void copyMailboxTextures(); + struct SGObjects { QVector<QPair<int, QSharedPointer<QSGLayer> > > renderPassLayers; QVector<QSharedPointer<QSGRootNode> > renderPassRootNodes; - QVector<QSharedPointer<QSGTexture> > textureStrongRefs; - } m_sgObjects; + QHash<unsigned, QSharedPointer<QSGTexture> > bitmapTextures; + QHash<unsigned, QSharedPointer<MailboxTexture> > mailboxTextures; + } m_sgObjects, m_previousSGObjects; QVector<QSGNode*> m_sceneGraphNodes; - int m_numPendingSyncPoints; - QWaitCondition m_mailboxesFetchedWaitCond; - QMutex m_mutex; - QList<gl::TransferableFence> m_textureFences; #if defined(USE_OZONE) bool m_contextShared; QScopedPointer<QOffscreenSurface> m_offsurface; diff --git a/src/core/stream_video_node.cpp b/src/core/compositor/stream_video_node.cpp index 29922f866..29922f866 100644 --- a/src/core/stream_video_node.cpp +++ b/src/core/compositor/stream_video_node.cpp diff --git a/src/core/stream_video_node.h b/src/core/compositor/stream_video_node.h index 9d937791f..9d937791f 100644 --- a/src/core/stream_video_node.h +++ b/src/core/compositor/stream_video_node.h diff --git a/src/core/yuv_video_node.cpp b/src/core/compositor/yuv_video_node.cpp index 4a436d952..4a436d952 100644 --- a/src/core/yuv_video_node.cpp +++ b/src/core/compositor/yuv_video_node.cpp diff --git a/src/core/yuv_video_node.h b/src/core/compositor/yuv_video_node.h index dca8fa5e2..dca8fa5e2 100644 --- a/src/core/yuv_video_node.h +++ b/src/core/compositor/yuv_video_node.h diff --git a/src/core/config/common.pri b/src/core/config/common.pri index ddb4ca4bf..be57942d2 100644 --- a/src/core/config/common.pri +++ b/src/core/config/common.pri @@ -4,6 +4,7 @@ QT_FOR_CONFIG += webenginecore gn_args += \ use_qt=true \ + closure_compile=false \ is_component_build=false \ is_shared=true \ enable_message_center=false \ @@ -11,15 +12,17 @@ gn_args += \ enable_nacl=false \ enable_remoting=false \ enable_reporting=false \ + enable_swiftshader=false \ + enable_web_auth=false \ enable_web_speech=false \ enable_widevine=true \ has_native_accessibility=false \ use_allocator_shim=false \ use_allocator=\"none\" \ + use_custom_libcxx=false \ v8_use_external_startup_data=false \ - treat_warnings_as_errors=false \ - enable_swiftshader=false \ - use_custom_libcxx=false + toolkit_views=false \ + treat_warnings_as_errors=false !win32: gn_args += \ use_jumbo_build=true \ diff --git a/src/core/config/mac_osx.pri b/src/core/config/mac_osx.pri index 4426901cf..e49df90e7 100644 --- a/src/core/config/mac_osx.pri +++ b/src/core/config/mac_osx.pri @@ -28,8 +28,6 @@ gn_args += \ clang_use_chrome_plugins=false \ mac_deployment_target=\"$${QMAKE_MACOSX_DEPLOYMENT_TARGET}\" \ mac_sdk_min=\"$${QMAKE_MAC_SDK_VERSION}\" \ - mac_views_browser=false \ - toolkit_views=false \ use_external_popup_menu=false qtConfig(webengine-spellchecker) { diff --git a/src/core/configure.json b/src/core/configure.json index 044d85527..755a525a8 100644 --- a/src/core/configure.json +++ b/src/core/configure.json @@ -25,6 +25,8 @@ "webengine-v8-snapshot": "boolean", "webengine-webchannel": "boolean", "webengine-kerberos": "boolean", + "webengine-widgets": "boolean", + "webengine-qml": "boolean", "alsa": { "type": "boolean", "name": "webengine-alsa" }, "pulseaudio": { "type": "boolean", "name": "webengine-pulseaudio" }, "ffmpeg": { "type": "enum", "name": "webengine-system-ffmpeg", "values": { "system": "yes", "qt": "no" } }, @@ -82,9 +84,9 @@ ] }, "webengine-harfbuzz": { - "label": "harfbuzz >= 1.4.2", + "label": "harfbuzz >= 2.0.0", "sources": [ - { "type": "pkgConfig", "args": "harfbuzz >= 1.4.2" } + { "type": "pkgConfig", "args": "harfbuzz >= 2.0.0" } ] }, "webengine-glib": { @@ -546,15 +548,6 @@ "condition": "config.macos && features.webengine-spellchecker", "output": [ "publicFeature" ] }, - "webengine-ui-delegates": { - "label": "UI Delegates", - "output": [ "privateFeature" ] - }, - "webengine-testsupport": { - "label": "Test Support", - "autoDetect": "features.private_tests || call.isTestsInBuildParts", - "output": [ "privateFeature" ] - }, "webengine-webrtc": { "label": "WebRTC", "purpose": "Provides WebRTC support.", @@ -657,6 +650,19 @@ "label": "Thumb instruction set", "condition": "config.linux && features.webengine-embedded-build && arch.arm && tests.webengine-arm-thumb", "output": [ "privateFeature" ] + }, + "webengine-widgets": { + "label": "Qt WebEngine Widgets", + "purpose": "Provides WebEngine Widgets support.", + "section": "WebEngine", + "condition": "module.widgets", + "output": [ "privateFeature" ] + }, + "webengine-qml": { + "label": "Qt WebEngine Qml", + "purpose": "Provides WebEngine Qml support.", + "section": "WebEngine", + "output": [ "privateFeature" ] } }, @@ -705,8 +711,10 @@ "summary": [ { - "section": "Qt WebEngine", + "section": "Qt WebEngineCore", "entries": [ + "webengine-widgets", + "webengine-qml", "webengine-embedded-build", "webengine-pepper-plugins", "webengine-printing-and-pdf", diff --git a/src/core/content_browser_client_qt.cpp b/src/core/content_browser_client_qt.cpp index 0a51cc261..98262570c 100644 --- a/src/core/content_browser_client_qt.cpp +++ b/src/core/content_browser_client_qt.cpp @@ -42,12 +42,16 @@ #include "base/json/json_reader.h" #include "base/memory/ptr_util.h" #include "base/strings/utf_string_conversions.h" +#include "base/message_loop/message_loop.h" +#include "base/task/post_task.h" +#include "base/threading/thread_restrictions.h" #if QT_CONFIG(webengine_spellchecker) #include "chrome/browser/spellchecker/spell_check_host_chrome_impl.h" #endif #include "components/network_hints/browser/network_hints_message_filter.h" #include "content/browser/renderer_host/render_view_host_delegate.h" #include "content/common/url_schemes.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/child_process_security_policy.h" #include "content/public/browser/client_certificate_delegate.h" @@ -85,6 +89,7 @@ #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" @@ -95,7 +100,6 @@ #include "login_delegate_qt.h" #include "media_capture_devices_dispatcher.h" #include "net/network_delegate_qt.h" -#include "net/qrc_protocol_handler_qt.h" #include "net/url_request_context_getter_qt.h" #if QT_CONFIG(webengine_printing_and_pdf) #include "printing/printing_message_filter_qt.h" @@ -114,18 +118,6 @@ #include "ui/base/resource/resource_bundle.h" #endif -#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 - #if QT_CONFIG(webengine_pepper_plugins) #include "content/public/browser/browser_ppapi_host.h" #include "ppapi/host/ppapi_host.h" @@ -248,10 +240,10 @@ void ContentBrowserClientQt::RenderProcessWillLaunch(content::RenderProcessHost* { const int id = host->GetID(); Profile *profile = Profile::FromBrowserContext(host->GetBrowserContext()); - content::BrowserThread::PostTaskAndReplyWithResult( - content::BrowserThread::IO, FROM_HERE, - base::Bind(&net::URLRequestContextGetter::GetURLRequestContext, base::Unretained(profile->GetRequestContext())), - base::Bind(&ContentBrowserClientQt::AddNetworkHintsMessageFilter, base::Unretained(this), id)); + base::PostTaskWithTraitsAndReplyWithResult( + FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&net::URLRequestContextGetter::GetURLRequestContext, base::Unretained(profile->GetRequestContext())), + base::BindOnce(&ContentBrowserClientQt::AddNetworkHintsMessageFilter, base::Unretained(this), id)); // FIXME: Add a settings variable to enable/disable the file scheme. content::ChildProcessSecurityPolicy::GetInstance()->GrantRequestScheme(id, url::kFileScheme); @@ -382,17 +374,8 @@ std::unique_ptr<net::ClientCertStore> ContentBrowserClientQt::CreateClientCertSt { if (!resource_context) return nullptr; -#if defined(USE_NSS_CERTS) - // FIXME: Give it a proper callback for a password delegate. - 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 + + return std::unique_ptr<net::ClientCertStore>(new net::ClientCertOverrideStore()); } std::string ContentBrowserClientQt::GetApplicationLocale() @@ -618,11 +601,18 @@ void ContentBrowserClientQt::AddNetworkHintsMessageFilter(int render_process_id, if (!host) return; - content::BrowserMessageFilter *network_hints_message_filter( - new network_hints::NetworkHintsMessageFilter(context->host_resolver())); + content::BrowserMessageFilter *network_hints_message_filter = + new network_hints::NetworkHintsMessageFilter(render_process_id); host->AddFilter(network_hints_message_filter); } +bool ContentBrowserClientQt::ShouldEnableStrictSiteIsolation() +{ + // mirroring AwContentBrowserClient, CastContentBrowserClient and + // HeadlessContentBrowserClient + return false; +} + bool ContentBrowserClientQt::AllowGetCookie(const GURL &url, const GURL &first_party, const net::CookieList & /*cookie_list*/, @@ -671,7 +661,7 @@ bool ContentBrowserClientQt::AllowServiceWorker(const GURL &scope, // We control worker access to FS and indexed-db using cookie permissions, this is mirroring Chromium's logic. void ContentBrowserClientQt::AllowWorkerFileSystem(const GURL &url, content::ResourceContext *context, - const std::vector<std::pair<int, int> > &/*render_frames*/, + const std::vector<content::GlobalFrameRoutingId> &/*render_frames*/, base::Callback<void(bool)> callback) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); @@ -682,7 +672,7 @@ void ContentBrowserClientQt::AllowWorkerFileSystem(const GURL &url, bool ContentBrowserClientQt::AllowWorkerIndexedDB(const GURL &url, const base::string16 &/*name*/, content::ResourceContext *context, - const std::vector<std::pair<int, int> > &/*render_frames*/) + const std::vector<content::GlobalFrameRoutingId> &/*render_frames*/) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); NetworkDelegateQt *networkDelegate = static_cast<NetworkDelegateQt *>(context->GetRequestContext()->network_delegate()); @@ -715,15 +705,13 @@ bool ContentBrowserClientQt::HandleExternalProtocol( Q_UNUSED(child_id); Q_UNUSED(navigation_data); - content::BrowserThread::PostTask( - content::BrowserThread::UI, - FROM_HERE, - base::BindOnce(&LaunchURL, - url, - web_contents_getter, - page_transition, - is_main_frame, - has_user_gesture)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&LaunchURL, + url, + web_contents_getter, + page_transition, + is_main_frame, + has_user_gesture)); return true; } @@ -737,9 +725,9 @@ scoped_refptr<content::LoginDelegate> ContentBrowserClientQt::CreateLoginDelegat bool first_auth_attempt, LoginAuthRequiredCallback auth_required_callback) { - return base::MakeRefCounted<LoginDelegateQt>(authInfo, web_contents_getter, url, first_auth_attempt, std::move(auth_required_callback)); + auto loginDelegate = base::MakeRefCounted<LoginDelegateQt>(authInfo, web_contents_getter, url, first_auth_attempt, std::move(auth_required_callback)); + loginDelegate->triggerDialog(); + return loginDelegate; } } // namespace QtWebEngineCore - -DEFINE_WEB_CONTENTS_USER_DATA_KEY(QtWebEngineCore::ServiceDriver); diff --git a/src/core/content_browser_client_qt.h b/src/core/content_browser_client_qt.h index b2761b311..9e503cace 100644 --- a/src/core/content_browser_client_qt.h +++ b/src/core/content_browser_client_qt.h @@ -135,6 +135,7 @@ public: bool user_gesture, bool opener_suppressed, bool* no_javascript_access) override; + bool ShouldEnableStrictSiteIsolation() override; bool AllowGetCookie(const GURL& url, const GURL& first_party, @@ -161,13 +162,13 @@ public: void AllowWorkerFileSystem(const GURL &url, content::ResourceContext *context, - const std::vector<std::pair<int, int> > &render_frames, + const std::vector<content::GlobalFrameRoutingId> &render_frames, base::Callback<void(bool)> callback) override; bool AllowWorkerIndexedDB(const GURL &url, const base::string16 &name, content::ResourceContext *context, - const std::vector<std::pair<int, int> > &render_frames) override; + const std::vector<content::GlobalFrameRoutingId> &render_frames) override; #if QT_CONFIG(webengine_geolocation) std::unique_ptr<device::LocationProvider> OverrideSystemLocationProvider() override; diff --git a/src/core/content_client_qt.cpp b/src/core/content_client_qt.cpp index 1a3be31be..41e3f7f6c 100644 --- a/src/core/content_client_qt.cpp +++ b/src/core/content_client_qt.cpp @@ -56,7 +56,6 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" -#include "net/qrc_protocol_handler_qt.h" #include "type_conversion.h" #include <QCoreApplication> @@ -391,6 +390,11 @@ base::RefCountedMemory *ContentClientQt::GetDataResourceBytes(int resource_id) c return ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytes(resource_id); } +gfx::Image &ContentClientQt::GetNativeImageNamed(int resource_id) const +{ + return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(resource_id); +} + base::string16 ContentClientQt::GetLocalizedString(int message_id) const { return l10n_util::GetStringUTF16(message_id); diff --git a/src/core/content_client_qt.h b/src/core/content_client_qt.h index fbafa6a4b..c3feded00 100644 --- a/src/core/content_client_qt.h +++ b/src/core/content_client_qt.h @@ -58,7 +58,8 @@ public: std::vector<media::CdmHostFilePath> *cdm_host_file_paths) override; base::StringPiece GetDataResource(int, ui::ScaleFactor) const override; - base::RefCountedMemory* GetDataResourceBytes(int resource_id) const override; + base::RefCountedMemory* GetDataResourceBytes(int resource_id) const override; + gfx::Image &GetNativeImageNamed(int resource_id) const override; std::string GetUserAgent() const override { return getUserAgent(); } base::string16 GetLocalizedString(int message_id) const override; std::string GetProduct() const override; diff --git a/src/core/content_main_delegate_qt.cpp b/src/core/content_main_delegate_qt.cpp index 2811d5545..dc32bb8c1 100644 --- a/src/core/content_main_delegate_qt.cpp +++ b/src/core/content_main_delegate_qt.cpp @@ -40,17 +40,18 @@ #include "content_main_delegate_qt.h" #include "base/command_line.h" -#include <base/i18n/rtl.h> +#include "base/i18n/rtl.h" #include "base/logging.h" #include "base/path_service.h" #include "base/strings/string_number_conversions.h" -#include <chrome/grit/generated_resources.h> +#include "chrome/grit/generated_resources.h" +#include "content/public/browser/browser_main_runner.h" #include "content/public/common/content_paths.h" #include "content/public/common/content_switches.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/ui_base_paths.h" #include "ui/base/resource/resource_bundle.h" -#include <ui/base/webui/jstemplate_builder.h> +#include "ui/base/webui/jstemplate_builder.h" #include "net/grit/net_resources.h" #include "net/base/net_module.h" #include "services/service_manager/sandbox/switches.h" @@ -59,6 +60,7 @@ #include "content_client_qt.h" #include "renderer/content_renderer_client_qt.h" #include "type_conversion.h" +#include "web_engine_context.h" #include "web_engine_library_info.h" #if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) @@ -71,6 +73,10 @@ #include <QtCore/qcoreapplication.h> +namespace content { +ContentClient *GetContentClient(); +} + namespace QtWebEngineCore { // The logic of this function is based on chrome/common/net/net_resource_provider.cc @@ -167,6 +173,12 @@ content::ContentBrowserClient *ContentMainDelegateQt::CreateContentBrowserClient return m_browserClient.get(); } +content::ContentGpuClient *ContentMainDelegateQt::CreateContentGpuClient() +{ + m_gpuClient.reset(new ContentGpuClientQt); + return m_gpuClient.get(); +} + content::ContentRendererClient *ContentMainDelegateQt::CreateContentRendererClient() { #if defined(OS_LINUX) @@ -221,7 +233,8 @@ bool ContentMainDelegateQt::BasicStartupComplete(int *exit_code) #if QT_CONFIG(webengine_spellchecker) SafeOverridePath(base::DIR_APP_DICTIONARIES, WebEngineLibraryInfo::getPath(base::DIR_APP_DICTIONARIES)); #endif - SetContentClient(new ContentClientQt); + if (!content::GetContentClient()) + content::SetContentClient(new ContentClientQt); url::CustomScheme::LoadSchemes(base::CommandLine::ForCurrentProcess()); diff --git a/src/core/content_main_delegate_qt.h b/src/core/content_main_delegate_qt.h index c06afb0fb..4d2f33792 100644 --- a/src/core/content_main_delegate_qt.h +++ b/src/core/content_main_delegate_qt.h @@ -42,6 +42,7 @@ #include "content/public/app/content_main_delegate.h" +#include "compositor/content_gpu_client_qt.h" #include "content_browser_client_qt.h" #include "content_utility_client_qt.h" @@ -56,12 +57,14 @@ public: void PreSandboxStartup() override; content::ContentBrowserClient* CreateContentBrowserClient() override; + content::ContentGpuClient* CreateContentGpuClient() override; content::ContentRendererClient* CreateContentRendererClient() override; content::ContentUtilityClient* CreateContentUtilityClient() override; bool BasicStartupComplete(int* /*exit_code*/) override; private: std::unique_ptr<ContentBrowserClientQt> m_browserClient; + std::unique_ptr<ContentGpuClientQt> m_gpuClient; std::unique_ptr<ContentUtilityClientQt> m_utilityClient; }; diff --git a/src/core/core_chromium.pri b/src/core/core_chromium.pri index e92dd60ff..3918dc0aa 100644 --- a/src/core/core_chromium.pri +++ b/src/core/core_chromium.pri @@ -38,6 +38,7 @@ qtConfig(egl): CONFIG += egl INCLUDEPATH += $$PWD $$PWD/api SOURCES = \ + accessibility_activation_observer.cpp \ accessibility_tree_formatter_qt.cpp \ authentication_dialog_controller.cpp \ browser_accessibility_manager_qt.cpp \ @@ -46,8 +47,8 @@ SOURCES = \ browser_main_parts_qt.cpp \ browser_message_filter_qt.cpp \ certificate_error_controller.cpp \ - chromium_gpu_helper.cpp \ chromium_overrides.cpp \ + client_cert_override_key.cpp \ client_cert_select_controller.cpp \ clipboard_qt.cpp \ color_chooser_qt.cpp \ @@ -55,12 +56,15 @@ SOURCES = \ common/qt_ipc_logging.cpp \ common/qt_messages.cpp \ common/user_script_data.cpp \ - compositor.cpp \ + compositor/chromium_gpu_helper.cpp \ + compositor/compositor.cpp \ + compositor/compositor_resource_tracker.cpp \ + compositor/content_gpu_client_qt.cpp \ + compositor/delegated_frame_node.cpp \ content_client_qt.cpp \ content_browser_client_qt.cpp \ content_main_delegate_qt.cpp \ content_utility_client_qt.cpp \ - delegated_frame_node.cpp \ desktop_screen_qt.cpp \ devtools_frontend_qt.cpp \ devtools_manager_delegate_qt.cpp \ @@ -76,13 +80,12 @@ SOURCES = \ net/custom_protocol_handler.cpp \ net/network_delegate_qt.cpp \ net/proxy_config_service_qt.cpp \ - net/qrc_protocol_handler_qt.cpp \ + net/qrc_url_scheme_handler.cpp \ net/ssl_host_state_delegate_qt.cpp \ net/url_request_context_getter_qt.cpp \ net/url_request_custom_job.cpp \ net/url_request_custom_job_delegate.cpp \ net/url_request_custom_job_proxy.cpp \ - net/url_request_qrc_job_qt.cpp \ net/webui_controller_factory_qt.cpp \ ozone/gl_context_qt.cpp \ ozone/gl_ozone_egl_qt.cpp \ @@ -100,6 +103,7 @@ 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 \ @@ -113,6 +117,9 @@ SOURCES = \ resource_bundle_qt.cpp \ resource_context_qt.cpp \ service/service_qt.cpp \ + touch_handle_drawable_qt.cpp \ + touch_selection_controller_client_qt.cpp \ + touch_selection_menu_controller.cpp \ type_conversion.cpp \ user_script.cpp \ visited_links_manager_qt.cpp \ @@ -120,12 +127,14 @@ SOURCES = \ web_contents_delegate_qt.cpp \ web_contents_view_qt.cpp \ web_engine_context.cpp \ + web_engine_context_threads.cpp \ web_engine_error.cpp \ web_engine_library_info.cpp \ web_engine_settings.cpp \ web_event_factory.cpp HEADERS = \ + accessibility_activation_observer.h \ authentication_dialog_controller_p.h \ authentication_dialog_controller.h \ build_config_qt.h \ @@ -137,6 +146,8 @@ HEADERS = \ 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 \ color_chooser_qt.h \ @@ -144,35 +155,38 @@ HEADERS = \ color_chooser_controller.h \ common/qt_messages.h \ common/user_script_data.h \ - compositor.h \ + compositor/chromium_gpu_helper.h \ + compositor/compositor.h \ + compositor/compositor_resource.h \ + compositor/compositor_resource_tracker.h \ + compositor/content_gpu_client_qt.h \ + compositor/delegated_frame_node.h \ content_client_qt.h \ content_browser_client_qt.h \ content_main_delegate_qt.h \ content_utility_client_qt.h \ - delegated_frame_node.h \ desktop_screen_qt.h \ devtools_frontend_qt.h \ devtools_manager_delegate_qt.h \ download_manager_delegate_qt.h \ - chromium_gpu_helper.h \ favicon_manager.h \ file_picker_controller.h \ global_descriptors_qt.h \ javascript_dialog_controller_p.h \ javascript_dialog_controller.h \ javascript_dialog_manager_qt.h \ + locked_ptr.h \ login_delegate_qt.h \ media_capture_devices_dispatcher.h \ net/cookie_monster_delegate_qt.h \ net/custom_protocol_handler.h \ net/network_delegate_qt.h \ - net/qrc_protocol_handler_qt.h \ + net/qrc_url_scheme_handler.h \ net/ssl_host_state_delegate_qt.h \ net/url_request_context_getter_qt.h \ net/url_request_custom_job.h \ net/url_request_custom_job_delegate.h \ net/url_request_custom_job_proxy.h \ - net/url_request_qrc_job_qt.h \ net/webui_controller_factory_qt.h \ ozone/gl_context_qt.h \ ozone/gl_ozone_egl_qt.h \ @@ -207,6 +221,10 @@ HEADERS = \ request_controller.h \ resource_context_qt.h \ service/service_qt.h \ + touch_handle_drawable_client.h \ + touch_handle_drawable_qt.h \ + touch_selection_controller_client_qt.h \ + touch_selection_menu_controller.h \ type_conversion.h \ user_script.h \ visited_links_manager_qt.h \ @@ -266,12 +284,14 @@ qtConfig(webengine-printing-and-pdf) { contains(QT_CONFIG, opengl) { SOURCES += \ - yuv_video_node.cpp \ - stream_video_node.cpp + compositor/compositor_resource_fence.cpp \ + compositor/stream_video_node.cpp \ + compositor/yuv_video_node.cpp HEADERS += \ - yuv_video_node.h \ - stream_video_node.h + compositor/compositor_resource_fence.h \ + compositor/stream_video_node.h \ + compositor/yuv_video_node.h } qtConfig(webengine-geolocation) { diff --git a/src/core/devtools_frontend_qt.cpp b/src/core/devtools_frontend_qt.cpp index bd9e0ebe7..40e30e008 100644 --- a/src/core/devtools_frontend_qt.cpp +++ b/src/core/devtools_frontend_qt.cpp @@ -56,11 +56,13 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "base/task/post_task.h" #include "base/values.h" #include "chrome/common/url_constants.h" #include "components/prefs/in_memory_pref_store.h" #include "components/prefs/json_pref_store.h" #include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_handle.h" @@ -109,9 +111,9 @@ public: ~ResponseWriter() override; // URLFetcherResponseWriter overrides: - int Initialize(const net::CompletionCallback &callback) override; - int Write(net::IOBuffer *buffer, int num_bytes, const net::CompletionCallback &callback) override; - int Finish(int net_error, const net::CompletionCallback &callback) override; + int Initialize(net::CompletionOnceCallback callback) override; + int Write(net::IOBuffer *buffer, int num_bytes, net::CompletionOnceCallback callback) override; + int Finish(int net_error, net::CompletionOnceCallback callback) override; private: base::WeakPtr<DevToolsFrontendQt> shell_devtools_; @@ -126,12 +128,12 @@ ResponseWriter::ResponseWriter(base::WeakPtr<DevToolsFrontendQt> shell_devtools, ResponseWriter::~ResponseWriter() {} -int ResponseWriter::Initialize(const net::CompletionCallback& callback) +int ResponseWriter::Initialize(net::CompletionOnceCallback callback) { return net::OK; } -int ResponseWriter::Write(net::IOBuffer *buffer, int num_bytes, const net::CompletionCallback &callback) +int ResponseWriter::Write(net::IOBuffer *buffer, int num_bytes, net::CompletionOnceCallback callback) { std::string chunk = std::string(buffer->data(), num_bytes); if (!base::IsStringUTF8(chunk)) @@ -140,15 +142,15 @@ int ResponseWriter::Write(net::IOBuffer *buffer, int num_bytes, const net::Compl base::Value *id = new base::Value(stream_id_); base::Value *chunkValue = new base::Value(chunk); - content::BrowserThread::PostTask( - content::BrowserThread::UI, FROM_HERE, + base::PostTaskWithTraits( + FROM_HERE, {content::BrowserThread::UI}, base::BindOnce(&DevToolsFrontendQt::CallClientFunction, shell_devtools_, "DevToolsAPI.streamWrite", base::Owned(id), base::Owned(chunkValue), nullptr)); return num_bytes; } -int ResponseWriter::Finish(int net_error, const net::CompletionCallback &callback) +int ResponseWriter::Finish(int net_error, net::CompletionOnceCallback callback) { return net::OK; } diff --git a/src/core/download_manager_delegate_qt.cpp b/src/core/download_manager_delegate_qt.cpp index 948a62047..398bde710 100644 --- a/src/core/download_manager_delegate_qt.cpp +++ b/src/core/download_manager_delegate_qt.cpp @@ -164,7 +164,7 @@ bool DownloadManagerDelegateQt::DetermineDownloadTarget(download::DownloadItem* suggestedFilename += QStringLiteral(".") + mimeType.preferredSuffix(); } - QDir defaultDownloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); + QDir defaultDownloadDirectory(m_profileAdapter->downloadPath()); QFileInfo suggestedFile(defaultDownloadDirectory.absoluteFilePath(suggestedFilename)); QString suggestedFilePath = suggestedFile.absoluteFilePath(); diff --git a/src/core/file_picker_controller.cpp b/src/core/file_picker_controller.cpp index 3ded5ec41..a495ce6f2 100644 --- a/src/core/file_picker_controller.cpp +++ b/src/core/file_picker_controller.cpp @@ -41,6 +41,7 @@ #include "type_conversion.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/file_select_listener.h" #include <QFileInfo> #include <QDir> @@ -49,18 +50,20 @@ namespace QtWebEngineCore { -FilePickerController::FilePickerController(FileChooserMode mode, content::RenderFrameHost *frameHost, const QString &defaultFileName, const QStringList &acceptedMimeTypes, QObject *parent) +FilePickerController::FilePickerController(FileChooserMode mode, std::unique_ptr<content::FileSelectListener> listener, const QString &defaultFileName, const QStringList &acceptedMimeTypes, QObject *parent) : QObject(parent) , m_defaultFileName(defaultFileName) , m_acceptedMimeTypes(acceptedMimeTypes) - , m_frameHost(frameHost) + , m_listener(std::move(listener)) , m_mode(mode) { } +FilePickerController::~FilePickerController() = default; + void FilePickerController::accepted(const QStringList &files) { - FilePickerController::filesSelectedInChooser(files, m_frameHost); + FilePickerController::filesSelectedInChooser(files); } void FilePickerController::accepted(const QVariant &files) @@ -77,12 +80,12 @@ void FilePickerController::accepted(const QVariant &files) qWarning("An unhandled type '%s' was provided in FilePickerController::accepted(QVariant)", files.typeName()); } - FilePickerController::filesSelectedInChooser(stringList, m_frameHost); + FilePickerController::filesSelectedInChooser(stringList); } void FilePickerController::rejected() { - FilePickerController::filesSelectedInChooser(QStringList(), m_frameHost); + FilePickerController::filesSelectedInChooser(QStringList()); } static QStringList listRecursively(const QDir &dir) @@ -99,19 +102,29 @@ static QStringList listRecursively(const QDir &dir) return ret; } -ASSERT_ENUMS_MATCH(FilePickerController::Open, content::FileChooserParams::Open) -ASSERT_ENUMS_MATCH(FilePickerController::OpenMultiple, content::FileChooserParams::OpenMultiple) -ASSERT_ENUMS_MATCH(FilePickerController::UploadFolder, content::FileChooserParams::UploadFolder) -ASSERT_ENUMS_MATCH(FilePickerController::Save, content::FileChooserParams::Save) +ASSERT_ENUMS_MATCH(FilePickerController::Open, blink::mojom::FileChooserParams_Mode::kOpen) +ASSERT_ENUMS_MATCH(FilePickerController::OpenMultiple, blink::mojom::FileChooserParams_Mode::kOpenMultiple) +ASSERT_ENUMS_MATCH(FilePickerController::UploadFolder, blink::mojom::FileChooserParams_Mode::kUploadFolder) +ASSERT_ENUMS_MATCH(FilePickerController::Save, blink::mojom::FileChooserParams_Mode::kSave) -void FilePickerController::filesSelectedInChooser(const QStringList &filesList, content::RenderFrameHost *frameHost) +void FilePickerController::filesSelectedInChooser(const QStringList &filesList) { - Q_ASSERT(frameHost); QStringList files(filesList); if (this->m_mode == UploadFolder && !filesList.isEmpty() && QFileInfo(filesList.first()).isDir()) // Enumerate the directory files = listRecursively(QDir(filesList.first())); - frameHost->FilesSelectedInChooser(toVector<content::FileChooserFileInfo>(files), static_cast<content::FileChooserParams::Mode>(this->m_mode)); + + std::vector<blink::mojom::FileChooserFileInfoPtr> chooser_files; + for (const auto &file : qAsConst(files)) { + chooser_files.push_back(blink::mojom::FileChooserFileInfo::NewNativeFile( + blink::mojom::NativeFileInfo::New(toFilePath(file), base::string16()))); + } + + if (files.isEmpty()) + m_listener->FileSelectionCanceled(); + else + m_listener->FileSelected(std::move(chooser_files), + static_cast<blink::mojom::FileChooserParams::Mode>(this->m_mode)); } QStringList FilePickerController::acceptedMimeTypes() const diff --git a/src/core/file_picker_controller.h b/src/core/file_picker_controller.h index 7507cf358..613d3ad9b 100644 --- a/src/core/file_picker_controller.h +++ b/src/core/file_picker_controller.h @@ -52,11 +52,14 @@ #define FILE_PICKER_CONTROLLER_H #include "qtwebenginecoreglobal_p.h" + +#include <memory> + #include <QObject> #include <QStringList> namespace content { - class RenderFrameHost; + class FileSelectListener; } namespace QtWebEngineCore { @@ -71,7 +74,8 @@ public: Save }; - FilePickerController(FileChooserMode mode, content::RenderFrameHost *contents, const QString &defaultFileName, const QStringList &acceptedMimeTypes, QObject * = 0); + FilePickerController(FileChooserMode mode, std::unique_ptr<content::FileSelectListener> listener, const QString &defaultFileName, const QStringList &acceptedMimeTypes, QObject * = 0); + ~FilePickerController() override; QStringList acceptedMimeTypes() const; QString defaultFileName() const; FileChooserMode mode() const; @@ -82,10 +86,10 @@ public Q_SLOTS: void rejected(); private: - void filesSelectedInChooser(const QStringList &filesList, content::RenderFrameHost *contents); + void filesSelectedInChooser(const QStringList &filesList); QString m_defaultFileName; QStringList m_acceptedMimeTypes; - content::RenderFrameHost *m_frameHost; + std::unique_ptr<content::FileSelectListener> m_listener; FileChooserMode m_mode; }; diff --git a/src/core/locked_ptr.h b/src/core/locked_ptr.h new file mode 100644 index 000000000..46d89819b --- /dev/null +++ b/src/core/locked_ptr.h @@ -0,0 +1,301 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LOCKED_PTR_H +#define LOCKED_PTR_H + +#include <base/bind_internal.h> + +#include <QtCore/qreadwritelock.h> + +namespace base { + +struct LockedPtrCore +{ + LockedPtrCore(uintptr_t data) : data(data) {} + + std::atomic<size_t> refCount{1}; + // Atomic so that WeakLockedPtr::get can still read it. + std::atomic<uintptr_t> data; + QReadWriteLock lock{QReadWriteLock::Recursive}; +}; + +enum class LockedPtrMode { Weak, Shared, Exclusive }; + +template<class T, LockedPtrMode mode> class LockedPtr; + +// A WeakLockedPtr<T> is something like shared_ptr<T*>. The T* value can only be +// accessed by atomic read. +template<class T> using WeakLockedPtr = LockedPtr<T, LockedPtrMode::Weak>; + +// A SharedLockedPtr<T> is like WeakLockedPtr<T>, but the T* value is prevented +// from changing for the lifetime of the SharedLockedPtr by holding a +// shared-exclusive mutex in shared mode. +template<class T> using SharedLockedPtr = LockedPtr<T, LockedPtrMode::Shared>; + +// An ExclusiveLockedPtr<T> is like SharedLockedPtr<T>, but the mutex is held in +// exclusive mode. Only in this mode can the T* value be changed. +template<class T> using ExclusiveLockedPtr = LockedPtr<T, LockedPtrMode::Exclusive>; + +template<class T, LockedPtrMode mode> +class LockedPtr +{ + template<class T1> + static constexpr bool canConstructFrom = + std::is_same<T, T1>::value || + std::is_same<T, const T1>::value; + +public: + constexpr LockedPtr() {} + constexpr LockedPtr(std::nullptr_t) {} + + LockedPtr(const LockedPtr &that) + { + m_core = that.m_core; + lock(); + } + + LockedPtr &operator=(const LockedPtr &that) + { + unlock(); + m_core = that.m_core; + lock(); + } + + LockedPtr(LockedPtr &&that) + { + m_core = that.m_core; + that.m_core = nullptr; + } + + LockedPtr &operator=(LockedPtr &&that) + { + unlock(); + m_core = that.m_core; + that.m_core = nullptr; + } + + template<class T1, LockedPtrMode mode1, + class Enable = std::enable_if_t<canConstructFrom<T1>>> + LockedPtr(const LockedPtr<T1, mode1> &that) + { + m_core = that.m_core; + lock(); + } + + template<class T1, LockedPtrMode mode1, + class Enable = std::enable_if_t<canConstructFrom<T1>>> + LockedPtr &operator=(const LockedPtr<T1, mode1> &that) + { + unlock(); + m_core = that.m_core; + lock(); + } + + template<class T1, + class Enable = std::enable_if_t<canConstructFrom<T1>>> + LockedPtr(LockedPtr<T1, mode> &&that) + { + m_core = that.m_core; + that.m_core = nullptr; + } + + template<class T1, + class Enable = std::enable_if_t<canConstructFrom<T1>>> + LockedPtr &operator=(LockedPtr<T1, mode> &&that) + { + unlock(); + m_core = that.m_core; + that.m_core = nullptr; + } + + ~LockedPtr() + { + unlock(); + } + + T *get() const + { + if (m_core) { + if (mode == LockedPtrMode::Weak) + return reinterpret_cast<T *>(m_core->data.load(std::memory_order_acquire)); + else + return reinterpret_cast<T *>(m_core->data.load(std::memory_order_relaxed)); + } + return nullptr; + } + + void set(T *value) + { + static_assert(mode == LockedPtrMode::Exclusive, ""); + DCHECK(m_core); + m_core->data.store(reinterpret_cast<uintptr_t>(value), std::memory_order_release); + } + + T &operator*() const { return *get(); } + T *operator->() const { return get(); } + explicit operator bool() const { return get(); } + + bool MaybeValid() const { return m_core; } + + static LockedPtr create(T *value) + { + return new LockedPtrCore(reinterpret_cast<uintptr_t>(value)); + } + +private: + template<class T1, LockedPtrMode mode1> friend class LockedPtr; + + LockedPtr(LockedPtrCore *core) + : m_core(core) + {} + + void lock() + { + if (m_core) { + ++m_core->refCount; + + if (mode == LockedPtrMode::Shared) + m_core->lock.lockForRead(); + else if (mode == LockedPtrMode::Exclusive) + m_core->lock.lockForWrite(); + } + } + + void unlock() + { + if (m_core) { + if (mode != LockedPtrMode::Weak) + m_core->lock.unlock(); + + if (--m_core->refCount == 0) + delete m_core; + } + } + + LockedPtrCore *m_core = nullptr; +}; + +// This makes Bind check the pointer before calling the functor. +template<class T> +struct IsWeakReceiver<WeakLockedPtr<T>> : std::true_type {}; + +// By converting the WeakLockedPtr into a SharedLockedPtr we prevent the +// pointed-to object from being destroyed during the base::Callback::Run call. +// +// Unwrap() is called before checking the pointer, so there's no race condition. +template<class T> +struct BindUnwrapTraits<WeakLockedPtr<T>> +{ + static SharedLockedPtr<T> Unwrap(const WeakLockedPtr<T> &o) + { + return o; + } +}; + +// Like base::WeakPtrFactory, but InvalidateWeakPtrs *waits* until all currently +// executing base::Callbacks are finished. Queued up base::Callbacks are still +// canceled, exactly like with WeakPtrFactory. +// +// Consider, for example, the function +// +// void fun() +// { +// MyClass *myClass = new MyClass; +// myClass->scheduleDoStuff(); +// delete myClass; // ??? +// } +// +// where +// +// class MyClass +// { +// public: +// void scheduleDoStuff() +// { +// content::BrowserThread::PostTask( +// content::BrowserThread::IO, FROM_HERE, +// base::BindOnce(&MyClass::doStuff, m_weakPtrFactory.GetWeakPtr())); +// } +// void doStuff(); +// private: +// //base::WeakPtrFactory m_weakPtrFactory{this}; +// base::LockedPtrFactory m_weakPtrFactory{this}; +// }; +// +// What happens if the 'delete myClass' line is executed concurrently with +// MyClass::doStuff? +// +// With WeakPtrs we get a segfault or perhaps memory corruption. +// +// With LockedPtrs we get no crash and no corruption: LockedPtrFactory's +// destructor will wait until doStuff is done before continuing. +template<class T> +class LockedPtrFactory +{ +public: + explicit LockedPtrFactory(T *value) + : m_ptr(WeakLockedPtr<T>::create(value)) + {} + + ~LockedPtrFactory() + { + InvalidateWeakPtrs(); + } + + WeakLockedPtr<T> GetWeakPtr() { return m_ptr; } + WeakLockedPtr<const T> GetWeakPtr() const { return m_ptr; } + SharedLockedPtr<T> GetSharedPtr() { return m_ptr; } + SharedLockedPtr<const T> GetSharedPtr() const { return m_ptr; } + ExclusiveLockedPtr<T> GetExclusivePtr() { return m_ptr; } + ExclusiveLockedPtr<const T> GetExclusivePtr() const { return m_ptr; } + + void InvalidateWeakPtrs() + { + if (ExclusiveLockedPtr<T> ptr = m_ptr) + ptr.set(nullptr); + } + +private: + WeakLockedPtr<T> m_ptr; +}; + +} // namespace base + +#endif // !LOCKED_PTR_H diff --git a/src/core/login_delegate_qt.cpp b/src/core/login_delegate_qt.cpp index 9659b354a..31824ca71 100644 --- a/src/core/login_delegate_qt.cpp +++ b/src/core/login_delegate_qt.cpp @@ -43,7 +43,9 @@ #include "login_delegate_qt.h" +#include "base/task/post_task.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/resource_dispatcher_host.h" @@ -66,14 +68,9 @@ LoginDelegateQt::LoginDelegateQt( : m_authInfo(authInfo) , m_url(url) , m_auth_required_callback(std::move(auth_required_callback)) + , m_webContentsGetter(web_contents_getter) { Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); - - content::BrowserThread::PostTask( - content::BrowserThread::UI, FROM_HERE, - base::Bind(&LoginDelegateQt::triggerDialog, - this, - web_contents_getter)); } LoginDelegateQt::~LoginDelegateQt() @@ -81,6 +78,13 @@ LoginDelegateQt::~LoginDelegateQt() Q_ASSERT(m_dialogController.isNull()); } +void LoginDelegateQt::triggerDialog() +{ + base::PostTaskWithTraits( + FROM_HERE, { content::BrowserThread::UI }, + base::BindOnce(&LoginDelegateQt::triggerDialogOnUI, this)); +} + void LoginDelegateQt::OnRequestCancelled() { destroy(); @@ -107,11 +111,11 @@ bool LoginDelegateQt::isProxy() const return m_authInfo->is_proxy; } -void LoginDelegateQt::triggerDialog(const content::ResourceRequestInfo::WebContentsGetter &webContentsGetter) +void LoginDelegateQt::triggerDialogOnUI() { Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); content::WebContentsImpl *webContents = - static_cast<content::WebContentsImpl *>(webContentsGetter.Run()); + static_cast<content::WebContentsImpl *>(m_webContentsGetter.Run()); if (!webContents) return; WebContentsAdapterClient *client = WebContentsViewQt::from(webContents->GetView())->client(); diff --git a/src/core/login_delegate_qt.h b/src/core/login_delegate_qt.h index 9ce5df843..ccd1f7322 100644 --- a/src/core/login_delegate_qt.h +++ b/src/core/login_delegate_qt.h @@ -66,6 +66,8 @@ public: ~LoginDelegateQt(); + void triggerDialog(); + // LoginDelegate implementation void OnRequestCancelled() override; @@ -77,13 +79,14 @@ public: void sendAuthToRequester(bool success, const QString &user, const QString &password); private: - void triggerDialog(const content::ResourceRequestInfo::WebContentsGetter &); + void triggerDialogOnUI(); void destroy(); scoped_refptr<net::AuthChallengeInfo> m_authInfo; GURL m_url; LoginAuthRequiredCallback m_auth_required_callback; + content::ResourceRequestInfo::WebContentsGetter m_webContentsGetter; // This member is used to keep authentication dialog controller alive until // authorization is sent or cancelled. diff --git a/src/core/media_capture_devices_dispatcher.cpp b/src/core/media_capture_devices_dispatcher.cpp index 2bac62084..a0b19cad3 100644 --- a/src/core/media_capture_devices_dispatcher.cpp +++ b/src/core/media_capture_devices_dispatcher.cpp @@ -49,10 +49,10 @@ #include "web_engine_settings.h" #include "base/strings/utf_string_conversions.h" -#include "chrome/browser/media/webrtc/desktop_streams_registry.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/desktop_media_id.h" +#include "content/public/browser/desktop_streams_registry.h" #include "content/public/browser/media_capture_devices.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_source.h" @@ -95,16 +95,16 @@ void getDevicesForDesktopCapture(content::MediaStreamDevices *devices, content:: DCHECK_CURRENTLY_ON(BrowserThread::UI); // Add selected desktop source to the list. - devices->push_back(content::MediaStreamDevice(content::MEDIA_DESKTOP_VIDEO_CAPTURE, mediaId.ToString(), "Screen")); + devices->push_back(content::MediaStreamDevice(content::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, mediaId.ToString(), "Screen")); if (captureAudio) { if (mediaId.type == content::DesktopMediaID::TYPE_WEB_CONTENTS) { devices->push_back( - content::MediaStreamDevice(content::MEDIA_DESKTOP_AUDIO_CAPTURE, + content::MediaStreamDevice(content::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE, mediaId.ToString(), "Tab audio")); } else { // Use the special loopback device ID for system audio capture. devices->push_back(content::MediaStreamDevice( - content::MEDIA_DESKTOP_AUDIO_CAPTURE, + content::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE, media::AudioDeviceDescription::kLoopbackInputDeviceId, "System Audio")); } @@ -157,12 +157,12 @@ WebContentsAdapterClient::MediaRequestFlags mediaRequestFlagsForRequest(const co if (request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE) requestFlags |= WebContentsAdapterClient::MediaAudioCapture; - else if (request.audio_type == content::MEDIA_DESKTOP_AUDIO_CAPTURE) + else if (request.audio_type == content::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE) requestFlags |= WebContentsAdapterClient::MediaDesktopAudioCapture; if (request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE) requestFlags |= WebContentsAdapterClient::MediaVideoCapture; - else if (request.video_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE) + else if (request.video_type == content::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE) requestFlags |= WebContentsAdapterClient::MediaDesktopVideoCapture; return requestFlags; @@ -233,8 +233,9 @@ void MediaCaptureDevicesDispatcher::handleMediaAccessPermissionResponse(content: // Post a task to process next queued request. It has to be done // asynchronously to make sure that calling infobar is not destroyed until // after this function returns. - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, base::BindOnce(&MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest, base::Unretained(this), webContents)); + base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI}, + base::BindOnce(&MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest, + base::Unretained(this), webContents)); } std::move(callback).Run(devices, devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE : content::MEDIA_DEVICE_OK, @@ -275,12 +276,12 @@ void MediaCaptureDevicesDispatcher::processMediaAccessRequest(WebContentsAdapter DCHECK_CURRENTLY_ON(BrowserThread::UI); // Let's not support tab capture for now. - if (request.video_type == content::MEDIA_TAB_VIDEO_CAPTURE || request.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE) { + if (request.video_type == content::MEDIA_GUM_TAB_VIDEO_CAPTURE || request.audio_type == content::MEDIA_GUM_TAB_AUDIO_CAPTURE) { std::move(callback).Run(content::MediaStreamDevices(), content::MEDIA_DEVICE_NOT_SUPPORTED, std::unique_ptr<content::MediaStreamUI>()); return; } - if (request.video_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE || request.audio_type == content::MEDIA_DESKTOP_AUDIO_CAPTURE) { + if (request.video_type == content::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE || request.audio_type == content::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE) { const bool screenCaptureEnabled = adapterClient->webEngineSettings()->testAttribute(WebEngineSettings::ScreenCaptureEnabled); const bool originIsSecure = content::IsOriginSecure(request.security_origin); @@ -305,7 +306,7 @@ void MediaCaptureDevicesDispatcher::processDesktopCaptureAccessRequest(content:: { content::MediaStreamDevices devices; - if (request.video_type != content::MEDIA_DESKTOP_VIDEO_CAPTURE || request.requested_video_device_id.empty()) { + if (request.video_type != content::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE || request.requested_video_device_id.empty()) { std::move(callback).Run(devices, content::MEDIA_DEVICE_INVALID_STATE, std::unique_ptr<content::MediaStreamUI>()); return; } @@ -319,10 +320,10 @@ void MediaCaptureDevicesDispatcher::processDesktopCaptureAccessRequest(content:: // The extension name that the stream is registered with. std::string originalExtensionName; // Resolve DesktopMediaID for the specified device id. - mediaId = getDesktopStreamsRegistry()->RequestMediaForStreamId( + mediaId = content::DesktopStreamsRegistry::GetInstance()->RequestMediaForStreamId( request.requested_video_device_id, main_frame->GetProcess()->GetID(), main_frame->GetRoutingID(), request.security_origin, - &originalExtensionName); + &originalExtensionName, content::kRegistryStreamTypeDesktop); } // Received invalid device id. @@ -332,7 +333,7 @@ void MediaCaptureDevicesDispatcher::processDesktopCaptureAccessRequest(content:: } // Audio is only supported for screen capture streams. - bool capture_audio = (mediaId.type == content::DesktopMediaID::TYPE_SCREEN && request.audio_type == content::MEDIA_DESKTOP_AUDIO_CAPTURE); + bool capture_audio = (mediaId.type == content::DesktopMediaID::TYPE_SCREEN && request.audio_type == content::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE); getDevicesForDesktopCapture(&devices, mediaId, capture_audio); @@ -390,22 +391,13 @@ void MediaCaptureDevicesDispatcher::getDefaultDevices(const std::string &audioDe } } -DesktopStreamsRegistry *MediaCaptureDevicesDispatcher::getDesktopStreamsRegistry() -{ - if (!m_desktopStreamsRegistry) - m_desktopStreamsRegistry.reset(new DesktopStreamsRegistry()); - return m_desktopStreamsRegistry.get(); -} - void MediaCaptureDevicesDispatcher::OnMediaRequestStateChanged(int render_process_id, int render_frame_id, int page_request_id, const GURL &security_origin, content::MediaStreamType stream_type, content::MediaRequestState state) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind( - &MediaCaptureDevicesDispatcher::updateMediaRequestStateOnUIThread, - base::Unretained(this), render_process_id, render_frame_id, - page_request_id, security_origin, stream_type, state)); + base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI}, + base::BindOnce(&MediaCaptureDevicesDispatcher::updateMediaRequestStateOnUIThread, + base::Unretained(this), render_process_id, render_frame_id, + page_request_id, security_origin, stream_type, state)); } void MediaCaptureDevicesDispatcher::updateMediaRequestStateOnUIThread(int render_process_id, diff --git a/src/core/media_capture_devices_dispatcher.h b/src/core/media_capture_devices_dispatcher.h index 0e5aa38be..cc6e60ede 100644 --- a/src/core/media_capture_devices_dispatcher.h +++ b/src/core/media_capture_devices_dispatcher.h @@ -57,8 +57,6 @@ #include "content/public/browser/web_contents_delegate.h" #include "content/public/common/media_stream_request.h" -class DesktopStreamsRegistry; - namespace QtWebEngineCore { // This singleton is used to receive updates about media events from the content @@ -94,8 +92,6 @@ private: content::MediaStreamType /*stream_type*/, bool /*is_secure*/) override {} - DesktopStreamsRegistry *getDesktopStreamsRegistry(); - friend struct base::DefaultSingletonTraits<MediaCaptureDevicesDispatcher>; typedef base::RepeatingCallback<void(const content::MediaStreamDevices &devices, @@ -129,8 +125,6 @@ private: RequestsQueues m_pendingRequests; - std::unique_ptr<DesktopStreamsRegistry> m_desktopStreamsRegistry; - content::NotificationRegistrar m_notificationsRegistrar; DISALLOW_COPY_AND_ASSIGN(MediaCaptureDevicesDispatcher); diff --git a/src/core/net/cookie_monster_delegate_qt.cpp b/src/core/net/cookie_monster_delegate_qt.cpp index bb89e9e5f..3253b08b9 100644 --- a/src/core/net/cookie_monster_delegate_qt.cpp +++ b/src/core/net/cookie_monster_delegate_qt.cpp @@ -41,6 +41,8 @@ #include "base/bind.h" #include "base/memory/ptr_util.h" +#include "base/task/post_task.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "net/cookies/cookie_util.h" @@ -87,8 +89,8 @@ void CookieMonsterDelegateQt::getAllCookies(quint64 callbackId) net::CookieMonster::GetCookieListCallback callback = base::BindOnce(&CookieMonsterDelegateQt::GetAllCookiesCallbackOnIOThread, this, callbackId); - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::BindOnce(&CookieMonsterDelegateQt::GetAllCookiesOnIOThread, this, std::move(callback))); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&CookieMonsterDelegateQt::GetAllCookiesOnIOThread, this, std::move(callback))); } void CookieMonsterDelegateQt::GetAllCookiesOnIOThread(net::CookieMonster::GetCookieListCallback callback) @@ -108,9 +110,9 @@ void CookieMonsterDelegateQt::setCookie(quint64 callbackId, const QNetworkCookie GURL gurl = origin.isEmpty() ? sourceUrlForCookie(cookie) : toGurl(origin); - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::BindOnce(&CookieMonsterDelegateQt::SetCookieOnIOThread, this, - gurl, cookie.toRawForm().toStdString(), std::move(callback))); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&CookieMonsterDelegateQt::SetCookieOnIOThread, this, + gurl, cookie.toRawForm().toStdString(), std::move(callback))); } void CookieMonsterDelegateQt::SetCookieOnIOThread( @@ -131,9 +133,9 @@ void CookieMonsterDelegateQt::deleteCookie(const QNetworkCookie &cookie, const Q GURL gurl = origin.isEmpty() ? sourceUrlForCookie(cookie) : toGurl(origin); - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::BindOnce(&CookieMonsterDelegateQt::DeleteCookieOnIOThread, this, - gurl, cookie.name().toStdString())); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&CookieMonsterDelegateQt::DeleteCookieOnIOThread, this, + gurl, cookie.name().toStdString())); } void CookieMonsterDelegateQt::DeleteCookieOnIOThread(const GURL& url, const std::string& cookie_name) @@ -149,8 +151,8 @@ void CookieMonsterDelegateQt::deleteSessionCookies(quint64 callbackId) net::CookieMonster::DeleteCallback callback = base::BindOnce(&CookieMonsterDelegateQt::DeleteCookiesCallbackOnIOThread, this, callbackId); - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::BindOnce(&CookieMonsterDelegateQt::DeleteSessionCookiesOnIOThread, this, std::move(callback))); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&CookieMonsterDelegateQt::DeleteSessionCookiesOnIOThread, this, std::move(callback))); } void CookieMonsterDelegateQt::DeleteSessionCookiesOnIOThread(net::CookieMonster::DeleteCallback callback) @@ -166,8 +168,8 @@ void CookieMonsterDelegateQt::deleteAllCookies(quint64 callbackId) net::CookieMonster::DeleteCallback callback = base::BindOnce(&CookieMonsterDelegateQt::DeleteCookiesCallbackOnIOThread, this, callbackId); - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::BindOnce(&CookieMonsterDelegateQt::DeleteAllOnIOThread, this, std::move(callback))); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&CookieMonsterDelegateQt::DeleteAllOnIOThread, this, std::move(callback))); } void CookieMonsterDelegateQt::DeleteAllOnIOThread(net::CookieMonster::DeleteCallback callback) @@ -238,26 +240,23 @@ void CookieMonsterDelegateQt::GetAllCookiesCallbackOnIOThread(qint64 callbackId, for (auto &&cookie : cookies) rawCookies += toQt(cookie).toRawForm() % QByteArrayLiteral("\n"); - content::BrowserThread::PostTask( - content::BrowserThread::UI, - FROM_HERE, - base::BindOnce(&CookieMonsterDelegateQt::GetAllCookiesCallbackOnUIThread, this, callbackId, rawCookies)); + base::PostTaskWithTraits( + FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&CookieMonsterDelegateQt::GetAllCookiesCallbackOnUIThread, this, callbackId, rawCookies)); } void CookieMonsterDelegateQt::SetCookieCallbackOnIOThread(qint64 callbackId, bool success) { - content::BrowserThread::PostTask( - content::BrowserThread::UI, - FROM_HERE, - base::BindOnce(&CookieMonsterDelegateQt::SetCookieCallbackOnUIThread, this, callbackId, success)); + base::PostTaskWithTraits( + FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&CookieMonsterDelegateQt::SetCookieCallbackOnUIThread, this, callbackId, success)); } void CookieMonsterDelegateQt::DeleteCookiesCallbackOnIOThread(qint64 callbackId, uint numCookies) { - content::BrowserThread::PostTask( - content::BrowserThread::UI, - FROM_HERE, - base::BindOnce(&CookieMonsterDelegateQt::DeleteCookiesCallbackOnUIThread, this, callbackId, numCookies)); + base::PostTaskWithTraits( + FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&CookieMonsterDelegateQt::DeleteCookiesCallbackOnUIThread, this, callbackId, numCookies)); } void CookieMonsterDelegateQt::GetAllCookiesCallbackOnUIThread(qint64 callbackId, const QByteArray &cookies) diff --git a/src/core/net/network_delegate_qt.cpp b/src/core/net/network_delegate_qt.cpp index 37309931e..73f3ff818 100644 --- a/src/core/net/network_delegate_qt.cpp +++ b/src/core/net/network_delegate_qt.cpp @@ -39,16 +39,19 @@ #include "network_delegate_qt.h" -#include "profile_adapter.h" +#include "base/task/post_task.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/resource_request_info.h" -#include "cookie_monster_delegate_qt.h" -#include "ui/base/page_transition_types.h" -#include "profile_io_data_qt.h" #include "net/base/load_flags.h" #include "net/url_request/url_request.h" +#include "ui/base/page_transition_types.h" + +#include "profile_adapter.h" +#include "cookie_monster_delegate_qt.h" +#include "profile_io_data_qt.h" #include "qwebengineurlrequestinfo.h" #include "qwebengineurlrequestinfo_p.h" #include "qwebengineurlrequestinterceptor.h" @@ -81,16 +84,19 @@ WebContentsAdapterClient::NavigationType pageTransitionToNavigationType(ui::Page } } -namespace { - -QWebEngineUrlRequestInfo::ResourceType toQt(content::ResourceType resourceType) +static QWebEngineUrlRequestInfo::ResourceType toQt(content::ResourceType resourceType) { if (resourceType >= 0 && resourceType < content::ResourceType(QWebEngineUrlRequestInfo::ResourceTypeLast)) return static_cast<QWebEngineUrlRequestInfo::ResourceType>(resourceType); return QWebEngineUrlRequestInfo::ResourceTypeUnknown; } -QWebEngineUrlRequestInfo::NavigationType toQt(WebContentsAdapterClient::NavigationType navigationType) +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); } @@ -99,26 +105,27 @@ QWebEngineUrlRequestInfo::NavigationType toQt(WebContentsAdapterClient::Navigati class URLRequestNotification { public: URLRequestNotification(net::URLRequest *request, - const QUrl &url, bool isMainFrameRequest, - int navigationType, - int frameTreeNodeId, + GURL *newUrl, + QWebEngineUrlRequestInfo &&requestInfo, + content::ResourceRequestInfo::WebContentsGetter webContentsGetter, net::CompletionOnceCallback callback) : m_request(request) - , m_url(url) , m_isMainFrameRequest(isMainFrameRequest) - , m_navigationType(navigationType) - , m_frameTreeNodeId(frameTreeNodeId) + , 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)); - content::BrowserThread::PostTask( - content::BrowserThread::UI, + base::PostTaskWithTraits( FROM_HERE, - base::Bind(&URLRequestNotification::notify, base::Unretained(this))); + {content::BrowserThread::UI}, + base::BindOnce(&URLRequestNotification::notify, base::Unretained(this))); } private: @@ -148,31 +155,42 @@ private: // May run concurrently with cancel() so no peeking at m_request here. int error = net::OK; - content::WebContents *webContents = content::WebContents::FromFrameTreeNodeId(m_frameTreeNodeId); + content::WebContents *webContents = m_webContentsGetter.Run(); + if (webContents) { - int navigationRequestAction = WebContentsAdapterClient::AcceptRequest; WebContentsAdapterClient *client = - WebContentsViewQt::from(static_cast<content::WebContentsImpl*>(webContents)->GetView())->client(); - client->navigationRequested(m_navigationType, - m_url, - 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; + 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); } - DCHECK(error != net::ERR_FAILED); } // Run the callback on the IO thread. - content::BrowserThread::PostTask( - content::BrowserThread::IO, + base::PostTaskWithTraits( FROM_HERE, + {content::BrowserThread::IO}, base::BindOnce(&URLRequestNotification::complete, base::Unretained(this), error)); } @@ -181,6 +199,17 @@ private: 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); @@ -192,17 +221,16 @@ private: ~URLRequestNotification() {} net::URLRequest *m_request; - QUrl m_url; bool m_isMainFrameRequest; - int m_navigationType; - int m_frameTreeNodeId; + 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"; -} // namespace - NetworkDelegateQt::NetworkDelegateQt(ProfileIODataQt *data) : m_profileIOData(data) { @@ -212,7 +240,6 @@ int NetworkDelegateQt::OnBeforeURLRequest(net::URLRequest *request, net::Complet { Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); Q_ASSERT(m_profileIOData); - const content::ResourceRequestInfo *resourceInfo = content::ResourceRequestInfo::ForRequest(request); content::ResourceType resourceType = content::RESOURCE_TYPE_LAST_TYPE; @@ -231,14 +258,14 @@ int NetworkDelegateQt::OnBeforeURLRequest(net::URLRequest *request, net::Complet else firstPartyUrl = toQt(request->site_for_cookies()); - QWebEngineUrlRequestInterceptor* interceptor = m_profileIOData->acquireInterceptor(); - if (interceptor) { - QWebEngineUrlRequestInfoPrivate *infoPrivate = new QWebEngineUrlRequestInfoPrivate(toQt(resourceType), - toQt(navigationType), - qUrl, - firstPartyUrl, - QByteArray::fromStdString(request->method())); - QWebEngineUrlRequestInfo requestInfo(infoPrivate); + QWebEngineUrlRequestInfoPrivate *infoPrivate = new QWebEngineUrlRequestInfoPrivate(toQt(resourceType), + toQt(navigationType), + qUrl, + firstPartyUrl, + QByteArray::fromStdString(request->method())); + QWebEngineUrlRequestInfo requestInfo(infoPrivate); + + if (QWebEngineUrlRequestInterceptor* interceptor = m_profileIOData->acquireInterceptor()) { interceptor->interceptRequest(requestInfo); m_profileIOData->releaseInterceptor(); if (requestInfo.changed()) { @@ -255,6 +282,8 @@ int NetworkDelegateQt::OnBeforeURLRequest(net::URLRequest *request, net::Complet if (result != net::OK) return result; + + requestInfo.resetChanged(); } } else m_profileIOData->releaseInterceptor(); @@ -262,17 +291,16 @@ int NetworkDelegateQt::OnBeforeURLRequest(net::URLRequest *request, net::Complet if (!resourceInfo) return net::OK; - int frameTreeNodeId = resourceInfo->GetFrameTreeNodeId(); - // Only intercept MAIN_FRAME and SUB_FRAME with an associated render frame. - if (!content::IsResourceTypeFrame(resourceType) || frameTreeNodeId == -1) + if (!m_profileIOData->hasPageInterceptors() && !content::IsResourceTypeFrame(resourceType)) return net::OK; + auto webContentsGetter = resourceInfo->GetWebContentsGetterForRequest(); new URLRequestNotification( request, - qUrl, resourceInfo->IsMainFrame(), - navigationType, - frameTreeNodeId, + newUrl, + std::move(requestInfo), + webContentsGetter, std::move(callback) ); @@ -290,13 +318,18 @@ void NetworkDelegateQt::OnCompleted(net::URLRequest */*request*/, bool /*started bool NetworkDelegateQt::OnCanSetCookie(const net::URLRequest& request, const net::CanonicalCookie & /*cookie*/, - net::CookieOptions*) + net::CookieOptions*, + bool allowedFromCaller) { + if (!allowedFromCaller) + return false; return canSetCookies(request.site_for_cookies(), request.url(), std::string()); } -bool NetworkDelegateQt::OnCanGetCookies(const net::URLRequest& request, const net::CookieList&) +bool NetworkDelegateQt::OnCanGetCookies(const net::URLRequest& request, const net::CookieList&, bool allowedFromCaller) { + if (!allowedFromCaller) + return false; return canGetCookies(request.site_for_cookies(), request.url()); } diff --git a/src/core/net/network_delegate_qt.h b/src/core/net/network_delegate_qt.h index e4ff196aa..842af5006 100644 --- a/src/core/net/network_delegate_qt.h +++ b/src/core/net/network_delegate_qt.h @@ -62,7 +62,7 @@ public: // net::NetworkDelegate implementation int OnBeforeURLRequest(net::URLRequest* request, net::CompletionOnceCallback callback, GURL* new_url) override; void OnURLRequestDestroyed(net::URLRequest* request) override; - bool OnCanSetCookie(const net::URLRequest& request, const net::CanonicalCookie& cookie, net::CookieOptions* options) override; + bool OnCanSetCookie(const net::URLRequest& request, const net::CanonicalCookie& cookie, net::CookieOptions* options, bool) override; int OnBeforeStartTransaction(net::URLRequest *request, const net::CompletionOnceCallback callback, net::HttpRequestHeaders *headers) override; void OnBeforeSendHeaders(net::URLRequest* request, const net::ProxyInfo& proxy_info, const net::ProxyRetryInfoMap& proxy_retry_info, net::HttpRequestHeaders* headers) override; @@ -75,7 +75,7 @@ public: void OnCompleted(net::URLRequest *request, bool started, int net_error) override; void OnPACScriptError(int, const base::string16&) override; net::NetworkDelegate::AuthRequiredResponse OnAuthRequired(net::URLRequest*, const net::AuthChallengeInfo&, AuthCallback, net::AuthCredentials*) override; - bool OnCanGetCookies(const net::URLRequest&, const net::CookieList&) override; + bool OnCanGetCookies(const net::URLRequest&, const net::CookieList&, bool) override; bool OnCanAccessFile(const net::URLRequest&, const base::FilePath&, const base::FilePath&) const override; bool OnCanEnablePrivacyMode(const GURL&, const GURL&) const override; bool OnAreExperimentalCookieFeaturesEnabled() const override; diff --git a/src/core/net/proxy_config_service_qt.h b/src/core/net/proxy_config_service_qt.h index dcd303894..961927b89 100644 --- a/src/core/net/proxy_config_service_qt.h +++ b/src/core/net/proxy_config_service_qt.h @@ -76,7 +76,7 @@ private: void RegisterObserver(); std::unique_ptr<net::ProxyConfigService> m_baseService; - base::ObserverList<net::ProxyConfigService::Observer, true> m_observers; + base::ObserverList<net::ProxyConfigService::Observer, true>::Unchecked m_observers; // Keep the last state around. bool m_usesSystemConfiguration; diff --git a/src/core/net/qrc_url_scheme_handler.cpp b/src/core/net/qrc_url_scheme_handler.cpp new file mode 100644 index 000000000..74a77a7ec --- /dev/null +++ b/src/core/net/qrc_url_scheme_handler.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** 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 "qrc_url_scheme_handler.h" + +#include <QtWebEngineCore/qwebengineurlrequestjob.h> + +#include <QFile> +#include <QFileInfo> +#include <QMimeDatabase> +#include <QMimeType> + +namespace QtWebEngineCore { + +void QrcUrlSchemeHandler::requestStarted(QWebEngineUrlRequestJob *job) +{ + QByteArray requestMethod = job->requestMethod(); + if (requestMethod != "GET") { + job->fail(QWebEngineUrlRequestJob::RequestDenied); + return; + } + + QUrl requestUrl = job->requestUrl(); + QString requestPath = requestUrl.path(); + QScopedPointer<QFile> file(new QFile(':' + requestPath, job)); + QFileInfo fileInfo(*file); + QMimeDatabase mimeDatabase; + QMimeType mimeType = mimeDatabase.mimeTypeForFile(fileInfo); + job->reply(mimeType.name().toUtf8(), file.take()); +} + +} // namespace QtWebEngineCore diff --git a/src/core/net/qrc_url_scheme_handler.h b/src/core/net/qrc_url_scheme_handler.h new file mode 100644 index 000000000..f6ca92879 --- /dev/null +++ b/src/core/net/qrc_url_scheme_handler.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRC_URL_SCHEME_HANDLER_H +#define QRC_URL_SCHEME_HANDLER_H + +#include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h> +#include <QtWebEngineCore/qwebengineurlschemehandler.h> + +namespace QtWebEngineCore { + +class QrcUrlSchemeHandler final : public QWebEngineUrlSchemeHandler { +public: + void requestStarted(QWebEngineUrlRequestJob *) override; +}; + +} // namespace QtWebEngineCore + +#endif // !QRC_URL_SCHEME_HANDLER_H diff --git a/src/core/net/url_request_context_getter_qt.cpp b/src/core/net/url_request_context_getter_qt.cpp index 636d27358..6081a5e9f 100644 --- a/src/core/net/url_request_context_getter_qt.cpp +++ b/src/core/net/url_request_context_getter_qt.cpp @@ -40,6 +40,10 @@ #include "url_request_context_getter_qt.h" #include "profile_io_data_qt.h" +#include "base/task/post_task.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" + namespace QtWebEngineCore { URLRequestContextGetterQt::URLRequestContextGetterQt(ProfileIODataQt *data) @@ -59,7 +63,7 @@ net::URLRequestContext *URLRequestContextGetterQt::GetURLRequestContext() scoped_refptr<base::SingleThreadTaskRunner> URLRequestContextGetterQt::GetNetworkTaskRunner() const { - return content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::IO); + return base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::IO}); } } // namespace QtWebEngineCore diff --git a/src/core/net/url_request_custom_job.cpp b/src/core/net/url_request_custom_job.cpp index d371c7bff..cba9b4dc5 100644 --- a/src/core/net/url_request_custom_job.cpp +++ b/src/core/net/url_request_custom_job.cpp @@ -39,6 +39,9 @@ #include "url_request_custom_job.h" #include "url_request_custom_job_proxy.h" + +#include "base/task/post_task.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "net/base/io_buffer.h" @@ -68,17 +71,30 @@ URLRequestCustomJob::~URLRequestCustomJob() if (m_device && m_device->isOpen()) m_device->close(); m_device = nullptr; - content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, - base::Bind(&URLRequestCustomJobProxy::release, - m_proxy)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&URLRequestCustomJobProxy::release, m_proxy)); } void URLRequestCustomJob::Start() { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, - base::Bind(&URLRequestCustomJobProxy::initialize, - m_proxy, request()->url(), request()->method(), request()->initiator())); + HttpRequestHeaders requestHeaders = request()->extra_request_headers(); + std::map<std::string, std::string> headers; + net::HttpRequestHeaders::Iterator it(requestHeaders); + while (it.GetNext()) + headers.emplace(it.name(), it.value()); + if (!request()->referrer().empty()) + headers.emplace("Referer", request()->referrer()); + + // TODO: handle UploadDataStream, for instance using a QIODevice wrapper. + + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&URLRequestCustomJobProxy::initialize, + m_proxy, + request()->url(), + request()->method(), + request()->initiator(), + std::move(headers))); } void URLRequestCustomJob::Kill() @@ -94,9 +110,9 @@ void URLRequestCustomJob::Kill() m_pendingReadPos = 0; } m_device = nullptr; - content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, - base::Bind(&URLRequestCustomJobProxy::release, - m_proxy)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&URLRequestCustomJobProxy::release, + m_proxy)); URLRequestJob::Kill(); } diff --git a/src/core/net/url_request_custom_job_delegate.cpp b/src/core/net/url_request_custom_job_delegate.cpp index 338bd7137..83d47e291 100644 --- a/src/core/net/url_request_custom_job_delegate.cpp +++ b/src/core/net/url_request_custom_job_delegate.cpp @@ -40,9 +40,12 @@ #include "url_request_custom_job_delegate.h" #include "url_request_custom_job_proxy.h" -#include "type_conversion.h" -#include "net/base/net_errors.h" +#include "base/task/post_task.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" +#include "net/base/net_errors.h" + +#include "type_conversion.h" #include <QByteArray> @@ -51,11 +54,13 @@ namespace QtWebEngineCore { URLRequestCustomJobDelegate::URLRequestCustomJobDelegate(URLRequestCustomJobProxy *proxy, const QUrl &url, const QByteArray &method, - const QUrl &initiatorOrigin) + const QUrl &initiatorOrigin, + const QMap<QByteArray, QByteArray> &headers) : m_proxy(proxy), m_request(url), m_method(method), - m_initiatorOrigin(initiatorOrigin) + m_initiatorOrigin(initiatorOrigin), + m_requestHeaders(headers) { } @@ -78,32 +83,36 @@ QUrl URLRequestCustomJobDelegate::initiator() const return m_initiatorOrigin; } +const QMap<QByteArray, QByteArray> &URLRequestCustomJobDelegate::requestHeaders() const +{ + return m_requestHeaders; +} + void URLRequestCustomJobDelegate::reply(const QByteArray &contentType, QIODevice *device) { if (device) QObject::connect(device, &QIODevice::readyRead, this, &URLRequestCustomJobDelegate::slotReadyRead); - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::Bind(&URLRequestCustomJobProxy::reply, - m_proxy,contentType.toStdString(),device)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&URLRequestCustomJobProxy::reply, + m_proxy,contentType.toStdString(),device)); } void URLRequestCustomJobDelegate::slotReadyRead() { - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::Bind(&URLRequestCustomJobProxy::readyRead, m_proxy)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&URLRequestCustomJobProxy::readyRead, m_proxy)); } void URLRequestCustomJobDelegate::abort() { - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::Bind(&URLRequestCustomJobProxy::abort, m_proxy)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&URLRequestCustomJobProxy::abort, m_proxy)); } void URLRequestCustomJobDelegate::redirect(const QUrl &url) { - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::Bind(&URLRequestCustomJobProxy::redirect, - m_proxy, toGurl(url))); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&URLRequestCustomJobProxy::redirect, m_proxy, toGurl(url))); } void URLRequestCustomJobDelegate::fail(Error error) @@ -129,9 +138,8 @@ void URLRequestCustomJobDelegate::fail(Error error) break; } if (net_error) { - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::Bind(&URLRequestCustomJobProxy::fail, - m_proxy, net_error)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&URLRequestCustomJobProxy::fail, m_proxy, net_error)); } } diff --git a/src/core/net/url_request_custom_job_delegate.h b/src/core/net/url_request_custom_job_delegate.h index caabfcf99..9de0224f9 100644 --- a/src/core/net/url_request_custom_job_delegate.h +++ b/src/core/net/url_request_custom_job_delegate.h @@ -54,6 +54,7 @@ #include "base/memory/ref_counted.h" #include "qtwebenginecoreglobal_p.h" +#include <QMap> #include <QObject> #include <QUrl> @@ -80,6 +81,7 @@ public: QUrl url() const; QByteArray method() const; QUrl initiator() const; + const QMap<QByteArray, QByteArray> &requestHeaders() const; void reply(const QByteArray &contentType, QIODevice *device); void redirect(const QUrl& url); @@ -93,13 +95,15 @@ private: URLRequestCustomJobDelegate(URLRequestCustomJobProxy *proxy, const QUrl &url, const QByteArray &method, - const QUrl &initiatorOrigin); + const QUrl &initiatorOrigin, + const QMap<QByteArray, QByteArray> &requestHeaders); friend class URLRequestCustomJobProxy; scoped_refptr<URLRequestCustomJobProxy> m_proxy; QUrl m_request; QByteArray m_method; QUrl m_initiatorOrigin; + const QMap<QByteArray, QByteArray> m_requestHeaders; }; } // namespace diff --git a/src/core/net/url_request_custom_job_proxy.cpp b/src/core/net/url_request_custom_job_proxy.cpp index b5f10388c..72d14450e 100644 --- a/src/core/net/url_request_custom_job_proxy.cpp +++ b/src/core/net/url_request_custom_job_proxy.cpp @@ -152,7 +152,9 @@ void URLRequestCustomJobProxy::readyRead() m_job->notifyReadyRead(); } -void URLRequestCustomJobProxy::initialize(GURL url, std::string method, base::Optional<url::Origin> initiator) +void URLRequestCustomJobProxy::initialize(GURL url, std::string method, + base::Optional<url::Origin> initiator, + std::map<std::string, std::string> headers) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); Q_ASSERT(!m_delegate); @@ -164,12 +166,16 @@ void URLRequestCustomJobProxy::initialize(GURL url, std::string method, base::Op QWebEngineUrlSchemeHandler *schemeHandler = nullptr; if (m_profileAdapter) - schemeHandler = m_profileAdapter->customUrlSchemeHandlers()[toQByteArray(m_scheme)]; + schemeHandler = m_profileAdapter->urlSchemeHandler(toQByteArray(m_scheme)); + QMap<QByteArray, QByteArray> qHeaders; + for (auto it = headers.cbegin(); it != headers.cend(); ++it) + qHeaders.insert(toQByteArray(it->first), toQByteArray(it->second)); if (schemeHandler) { m_delegate = new URLRequestCustomJobDelegate(this, toQt(url), QByteArray::fromStdString(method), - initiatorOrigin); + initiatorOrigin, + qHeaders); QWebEngineUrlRequestJob *requestJob = new QWebEngineUrlRequestJob(m_delegate); schemeHandler->requestStarted(requestJob); } diff --git a/src/core/net/url_request_custom_job_proxy.h b/src/core/net/url_request_custom_job_proxy.h index 3986fe119..aa55db07c 100644 --- a/src/core/net/url_request_custom_job_proxy.h +++ b/src/core/net/url_request_custom_job_proxy.h @@ -72,7 +72,7 @@ public: void abort(); void fail(int error); void release(); - void initialize(GURL url, std::string method, base::Optional<url::Origin> initiatorOrigin); + void initialize(GURL url, std::string method, base::Optional<url::Origin> initiatorOrigin, std::map<std::string, std::string> headers); void readyRead(); // IO thread owned: diff --git a/src/core/net/url_request_qrc_job_qt.cpp b/src/core/net/url_request_qrc_job_qt.cpp deleted file mode 100644 index a2712653d..000000000 --- a/src/core/net/url_request_qrc_job_qt.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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_qrc_job_qt.h" - -#include "type_conversion.h" - -#include "base/pending_task.h" -#include "base/threading/thread_task_runner_handle.h" -#include "net/base/net_errors.h" -#include "net/base/io_buffer.h" - -#include <QUrl> -#include <QFileInfo> -#include <QMimeDatabase> -#include <QMimeType> - -using namespace net; -namespace QtWebEngineCore { - -URLRequestQrcJobQt::URLRequestQrcJobQt(URLRequest *request, NetworkDelegate *networkDelegate) - : URLRequestJob(request, networkDelegate) - , m_remainingBytes(0) - , m_weakFactory(this) -{ -} - -URLRequestQrcJobQt::~URLRequestQrcJobQt() -{ - if (m_file.isOpen()) - m_file.close(); -} - -void URLRequestQrcJobQt::Start() -{ - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::Bind(&URLRequestQrcJobQt::startGetHead, m_weakFactory.GetWeakPtr())); -} - -void URLRequestQrcJobQt::Kill() -{ - if (m_file.isOpen()) - m_file.close(); - m_weakFactory.InvalidateWeakPtrs(); - - URLRequestJob::Kill(); -} - -bool URLRequestQrcJobQt::GetMimeType(std::string *mimeType) const -{ - DCHECK(request_); - if (m_mimeType.size() > 0) { - *mimeType = m_mimeType; - return true; - } - return false; -} - -int URLRequestQrcJobQt::ReadRawData(IOBuffer *buf, int bufSize) -{ - DCHECK_GE(m_remainingBytes, 0); - // File has been read finished. - if (!m_remainingBytes || !bufSize) { - return 0; - } - if (m_remainingBytes < bufSize) - bufSize = static_cast<int>(m_remainingBytes); - qint64 rv = m_file.read(buf->data(), bufSize); - if (rv >= 0) { - m_remainingBytes -= rv; - DCHECK_GE(m_remainingBytes, 0); - return static_cast<int>(rv); - } - return static_cast<int>(rv); -} - -void URLRequestQrcJobQt::startGetHead() -{ - // Get qrc file path. - QString qrcFilePath = ':' + toQt(request_->url()).path(); - m_file.setFileName(qrcFilePath); - QFileInfo qrcFileInfo(m_file); - // Get qrc file mime type. - QMimeDatabase mimeDatabase; - QMimeType mimeType = mimeDatabase.mimeTypeForFile(qrcFileInfo); - m_mimeType = mimeType.name().toStdString(); - // Open file - if (m_file.open(QIODevice::ReadOnly)) { - m_remainingBytes = m_file.size(); - set_expected_content_size(m_remainingBytes); - // Notify that the headers are complete - NotifyHeadersComplete(); - } else { - NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, ERR_INVALID_URL)); - } -} - -} // namespace QtWebEngineCore diff --git a/src/core/net/webui_controller_factory_qt.cpp b/src/core/net/webui_controller_factory_qt.cpp index 918500b58..ec36e70d9 100644 --- a/src/core/net/webui_controller_factory_qt.cpp +++ b/src/core/net/webui_controller_factory_qt.cpp @@ -48,6 +48,7 @@ #include "base/location.h" #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" +#include "chrome/browser/accessibility/accessibility_ui.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/webui/devtools_ui.h" #include "chrome/browser/ui/webui/quota_internals/quota_internals_ui.h" @@ -136,6 +137,8 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI *web_ui, Profile *profile, co // return nullptr; return &NewWebUI<DevToolsUI>; } + if (url.host() == chrome::kChromeUIAccessibilityHost) + return &NewWebUI<AccessibilityUI>; // if (url.host_piece() == chrome::kChromeUIUserActionsHost) // return &NewWebUI<UserActionsUI>; diff --git a/src/core/ozone/gl_context_qt.cpp b/src/core/ozone/gl_context_qt.cpp index ec49414b0..7e913817e 100644 --- a/src/core/ozone/gl_context_qt.cpp +++ b/src/core/ozone/gl_context_qt.cpp @@ -119,7 +119,13 @@ void* GLContextHelper::getGlXConfig() void* GLContextHelper::getEGLDisplay() { +#ifdef Q_OS_WIN + // Windows QPA plugin does not implement resourceForIntegration for "egldisplay". + // Use resourceForContext instead. + return resourceForContext(QByteArrayLiteral("egldisplay")); +#else return resourceForIntegration(QByteArrayLiteral("egldisplay")); +#endif } void* GLContextHelper::getXDisplay() diff --git a/src/core/ozone/gl_surface_egl_qt.cpp b/src/core/ozone/gl_surface_egl_qt.cpp index 9fe5985ce..8715a5095 100644 --- a/src/core/ozone/gl_surface_egl_qt.cpp +++ b/src/core/ozone/gl_surface_egl_qt.cpp @@ -44,9 +44,9 @@ #include "gl_context_qt.h" #include "ozone/gl_surface_egl_qt.h" -#include "ui/gl/gl_surface_egl.h" #if !defined(OS_MACOSX) #include "ui/gl/egl_util.h" +#include "ui/gl/gl_surface_egl.h" #include "ui/gl/init/gl_factory.h" // From ANGLE's egl/eglext.h. @@ -302,10 +302,6 @@ void* GLSurfacelessQtEGL::GetShareHandle() return NULL; } -} // namespace gl -#endif // !defined(OS_MACOSX) - -namespace gl { std::string DriverEGL::GetPlatformExtensions() { EGLDisplay display = GLContextHelper::getEGLDisplay(); @@ -317,3 +313,15 @@ std::string DriverEGL::GetPlatformExtensions() return str ? std::string(str) : ""; } } // namespace gl +#else +namespace gl { +struct GL_EXPORT DriverEGL { + static std::string GetPlatformExtensions(); +}; + +std::string DriverEGL::GetPlatformExtensions() +{ + return ""; +} +} // namespace gl +#endif // !defined(OS_MACOSX) diff --git a/src/core/ozone/gl_surface_qt.cpp b/src/core/ozone/gl_surface_qt.cpp index 7cde289ae..551ba888c 100644 --- a/src/core/ozone/gl_surface_qt.cpp +++ b/src/core/ozone/gl_surface_qt.cpp @@ -64,7 +64,6 @@ #include "ozone/gl_surface_wgl_qt.h" #include "gpu/ipc/service/direct_composition_surface_win.h" -#include "ui/gl/gl_context_wgl.h" #include "ui/gl/vsync_provider_win.h" #endif @@ -141,9 +140,6 @@ bool InitializeGLOneOffPlatform() { VSyncProviderWin::InitializeOneOff(); - if (GetGLImplementation() == kGLImplementationOSMesaGL) - return false; - if (GetGLImplementation() == kGLImplementationEGLGLES2) return GLSurfaceEGLQt::InitializeOneOff(); diff --git a/src/core/ozone/gl_surface_wgl_qt.cpp b/src/core/ozone/gl_surface_wgl_qt.cpp index 7c9e87b86..ac27a9c20 100644 --- a/src/core/ozone/gl_surface_wgl_qt.cpp +++ b/src/core/ozone/gl_surface_wgl_qt.cpp @@ -37,9 +37,9 @@ ** ****************************************************************************/ -#if defined(OS_WIN) - #include "gl_surface_wgl_qt.h" + +#if defined(OS_WIN) #include "ui/gl/gl_surface_wgl.h" namespace gl { diff --git a/src/core/ozone/ozone_platform_qt.cpp b/src/core/ozone/ozone_platform_qt.cpp index 905e8f403..1115f3fac 100644 --- a/src/core/ozone/ozone_platform_qt.cpp +++ b/src/core/ozone/ozone_platform_qt.cpp @@ -43,6 +43,7 @@ #include "ozone/surface_factory_qt.h" #include "ozone/platform_window_qt.h" #include "ui/display/types/native_display_delegate.h" +#include "ui/events/system_input_injector.h" #include "ui/ozone/common/stub_client_native_pixmap_factory.h" #include "ui/ozone/common/stub_overlay_manager.h" #include "ui/ozone/public/cursor_factory_ozone.h" diff --git a/src/core/ozone/platform_window_qt.h b/src/core/ozone/platform_window_qt.h index b712b706a..bb2fc714b 100644 --- a/src/core/ozone/platform_window_qt.h +++ b/src/core/ozone/platform_window_qt.h @@ -75,6 +75,9 @@ public: void MoveCursorTo(const gfx::Point&) override { } void ConfineCursorToBounds(const gfx::Rect&) override { } PlatformImeController* GetPlatformImeController() override { return nullptr; } + void SetRestoredBoundsInPixels(const gfx::Rect& bounds) override { } + gfx::Rect GetRestoredBoundsInPixels() const override { return gfx::Rect(); } + // PlatformEventDispatcher: bool CanDispatchEvent(const PlatformEvent& event) override; uint32_t DispatchEvent(const PlatformEvent& event) override; diff --git a/src/core/permission_manager_qt.cpp b/src/core/permission_manager_qt.cpp index b999f4186..cf3041e7a 100644 --- a/src/core/permission_manager_qt.cpp +++ b/src/core/permission_manager_qt.cpp @@ -76,6 +76,7 @@ ProfileAdapter::PermissionType toQt(content::PermissionType type) case content::PermissionType::CLIPBOARD_WRITE: return ProfileAdapter::ClipboardWrite; case content::PermissionType::PAYMENT_HANDLER: + case content::PermissionType::BACKGROUND_FETCH: case content::PermissionType::NUM: break; } @@ -294,8 +295,8 @@ void PermissionManagerQt::ResetPermission( int PermissionManagerQt::SubscribePermissionStatusChange( content::PermissionType permission, + content::RenderFrameHost * /* render_frame_host */, const GURL& requesting_origin, - const GURL& /*embedding_origin*/, const base::Callback<void(blink::mojom::PermissionStatus)>& callback) { int subscriber_id = ++m_subscriberIdCount; @@ -311,7 +312,7 @@ int PermissionManagerQt::SubscribePermissionStatusChange( void PermissionManagerQt::UnsubscribePermissionStatusChange(int subscription_id) { if (!m_subscribers.remove(subscription_id)) - qWarning() << "PermissionManagerQt::UnsubscribePermissionStatusChange called on unknown subscription id" << subscription_id; + LOG(WARNING) << "PermissionManagerQt::UnsubscribePermissionStatusChange called on unknown subscription id" << subscription_id; } } // namespace QtWebEngineCore diff --git a/src/core/permission_manager_qt.h b/src/core/permission_manager_qt.h index b3bd3dc7a..89eb6cf85 100644 --- a/src/core/permission_manager_qt.h +++ b/src/core/permission_manager_qt.h @@ -92,8 +92,8 @@ public: int SubscribePermissionStatusChange( content::PermissionType permission, + content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, - const GURL& embedding_origin, const base::Callback<void(blink::mojom::PermissionStatus)>& callback) override; void UnsubscribePermissionStatusChange(int subscription_id) override; diff --git a/src/core/printing/print_view_manager_base_qt.cpp b/src/core/printing/print_view_manager_base_qt.cpp index 0e7239ef8..1a16be69e 100644 --- a/src/core/printing/print_view_manager_base_qt.cpp +++ b/src/core/printing/print_view_manager_base_qt.cpp @@ -52,6 +52,7 @@ #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" +#include "base/task/post_task.h" #include "base/timer/timer.h" #include "base/values.h" #include "chrome/browser/chrome_notification_types.h" @@ -59,12 +60,13 @@ #include "chrome/browser/printing/print_job_manager.h" #include "chrome/browser/printing/printer_query.h" #include "components/printing/common/print_messages.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" -#include "printing/pdf_metafile_skia.h" +#include "printing/metafile_skia.h" #include "printing/print_job_constants.h" #include "printing/printed_document.h" @@ -123,8 +125,8 @@ void PrintViewManagerBaseQt::PrintDocument(printing::PrintedDocument *document, const gfx::Rect &content_area, const gfx::Point &offsets) { - std::unique_ptr<printing::PdfMetafileSkia> metafile = - std::make_unique<printing::PdfMetafileSkia>(); + std::unique_ptr<printing::MetafileSkia> metafile = + std::make_unique<printing::MetafileSkia>(); CHECK(metafile->InitFromData(print_data->front(), print_data->size())); // Update the rendered document. It will send notifications to the listener. @@ -155,21 +157,19 @@ void PrintViewManagerBaseQt::OnDidPrintDocument(content::RenderFrameHost* /*rend return; const PrintHostMsg_DidPrintContent_Params &content = params.content; - if (!base::SharedMemory::IsHandleValid(content.metafile_data_handle)) { + if (!content.metafile_data_region.IsValid()) { NOTREACHED() << "invalid memory handle"; web_contents()->Stop(); return; } - std::unique_ptr<base::SharedMemory> shared_buf = - std::make_unique<base::SharedMemory>(content.metafile_data_handle, true); - if (!shared_buf->Map(content.data_size)) { + auto data = base::RefCountedSharedMemoryMapping::CreateFromWholeRegion(content.metafile_data_region); + if (!data) { NOTREACHED() << "couldn't map"; web_contents()->Stop(); return; } - auto data = base::MakeRefCounted<base::RefCountedSharedMemory>( - std::move(shared_buf), content.data_size); + PrintDocument(document, data, params.page_size, params.content_area, params.physical_offsets); } @@ -516,9 +516,8 @@ void PrintViewManagerBaseQt::ReleasePrinterQuery() printerQuery = m_printerQueriesQueue->PopPrinterQuery(cookie); if (!printerQuery.get()) return; - content::BrowserThread::PostTask( - content::BrowserThread::IO, FROM_HERE, - base::BindOnce(&printing::PrinterQuery::StopWorker, printerQuery.get())); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&printing::PrinterQuery::StopWorker, printerQuery.get())); } // Originally from print_preview_message_handler.cc: @@ -528,7 +527,7 @@ void PrintViewManagerBaseQt::StopWorker(int documentCookie) { scoped_refptr<printing::PrinterQuery> printer_query = m_printerQueriesQueue->PopPrinterQuery(documentCookie); if (printer_query.get()) { - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, base::BindOnce(&printing::PrinterQuery::StopWorker, printer_query)); } } diff --git a/src/core/printing/print_view_manager_qt.cpp b/src/core/printing/print_view_manager_qt.cpp index ff9fc76be..e88f48624 100644 --- a/src/core/printing/print_view_manager_qt.cpp +++ b/src/core/printing/print_view_manager_qt.cpp @@ -53,50 +53,43 @@ #include "base/values.h" #include "base/memory/ref_counted_memory.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "chrome/browser/printing/print_job_manager.h" #include "chrome/browser/printing/printer_query.h" #include "components/printing/common/print_messages.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_frame_host.h" #include "content/public/common/web_preferences.h" -#include "printing/pdf_metafile_skia.h" +#include "printing/metafile_skia.h" #include "printing/print_job_constants.h" #include "printing/units.h" -DEFINE_WEB_CONTENTS_USER_DATA_KEY(QtWebEngineCore::PrintViewManagerQt); - namespace { static const qreal kMicronsToMillimeter = 1000.0f; -static std::vector<char> -GetStdVectorFromHandle(base::SharedMemoryHandle handle, uint32_t data_size) +static QSharedPointer<QByteArray> GetStdVectorFromHandle(const base::ReadOnlySharedMemoryRegion &handle) { - std::unique_ptr<base::SharedMemory> shared_buf( - new base::SharedMemory(handle, true)); - - if (!shared_buf->Map(data_size)) { - return std::vector<char>(); - } + base::ReadOnlySharedMemoryMapping map = handle.Map(); + if (!map.IsValid()) + return QSharedPointer<QByteArray>(new QByteArray); - char* data = static_cast<char*>(shared_buf->memory()); - return std::vector<char>(data, data + data_size); + const char* data = static_cast<const char*>(map.memory()); + return QSharedPointer<QByteArray>(new QByteArray(data, map.size())); } static scoped_refptr<base::RefCountedBytes> -GetBytesFromHandle(base::SharedMemoryHandle handle, uint32_t data_size) +GetBytesFromHandle(const base::ReadOnlySharedMemoryRegion &handle) { - std::unique_ptr<base::SharedMemory> shared_buf(new base::SharedMemory(handle, true)); - - if (!shared_buf->Map(data_size)) { + base::ReadOnlySharedMemoryMapping map = handle.Map(); + if (!map.IsValid()) return nullptr; - } - unsigned char* data = static_cast<unsigned char*>(shared_buf->memory()); - std::vector<unsigned char> dataVector(data, data + data_size); + const unsigned char* data = static_cast<const unsigned char*>(map.memory()); + std::vector<unsigned char> dataVector(data, data + map.size()); return base::RefCountedBytes::TakeVector(&dataVector); } @@ -108,15 +101,14 @@ static void SavePdfFile(scoped_refptr<base::RefCountedBytes> data, base::AssertBlockingAllowed(); DCHECK_GT(data->size(), 0U); - printing::PdfMetafileSkia metafile; + printing::MetafileSkia metafile; metafile.InitFromData(static_cast<const void*>(data->front()), data->size()); base::File file(path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); bool success = file.IsValid() && metafile.SaveTo(&file); - content::BrowserThread::PostTask(content::BrowserThread::UI, - FROM_HERE, - base::Bind(saveCallback, success)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(saveCallback, success)); } static base::DictionaryValue *createPrintSettings() @@ -222,16 +214,16 @@ void PrintViewManagerQt::PrintToPDFFileWithCallback(const QPageLayout &pageLayou return; if (m_printSettings || !filePath.length()) { - content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, - base::Bind(callback, false)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(callback, false)); return; } m_pdfOutputPath = toFilePath(filePath); m_pdfSaveCallback = callback; if (!PrintToPDFInternal(pageLayout, printInColor)) { - content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, - base::Bind(callback, false)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(callback, false)); resetPdfState(); } } @@ -246,16 +238,15 @@ void PrintViewManagerQt::PrintToPDFWithCallback(const QPageLayout &pageLayout, // If there already is a pending print in progress, don't try starting another one. if (m_printSettings) { - content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, - base::Bind(callback, std::vector<char>())); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(callback, QSharedPointer<QByteArray>())); return; } m_pdfPrintCallback = callback; if (!PrintToPDFInternal(pageLayout, printInColor, useCustomMargins)) { - content::BrowserThread::PostTask(content::BrowserThread::UI, - FROM_HERE, - base::Bind(callback, std::vector<char>())); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(callback, QSharedPointer<QByteArray>())); resetPdfState(); } @@ -358,21 +349,18 @@ void PrintViewManagerQt::OnMetafileReadyForPrinting(content::RenderFrameHost* rf StopWorker(params.document_cookie); // Create local copies so we can reset the state and take a new pdf print job. - base::Callback<void(const std::vector<char>&)> pdf_print_callback = m_pdfPrintCallback; - base::Callback<void(bool)> pdf_save_callback = m_pdfSaveCallback; + PrintToPDFCallback pdf_print_callback = std::move(m_pdfPrintCallback); + PrintToPDFFileCallback pdf_save_callback = std::move(m_pdfSaveCallback); base::FilePath pdfOutputPath = m_pdfOutputPath; resetPdfState(); if (!pdf_print_callback.is_null()) { - std::vector<char> data_vector = GetStdVectorFromHandle(params.content.metafile_data_handle, - params.content.data_size); - content::BrowserThread::PostTask(content::BrowserThread::UI, - FROM_HERE, - base::Bind(pdf_print_callback, data_vector)); + QSharedPointer<QByteArray> data_array = GetStdVectorFromHandle(params.content.metafile_data_region); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(pdf_print_callback, data_array)); } else { - scoped_refptr<base::RefCountedBytes> data_bytes - = GetBytesFromHandle(params.content.metafile_data_handle, params.content.data_size); + scoped_refptr<base::RefCountedBytes> data_bytes = GetBytesFromHandle(params.content.metafile_data_region); base::PostTaskWithTraits(FROM_HERE, {base::MayBlock()}, base::BindOnce(&SavePdfFile, data_bytes, pdfOutputPath, pdf_save_callback)); } @@ -392,9 +380,8 @@ void PrintViewManagerQt::DidStartLoading() void PrintViewManagerQt::NavigationStopped() { if (!m_pdfPrintCallback.is_null()) { - content::BrowserThread::PostTask(content::BrowserThread::UI, - FROM_HERE, - base::Bind(m_pdfPrintCallback, std::vector<char>())); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(m_pdfPrintCallback, QSharedPointer<QByteArray>())); } resetPdfState(); PrintViewManagerBaseQt::NavigationStopped(); @@ -404,9 +391,8 @@ void PrintViewManagerQt::RenderProcessGone(base::TerminationStatus status) { PrintViewManagerBaseQt::RenderProcessGone(status); if (!m_pdfPrintCallback.is_null()) { - content::BrowserThread::PostTask(content::BrowserThread::UI, - FROM_HERE, - base::Bind(m_pdfPrintCallback, std::vector<char>())); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(m_pdfPrintCallback, QSharedPointer<QByteArray>())); } resetPdfState(); } diff --git a/src/core/printing/print_view_manager_qt.h b/src/core/printing/print_view_manager_qt.h index 209be8782..b21389691 100644 --- a/src/core/printing/print_view_manager_qt.h +++ b/src/core/printing/print_view_manager_qt.h @@ -56,6 +56,8 @@ #include "content/public/browser/notification_registrar.h" #include "content/public/browser/web_contents_user_data.h" +#include <QSharedPointer> + struct PrintHostMsg_RequestPrintPreview_Params; struct PrintHostMsg_DidPreviewDocument_Params; @@ -83,7 +85,7 @@ class PrintViewManagerQt { public: ~PrintViewManagerQt() override; - typedef base::Callback<void(const std::vector<char> &result)> PrintToPDFCallback; + typedef base::Callback<void(QSharedPointer<QByteArray> result)> PrintToPDFCallback; typedef base::Callback<void(bool success)> PrintToPDFFileCallback; // Method to print a page to a Pdf document with page size \a pageSize in location \a filePath. diff --git a/src/core/process_main.cpp b/src/core/process_main.cpp index 677f0b10a..d661d3b90 100644 --- a/src/core/process_main.cpp +++ b/src/core/process_main.cpp @@ -44,7 +44,10 @@ #if defined(OS_WIN) #include "sandbox/win/src/sandbox_types.h" #include "content/public/app/sandbox_helper_win.h" -#endif // OS_WIN +#elif defined(OS_MACOSX) +#include "base/logging.h" +#include "sandbox/mac/seatbelt_exec.h" +#endif namespace QtWebEngine { @@ -64,6 +67,13 @@ int processMain(int argc, const char **argv) params.argc = argc; params.argv = argv; #endif // OS_WIN +#if defined(OS_MACOSX) + sandbox::SeatbeltExecServer::CreateFromArgumentsResult seatbelt = + sandbox::SeatbeltExecServer::CreateFromArguments(argv[0], argc, const_cast<char**>(argv)); + if (seatbelt.sandbox_required) { + CHECK(seatbelt.server->InitializeSandbox()); + } +#endif // defined(OS_MACOSX) return content::ContentMain(params); } diff --git a/src/core/profile_adapter.cpp b/src/core/profile_adapter.cpp index 462be9f48..dfe701cc9 100644 --- a/src/core/profile_adapter.cpp +++ b/src/core/profile_adapter.cpp @@ -44,10 +44,12 @@ #include "content/public/browser/browsing_data_remover.h" #include "content/public/browser/download_manager.h" +#include "api/qwebengineurlscheme.h" #include "content_client_qt.h" #include "download_manager_delegate_qt.h" #include "net/url_request_context_getter_qt.h" #include "permission_manager_qt.h" +#include "profile_adapter_client.h" #include "profile_qt.h" #include "renderer_host/user_resource_controller_host.h" #include "type_conversion.h" @@ -81,6 +83,8 @@ ProfileAdapter::ProfileAdapter(const QString &storageName): , 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 @@ -88,6 +92,7 @@ ProfileAdapter::ProfileAdapter(const QString &storageName): content::BrowserContext::Initialize(m_profile.data(), toFilePath(dataPath())); // fixme: this should not be here m_profile->m_profileIOData->initializeOnUIThread(); + m_customUrlSchemeHandlers.insert(QByteArrayLiteral("qrc"), &m_qrcHandler); } ProfileAdapter::~ProfileAdapter() @@ -97,6 +102,7 @@ ProfileAdapter::~ProfileAdapter() m_profile->GetDownloadManager(m_profile.data())->Shutdown(); m_downloadManagerDelegate.reset(); } + Q_ASSERT(m_pageRequestInterceptors == 0); } void ProfileAdapter::setStorageName(const QString &storageName) @@ -173,6 +179,22 @@ void ProfileAdapter::removeClient(ProfileAdapterClient *adapterClient) m_clients.removeOne(adapterClient); } +void ProfileAdapter::addPageRequestInterceptor() +{ + ++m_pageRequestInterceptors; + if (m_profile->m_urlRequestContextGetter.get()) + m_profile->m_profileIOData->updateRequestInterceptor(); +} + +void ProfileAdapter::removePageRequestInterceptor() +{ + Q_ASSERT(m_pageRequestInterceptors > 0); + --m_pageRequestInterceptors; + if (m_profile->m_urlRequestContextGetter.get()) + m_profile->m_profileIOData->updateRequestInterceptor(); +} + + void ProfileAdapter::cancelDownload(quint32 downloadId) { downloadManagerDelegate()->cancelDownload(downloadId); @@ -232,6 +254,11 @@ void ProfileAdapter::setDataPath(const QString &path) } } +void ProfileAdapter::setDownloadPath(const QString &path) +{ + m_downloadPath = path.isEmpty() ? QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) : path; +} + QString ProfileAdapter::cachePath() const { if (m_offTheRecord) @@ -400,9 +427,23 @@ void ProfileAdapter::setHttpCacheMaxSize(int maxSize) m_profile->m_profileIOData->updateHttpCache(); } -const QHash<QByteArray, QWebEngineUrlSchemeHandler *> &ProfileAdapter::customUrlSchemeHandlers() const +static bool isInternalScheme(const QByteArray &scheme) { - return m_customUrlSchemeHandlers; + static QSet<QByteArray> internalSchemes{ + QByteArrayLiteral("qrc"), + QByteArrayLiteral("data"), + QByteArrayLiteral("blob"), + QByteArrayLiteral("http"), + QByteArrayLiteral("https"), + QByteArrayLiteral("ftp"), + QByteArrayLiteral("javascript"), + }; + return internalSchemes.contains(scheme); +} + +QWebEngineUrlSchemeHandler *ProfileAdapter::urlSchemeHandler(const QByteArray &scheme) +{ + return m_customUrlSchemeHandlers.value(scheme.toLower()).data(); } const QList<QByteArray> ProfileAdapter::customUrlSchemes() const @@ -416,12 +457,17 @@ void ProfileAdapter::updateCustomUrlSchemeHandlers() m_profile->m_profileIOData->updateJobFactory(); } -bool ProfileAdapter::removeCustomUrlSchemeHandler(QWebEngineUrlSchemeHandler *handler) +void ProfileAdapter::removeUrlSchemeHandler(QWebEngineUrlSchemeHandler *handler) { + Q_ASSERT(handler); bool removedOneOrMore = false; auto it = m_customUrlSchemeHandlers.begin(); while (it != m_customUrlSchemeHandlers.end()) { if (it.value() == handler) { + if (isInternalScheme(it.key())) { + qWarning("Cannot remove the URL scheme handler for an internal scheme: %s", it.key().constData()); + continue; + } it = m_customUrlSchemeHandlers.erase(it); removedOneOrMore = true; continue; @@ -430,26 +476,43 @@ bool ProfileAdapter::removeCustomUrlSchemeHandler(QWebEngineUrlSchemeHandler *ha } if (removedOneOrMore) updateCustomUrlSchemeHandlers(); - return removedOneOrMore; } -QWebEngineUrlSchemeHandler *ProfileAdapter::takeCustomUrlSchemeHandler(const QByteArray &scheme) +void ProfileAdapter::removeUrlScheme(const QByteArray &scheme) { - QWebEngineUrlSchemeHandler *handler = m_customUrlSchemeHandlers.take(scheme); - if (handler) + QByteArray canonicalScheme = scheme.toLower(); + if (isInternalScheme(canonicalScheme)) { + qWarning("Cannot remove the URL scheme handler for an internal scheme: %s", scheme.constData()); + return; + } + if (m_customUrlSchemeHandlers.remove(canonicalScheme)) updateCustomUrlSchemeHandlers(); - return handler; } -void ProfileAdapter::addCustomUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler) +void ProfileAdapter::installUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler) { - m_customUrlSchemeHandlers.insert(scheme, handler); + Q_ASSERT(handler); + QByteArray canonicalScheme = scheme.toLower(); + if (isInternalScheme(canonicalScheme)) { + qWarning("Cannot install a URL scheme handler overriding internal scheme: %s", scheme.constData()); + return; + } + if (m_customUrlSchemeHandlers.value(canonicalScheme, handler) != handler) { + qWarning("URL scheme handler already installed for the scheme: %s", scheme.constData()); + return; + } + if (QWebEngineUrlScheme::schemeByName(canonicalScheme) == QWebEngineUrlScheme()) + qWarning("Please register the custom scheme '%s' via QWebEngineUrlScheme::registerScheme() " + "before installing the custom scheme handler.", scheme.constData()); + + m_customUrlSchemeHandlers.insert(canonicalScheme, handler); updateCustomUrlSchemeHandlers(); } -void ProfileAdapter::clearCustomUrlSchemeHandlers() +void ProfileAdapter::removeAllUrlSchemeHandlers() { m_customUrlSchemeHandlers.clear(); + m_customUrlSchemeHandlers.insert(QByteArrayLiteral("qrc"), &m_qrcHandler); updateCustomUrlSchemeHandlers(); } @@ -548,7 +611,37 @@ bool ProfileAdapter::isSpellCheckEnabled() const void ProfileAdapter::resetVisitedLinksManager() { - m_visitedLinksManager.reset(new VisitedLinksManagerQt(this)); + m_visitedLinksManager.reset(new VisitedLinksManagerQt(m_profile.data(), persistVisitedLinks())); +} + +void ProfileAdapter::setUseForGlobalCertificateVerification(bool enable) +{ + if (m_usedForGlobalCertificateVerification == enable) + return; + + static QPointer<ProfileAdapter> profileForglobalCertificateVerification; + + m_usedForGlobalCertificateVerification = enable; + if (enable) { + if (profileForglobalCertificateVerification) { + profileForglobalCertificateVerification->m_usedForGlobalCertificateVerification = false; + for (auto *client : qAsConst(profileForglobalCertificateVerification->m_clients)) + client->useForGlobalCertificateVerificationChanged(); + } + profileForglobalCertificateVerification = this; + } else { + Q_ASSERT(profileForglobalCertificateVerification); + Q_ASSERT(profileForglobalCertificateVerification == this); + profileForglobalCertificateVerification = nullptr; + } + + if (m_profile->m_urlRequestContextGetter.get()) + m_profile->m_profileIOData->updateUsedForGlobalCertificateVerification(); +} + +bool ProfileAdapter::isUsedForGlobalCertificateVerification() const +{ + return m_usedForGlobalCertificateVerification; } } // namespace QtWebEngineCore diff --git a/src/core/profile_adapter.h b/src/core/profile_adapter.h index 9bc92b54a..800058bc5 100644 --- a/src/core/profile_adapter.h +++ b/src/core/profile_adapter.h @@ -63,6 +63,7 @@ #include "api/qwebenginecookiestore.h" #include "api/qwebengineurlrequestinterceptor.h" #include "api/qwebengineurlschemehandler.h" +#include "net/qrc_url_scheme_handler.h" QT_FORWARD_DECLARE_CLASS(QObject) @@ -112,6 +113,9 @@ public: QString dataPath() const; void setDataPath(const QString &path); + QString downloadPath() const { return m_downloadPath; } + void setDownloadPath(const QString &path); + QString cachePath() const; void setCachePath(const QString &path); @@ -170,14 +174,14 @@ public: void setHttpCacheMaxSize(int maxSize); bool trackVisitedLinks() const; - bool persistVisitedLinks() const; - const QHash<QByteArray, QWebEngineUrlSchemeHandler *> &customUrlSchemeHandlers() const; + QWebEngineUrlSchemeHandler *urlSchemeHandler(const QByteArray &scheme); + void installUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler); + void removeUrlScheme(const QByteArray &scheme); + void removeUrlSchemeHandler(QWebEngineUrlSchemeHandler *handler); + void removeAllUrlSchemeHandlers(); + const QList<QByteArray> customUrlSchemes() const; - void clearCustomUrlSchemeHandlers(); - void addCustomUrlSchemeHandler(const QByteArray &, QWebEngineUrlSchemeHandler *); - bool removeCustomUrlSchemeHandler(QWebEngineUrlSchemeHandler *); - QWebEngineUrlSchemeHandler *takeCustomUrlSchemeHandler(const QByteArray &); UserResourceControllerHost *userResourceController(); void permissionRequestReply(const QUrl &origin, PermissionType type, bool reply); @@ -189,12 +193,21 @@ public: void clearHttpCache(); + void setUseForGlobalCertificateVerification(bool enable = true); + bool isUsedForGlobalCertificateVerification() const; + + void addPageRequestInterceptor(); + void removePageRequestInterceptor(); + bool hasPageRequestInterceptor() const { return m_pageRequestInterceptors > 0; } + private: void updateCustomUrlSchemeHandlers(); void resetVisitedLinksManager(); + bool persistVisitedLinks() const; QString m_name; bool m_offTheRecord; + bool m_usedForGlobalCertificateVerification = false; QScopedPointer<ProfileQt> m_profile; QScopedPointer<VisitedLinksManagerQt> m_visitedLinksManager; QScopedPointer<DownloadManagerDelegateQt> m_downloadManagerDelegate; @@ -203,15 +216,18 @@ private: QPointer<QWebEngineUrlRequestInterceptor> m_requestInterceptor; QString m_dataPath; + QString m_downloadPath; QString m_cachePath; QString m_httpUserAgent; HttpCacheType m_httpCacheType; QString m_httpAcceptLanguage; PersistentCookiesPolicy m_persistentCookiesPolicy; VisitedLinksPolicy m_visitedLinksPolicy; - QHash<QByteArray, QWebEngineUrlSchemeHandler *> m_customUrlSchemeHandlers; + QHash<QByteArray, QPointer<QWebEngineUrlSchemeHandler>> m_customUrlSchemeHandlers; QList<ProfileAdapterClient*> m_clients; int m_httpCacheMaxSize; + int m_pageRequestInterceptors; + QrcUrlSchemeHandler m_qrcHandler; Q_DISABLE_COPY(ProfileAdapter) }; diff --git a/src/core/profile_adapter_client.h b/src/core/profile_adapter_client.h index 06051fab6..4711f8bcf 100644 --- a/src/core/profile_adapter_client.h +++ b/src/core/profile_adapter_client.h @@ -142,6 +142,7 @@ public: virtual void downloadRequested(DownloadItemInfo &info) = 0; virtual void downloadUpdated(const DownloadItemInfo &info) = 0; + virtual void useForGlobalCertificateVerificationChanged() {} static QString downloadInterruptReasonToString(DownloadInterruptReason reason); }; diff --git a/src/core/profile_io_data_qt.cpp b/src/core/profile_io_data_qt.cpp index 7783f1ae7..20754ed7a 100644 --- a/src/core/profile_io_data_qt.cpp +++ b/src/core/profile_io_data_qt.cpp @@ -39,9 +39,10 @@ #include "profile_io_data_qt.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "components/certificate_transparency/ct_known_logs.h" #include "components/network_session_configurator/common/network_features.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/browsing_data_remover.h" #include "content/public/browser/cookie_store_factory.h" @@ -80,12 +81,20 @@ #include "net/custom_protocol_handler.h" #include "net/network_delegate_qt.h" #include "net/proxy_config_service_qt.h" -#include "net/qrc_protocol_handler_qt.h" #include "net/url_request_context_getter_qt.h" #include "profile_qt.h" #include "resource_context_qt.h" #include "type_conversion.h" +#if defined(USE_NSS_CERTS) +#include "net/cert_net/nss_ocsp.h" +#endif + +#if defined(OS_LINUX) || defined(OS_MACOSX) +#include "net/cert/cert_net_fetcher.h" +#include "net/cert_net/cert_net_fetcher_impl.h" +#endif + namespace QtWebEngineCore { static const char* const kDefaultAuthSchemes[] = { net::kBasicAuthScheme, @@ -102,8 +111,6 @@ static bool doNetworkSessionParamsMatch(const net::HttpNetworkSession::Params &f return false; if (first.enable_channel_id != second.enable_channel_id) return false; - if (first.enable_token_binding != second.enable_token_binding) - return false; return true; } @@ -153,7 +160,6 @@ static net::HttpNetworkSession::Params generateNetworkSessionParams(bool ignoreC { net::HttpNetworkSession::Params network_session_params; network_session_params.ignore_certificate_errors = ignoreCertificateErrors; - network_session_params.enable_token_binding = base::FeatureList::IsEnabled(features::kTokenBinding); network_session_params.enable_channel_id = base::FeatureList::IsEnabled(features::kChannelID); return network_session_params; } @@ -171,6 +177,16 @@ ProfileIODataQt::~ProfileIODataQt() { if (content::BrowserThread::IsThreadInitialized(content::BrowserThread::IO)) DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + if (m_useForGlobalCertificateVerification) { +#if defined(USE_NSS_CERTS) + net::SetURLRequestContextForNSSHttpIO(nullptr); +#endif +#if defined(OS_LINUX) ||defined(OS_MACOSX) + net::ShutdownGlobalCertNetFetcher(); +#endif + } + m_resourceContext.reset(); if (m_cookieDelegate) m_cookieDelegate->setCookieMonster(0); // this will let CookieMonsterDelegateQt be deleted @@ -213,6 +229,7 @@ void ProfileIODataQt::initializeOnIOThread() m_initialized = true; generateAllStorage(); generateJobFactory(); + setGlobalCertificateVerification(); } void ProfileIODataQt::initializeOnUIThread() @@ -276,7 +293,15 @@ void ProfileIODataQt::generateStorage() net::ProxyConfigService *proxyConfigService = m_proxyConfigService.fetchAndStoreAcquire(0); Q_ASSERT(proxyConfigService); - m_storage->set_cert_verifier(net::CertVerifier::CreateDefault()); + std::unique_ptr<net::CertVerifier> cert_verifier = net::CertVerifier::CreateDefault(); + net::CertVerifier::Config config; + // Enable revocation checking: + config.enable_rev_checking = true; + // Mirroring Android WebView (we have no beef with Symantec, and our users might use them): + config.disable_symantec_enforcement = true; + cert_verifier->SetConfig(config); + + 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()); @@ -307,7 +332,7 @@ void ProfileIODataQt::generateStorage() scoped_refptr<base::SequencedTaskRunner> background_task_runner( base::CreateSequencedTaskRunnerWithTraits( {base::MayBlock(), - base::TaskPriority::BACKGROUND, + base::TaskPriority::BEST_EFFORT, base::TaskShutdownBehavior::BLOCK_SHUTDOWN})); m_transportSecurityPersister = std::make_unique<net::TransportSecurityPersister>( @@ -347,7 +372,7 @@ void ProfileIODataQt::generateCookieStore() channel_id_db = new net::SQLiteChannelIDStore( toFilePath(m_channelIdPath), base::CreateSequencedTaskRunnerWithTraits( - {base::MayBlock(), base::TaskPriority::BACKGROUND})); + {base::MayBlock(), base::TaskPriority::BEST_EFFORT})); } m_storage->set_channel_id_service( @@ -367,8 +392,8 @@ void ProfileIODataQt::generateCookieStore() base::FilePath(), false, false, - nullptr) - ); + nullptr), + nullptr); break; case ProfileAdapter::AllowPersistentCookies: cookieStore = content::CreateCookieStore( @@ -376,8 +401,8 @@ void ProfileIODataQt::generateCookieStore() toFilePath(m_cookiesPath), false, true, - nullptr) - ); + nullptr), + nullptr); break; case ProfileAdapter::ForcePersistentCookies: cookieStore = content::CreateCookieStore( @@ -385,8 +410,8 @@ void ProfileIODataQt::generateCookieStore() toFilePath(m_cookiesPath), true, true, - nullptr) - ); + nullptr), + nullptr); break; } @@ -494,13 +519,10 @@ void ProfileIODataQt::generateJobFactory() std::unique_ptr<net::URLRequestJobFactory::ProtocolHandler>( new net::DataProtocolHandler())); scoped_refptr<base::TaskRunner> taskRunner(base::CreateTaskRunnerWithTraits({base::MayBlock(), - base::TaskPriority::BACKGROUND, + base::TaskPriority::BEST_EFFORT, base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})); jobFactory->SetProtocolHandler(url::kFileScheme, std::make_unique<net::FileProtocolHandler>(taskRunner)); - jobFactory->SetProtocolHandler(kQrcSchemeQt, - std::unique_ptr<net::URLRequestJobFactory::ProtocolHandler>( - new QrcProtocolHandlerQt())); jobFactory->SetProtocolHandler(url::kFtpScheme, net::FtpProtocolHandler::Create(m_urlRequestContext->host_resolver())); @@ -558,6 +580,21 @@ void ProfileIODataQt::regenerateJobFactory() } } +void ProfileIODataQt::setGlobalCertificateVerification() +{ + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + QMutexLocker lock(&m_mutex); + if (m_useForGlobalCertificateVerification) { +#if defined(USE_NSS_CERTS) + // Set request context used by NSS for OCSP requests. + net::SetURLRequestContextForNSSHttpIO(m_urlRequestContext.get()); +#endif +#if defined(OS_LINUX) || defined(OS_MACOSX) + net::SetGlobalCertNetFetcher(net::CreateCertNetFetcher(m_urlRequestContext.get())); +#endif + } +} + void ProfileIODataQt::setRequestContextData(content::ProtocolHandlerMap *protocolHandlers, content::URLRequestInterceptorScopedVector request_interceptors) { @@ -580,6 +617,7 @@ void ProfileIODataQt::setFullConfiguration() m_httpCachePath = m_profileAdapter->httpCachePath(); m_httpCacheMaxSize = m_profileAdapter->httpCacheMaxSize(); m_customUrlSchemes = m_profileAdapter->customUrlSchemes(); + m_useForGlobalCertificateVerification = m_profileAdapter->isUsedForGlobalCertificateVerification(); m_dataPath = m_profileAdapter->dataPath(); } @@ -605,12 +643,12 @@ void ProfileIODataQt::updateStorageSettings() m_proxyConfigService = new ProxyConfigServiceQt( net::ProxyResolutionService::CreateSystemProxyConfigService( - content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::IO))); + base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::IO}))); //pass interface to io thread m_proxyResolverFactoryInterface = ChromeMojoProxyResolverFactory::CreateWithStrongBinding().PassInterface(); if (m_initialized) - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::Bind(&ProfileIODataQt::generateAllStorage, m_weakPtr)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&ProfileIODataQt::generateAllStorage, m_weakPtr)); } } @@ -625,8 +663,8 @@ void ProfileIODataQt::updateCookieStore() if (m_initialized && !m_updateAllStorage && !m_updateCookieStore) { m_updateCookieStore = true; m_updateHttpCache = true; - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::Bind(&ProfileIODataQt::generateCookieStore, m_weakPtr)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&ProfileIODataQt::generateCookieStore, m_weakPtr)); } } @@ -639,8 +677,8 @@ void ProfileIODataQt::updateUserAgent() if (m_initialized && !m_updateAllStorage && !m_updateUserAgent) { m_updateUserAgent = true; - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::Bind(&ProfileIODataQt::generateUserAgent, m_weakPtr)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&ProfileIODataQt::generateUserAgent, m_weakPtr)); } } @@ -663,8 +701,8 @@ void ProfileIODataQt::updateHttpCache() if (m_initialized && !m_updateAllStorage && !m_updateHttpCache) { m_updateHttpCache = true; - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::Bind(&ProfileIODataQt::generateHttpCache, m_weakPtr)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&ProfileIODataQt::generateHttpCache, m_weakPtr)); } } @@ -677,8 +715,8 @@ void ProfileIODataQt::updateJobFactory() if (m_initialized && !m_updateJobFactory) { m_updateJobFactory = true; - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::Bind(&ProfileIODataQt::regenerateJobFactory, m_weakPtr)); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&ProfileIODataQt::regenerateJobFactory, m_weakPtr)); } } @@ -687,6 +725,7 @@ void ProfileIODataQt::updateRequestInterceptor() Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); QMutexLocker lock(&m_mutex); m_requestInterceptor = m_profileAdapter->requestInterceptor(); + m_hasPageInterceptors = m_profileAdapter->hasPageRequestInterceptor(); // We in this case do not need to regenerate any Chromium classes. } @@ -696,6 +735,13 @@ QWebEngineUrlRequestInterceptor *ProfileIODataQt::acquireInterceptor() return m_requestInterceptor; } +bool ProfileIODataQt::hasPageInterceptors() +{ + // used in NetworkDelegateQt::OnBeforeURLRequest + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + return m_hasPageInterceptors; +} + void ProfileIODataQt::releaseInterceptor() { m_mutex.unlock(); @@ -711,4 +757,15 @@ bool ProfileIODataQt::canGetCookies(const QUrl &firstPartyUrl, const QUrl &url) return m_cookieDelegate->canGetCookies(firstPartyUrl, url); } +void ProfileIODataQt::updateUsedForGlobalCertificateVerification() +{ + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + QMutexLocker lock(&m_mutex); + m_useForGlobalCertificateVerification = m_profileAdapter->isUsedForGlobalCertificateVerification(); + + if (m_useForGlobalCertificateVerification) + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&ProfileIODataQt::setGlobalCertificateVerification, m_weakPtr)); +} + } // namespace QtWebEngineCore diff --git a/src/core/profile_io_data_qt.h b/src/core/profile_io_data_qt.h index 5b416861c..bcf49e22b 100644 --- a/src/core/profile_io_data_qt.h +++ b/src/core/profile_io_data_qt.h @@ -91,6 +91,7 @@ public: void regenerateJobFactory(); bool canSetCookie(const QUrl &firstPartyUrl, const QByteArray &cookieLine, const QUrl &url) const; bool canGetCookies(const QUrl &firstPartyUrl, const QUrl &url) const; + void setGlobalCertificateVerification(); // Used in NetworkDelegateQt::OnBeforeURLRequest. QWebEngineUrlRequestInterceptor *acquireInterceptor(); @@ -105,6 +106,8 @@ public: void updateHttpCache(); // runs on ui thread void updateJobFactory(); // runs on ui thread void updateRequestInterceptor(); // runs on ui thread + void updateUsedForGlobalCertificateVerification(); // runs on ui thread + bool hasPageInterceptors(); private: ProfileQt *m_profile; @@ -145,6 +148,8 @@ private: bool m_updateJobFactory = false; bool m_updateUserAgent = false; bool m_ignoreCertificateErrors = false; + bool m_useForGlobalCertificateVerification = false; + bool m_hasPageInterceptors = false; base::WeakPtrFactory<ProfileIODataQt> m_weakPtrFactory; // this should be always the last member QString m_dataPath; DISALLOW_COPY_AND_ASSIGN(ProfileIODataQt); diff --git a/src/core/profile_qt.cpp b/src/core/profile_qt.cpp index df05d891e..e4698c677 100644 --- a/src/core/profile_qt.cpp +++ b/src/core/profile_qt.cpp @@ -87,6 +87,8 @@ ProfileQt::ProfileQt(ProfileAdapter *profileAdapter) registry->RegisterBooleanPref(spellcheck::prefs::kSpellCheckEnable, false); registry->RegisterBooleanPref(spellcheck::prefs::kSpellCheckUseSpellingService, false); #endif // QT_CONFIG(webengine_spellchecker) + registry->RegisterBooleanPref(prefs::kShowInternalAccessibilityTree, false); + m_prefService = factory.Create(registry); user_prefs::UserPrefs::Set(this, m_prefService.get()); @@ -123,6 +125,11 @@ base::FilePath ProfileQt::GetPath() const return toFilePath(m_profileAdapter->dataPath()); } +base::FilePath ProfileQt::GetCachePath() const +{ + return toFilePath(m_profileAdapter->cachePath()); +} + bool ProfileQt::IsOffTheRecord() const { return m_profileAdapter->isOffTheRecord(); @@ -229,8 +236,8 @@ net::URLRequestContextGetter *ProfileQt::CreateRequestContextForStoragePartition void ProfileQt::FailedToLoadDictionary(const std::string &language) { Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - qWarning() << "Could not load dictionary for:" << toQt(language) << endl - << "Make sure that correct bdic file is in:" << toQt(WebEngineLibraryInfo::getPath(base::DIR_APP_DICTIONARIES).value()); + LOG(WARNING) << "Could not load dictionary for:" << language; + LOG(INFO) << "Make sure that correct bdic file is in:" << WebEngineLibraryInfo::getPath(base::DIR_APP_DICTIONARIES); } void ProfileQt::setSpellCheckLanguages(const QStringList &languages) diff --git a/src/core/profile_qt.h b/src/core/profile_qt.h index 00119c053..8641646b1 100644 --- a/src/core/profile_qt.h +++ b/src/core/profile_qt.h @@ -68,6 +68,7 @@ public: // BrowserContext implementation: base::FilePath GetPath() const override; + base::FilePath GetCachePath() const override; bool IsOffTheRecord() const override; net::URLRequestContextGetter *CreateMediaRequestContext() override; diff --git a/src/core/qtwebengine.gni b/src/core/qtwebengine.gni index 14da1e6cf..3ae2b999c 100644 --- a/src/core/qtwebengine.gni +++ b/src/core/qtwebengine.gni @@ -9,6 +9,7 @@ chromium_version = exec_script("//build/util/version.py", [ "-f", rebase_path("/ include_dirs = [ "//skia/config", "//third_party", + "//third_party/boringssl/src/include", "//third_party/skia/include/core" ] @@ -26,16 +27,14 @@ deps = [ "//components/web_cache/renderer", "//components/spellcheck:buildflags", "//content/public/app:browser", - "//content/public/browser", - "//content/public/common", - "//content/public/renderer", + "//content", "//media:media_buildflags", "//net:net_with_v8", "//services/proxy_resolver:lib", "//skia", "//third_party/blink/public:blink", - "//third_party/mesa:mesa_headers", "//ui/accessibility", + "//ui/gl", "//qtwebengine/browser:interfaces", ":qtwebengine_sources", ":qtwebengine_resources" @@ -49,6 +48,10 @@ if (is_linux && !is_desktop_linux) { deps += [ "//ui/events/ozone:events_ozone_evdev"] } +if (use_xscrnsaver) { + deps += [ "//ui/base/x" ] +} + if (use_ozone) { deps += [ "//ui/ozone/common" diff --git a/src/core/qtwebengine_sources.gni b/src/core/qtwebengine_sources.gni index b1361e727..aa9d12480 100644 --- a/src/core/qtwebengine_sources.gni +++ b/src/core/qtwebengine_sources.gni @@ -47,15 +47,13 @@ source_set("qtwebengine_sources") { "//third_party/blink/public/mojom:mojom_platform", ] sources = [ - "//chrome/common/custom_handlers/protocol_handler.cc", - "//chrome/common/custom_handlers/protocol_handler.h", + "//chrome/browser/accessibility/accessibility_ui.cc", + "//chrome/browser/accessibility/accessibility_ui.h", "//chrome/browser/custom_handlers/protocol_handler_registry.cc", "//chrome/browser/custom_handlers/protocol_handler_registry.h", "//chrome/browser/custom_handlers/protocol_handler_registry_factory.cc", "//chrome/browser/custom_handlers/protocol_handler_registry_factory.h", "//chrome/browser/media/webrtc/desktop_media_list.h", - "//chrome/browser/media/webrtc/desktop_streams_registry.cc", - "//chrome/browser/media/webrtc/desktop_streams_registry.h", "//chrome/browser/net/chrome_mojo_proxy_resolver_factory.cc", "//chrome/browser/net/chrome_mojo_proxy_resolver_factory.h", "//chrome/browser/profiles/profile.cc", @@ -72,6 +70,8 @@ source_set("qtwebengine_sources") { "//chrome/browser/ui/webui/quota_internals/quota_internals_ui.h", "//chrome/browser/ui/webui/task_scheduler_internals/task_scheduler_internals_ui.cc", "//chrome/browser/ui/webui/task_scheduler_internals/task_scheduler_internals_ui.h", + "//chrome/common/custom_handlers/protocol_handler.cc", + "//chrome/common/custom_handlers/protocol_handler.h", "//chrome/common/chrome_switches.cc", "//chrome/common/chrome_switches.h", "//chrome/common/pref_names.cc", @@ -109,11 +109,6 @@ source_set("qtwebengine_sources") { "//chrome/renderer/pepper/pepper_shared_memory_message_filter.cc", "//chrome/renderer/pepper/pepper_shared_memory_message_filter.h", ] - - deps += [ - # Need to depend on //content/ppapi_plugin, which is private, thus depending on parent. - "//content", - ] } if (enable_basic_printing || enable_print_preview) { diff --git a/src/core/quota_permission_context_qt.cpp b/src/core/quota_permission_context_qt.cpp index cb1467364..a502e7fc8 100644 --- a/src/core/quota_permission_context_qt.cpp +++ b/src/core/quota_permission_context_qt.cpp @@ -39,7 +39,9 @@ #include "quota_permission_context_qt.h" +#include "base/task/post_task.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_frame_host.h" #include "quota_request_controller_impl.h" @@ -64,10 +66,10 @@ void QuotaPermissionContextQt::RequestQuotaPermission(const StorageQuotaParams & } if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { - content::BrowserThread::PostTask( - content::BrowserThread::UI, FROM_HERE, - base::Bind(&QuotaPermissionContextQt::RequestQuotaPermission, this, - params, render_process_id, callback)); + base::PostTaskWithTraits( + FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&QuotaPermissionContextQt::RequestQuotaPermission, this, + params, render_process_id, callback)); return; } @@ -95,10 +97,10 @@ void QuotaPermissionContextQt::dispatchCallbackOnIOThread(const PermissionCallba return; if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)) { - content::BrowserThread::PostTask( - content::BrowserThread::IO, FROM_HERE, - base::Bind(&QuotaPermissionContextQt::dispatchCallbackOnIOThread, - this, callback, response)); + base::PostTaskWithTraits( + FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&QuotaPermissionContextQt::dispatchCallbackOnIOThread, + this, callback, response)); return; } diff --git a/src/core/qwebengineclientcertificatestore.cpp b/src/core/qwebengineclientcertificatestore.cpp new file mode 100644 index 000000000..08f4389d7 --- /dev/null +++ b/src/core/qwebengineclientcertificatestore.cpp @@ -0,0 +1,288 @@ +/**************************************************************************** +** +** 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 "api/qwebengineclientcertificatestore.h" +#include "client_cert_override_key_p.h" +#include "client_cert_override_p.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 <QByteArray> +#include <QList> + +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; + +/*! + \class QWebEngineClientCertificateStore::Entry + \inmodule QtWebEngineCore + \since 5.13 + \brief This structure holds the certificate and the private key. +*/ + +/*! + \class QWebEngineClientCertificateStore + \inmodule QtWebEngineCore + \since 5.13 + \brief The QWebEngineClientCertificateStore class provides an in-memory store for client certificates. + + The class allows to store client certificates in an in-memory store. + When a web site requests an SSL client certificate, the QWebEnginePage::selectClientCertificate + signal is emitted with matching certificates from the native certificate store or the in-memory store. + The getInstance() method can be used to access the single instance of the class. +*/ + +QWebEngineClientCertificateStore::QWebEngineClientCertificateStore() +{ + this->d_ptr = new QWebEngineClientCertificateStoreData; +} + +/*! + Destroys this QWebEngineClientCertificateStore object. +*/ + +QWebEngineClientCertificateStore::~QWebEngineClientCertificateStore() +{ + // Just in case user has not deleted in-memory certificates + clear(); + + qDeleteAll(d_ptr->deletedCerts); + delete d_ptr; +} + +/*! + Returns an in-memory client certificate store. +*/ + +QWebEngineClientCertificateStore *QWebEngineClientCertificateStore::getInstance() +{ + if (!m_instance) + m_instance = new QWebEngineClientCertificateStore; + return m_instance; +} + +/*! + Adds a \a certificate with the \a privateKey to the in-memory client certificate store. +*/ + +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); +} + +/*! + Returns a list of private and public keys of client certificates in the in-memory store. + Returns an empty list if the in-memory store does not contain certificates. +*/ + +QList<QWebEngineClientCertificateStore::Entry> QWebEngineClientCertificateStore::toList() const +{ + QList<Entry> certificateList; + for (auto data : ClientCertOverrideData) { + Entry entry; + entry.certificate = data->certificate; + entry.privateKey = data->key; + certificateList.append(entry); + } + return certificateList; +} + +/*! + Deletes all the instances of the client certificate in the in-memory client certificate store + that matches the certificate in the \a entry. +*/ + +void QWebEngineClientCertificateStore::remove(Entry entry) +{ + QMutableListIterator<OverrideData*> iterator(ClientCertOverrideData); + while (iterator.hasNext()) { + auto overrideData = iterator.next(); + if (entry.certificate.toDer() == overrideData->certificate.toDer()) { + d_ptr->deletedCerts.append(overrideData); + iterator.remove(); + } + } +} + +/*! + Clears all the client certificates from the in-memory store. +*/ + +void QWebEngineClientCertificateStore::clear() +{ + for (auto data : ClientCertOverrideData) + d_ptr->deletedCerts.append(data); + ClientCertOverrideData.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/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp index 40ed75973..ac7357f0e 100644 --- a/src/core/render_widget_host_view_qt.cpp +++ b/src/core/render_widget_host_view_qt.cpp @@ -41,17 +41,20 @@ #include "browser_accessibility_manager_qt.h" #include "chromium_overrides.h" -#include "compositor.h" +#include "compositor/compositor.h" #include "qtwebenginecoreglobal_p.h" #include "render_widget_host_view_qt_delegate.h" +#include "touch_handle_drawable_client.h" +#include "touch_selection_controller_client_qt.h" +#include "touch_selection_menu_controller.h" #include "type_conversion.h" #include "web_contents_adapter_client.h" #include "web_event_factory.h" #include "components/viz/common/surfaces/frame_sink_id_allocator.h" -#include "content/browser/accessibility/browser_accessibility_state_impl.h" -#include "content/browser/frame_host/render_frame_host_impl.h" #include "content/browser/frame_host/frame_tree.h" +#include "content/browser/frame_host/render_frame_host_impl.h" +#include "content/browser/renderer_host/render_view_host_delegate.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/common/content_switches_internal.h" #include "content/browser/renderer_host/render_widget_host_input_event_router.h" @@ -61,9 +64,12 @@ #include "third_party/blink/public/platform/web_cursor_info.h" #include "ui/events/blink/blink_event_util.h" #include "ui/events/event.h" +#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" #if defined(USE_OZONE) #include "ui/base/clipboard/scoped_clipboard_writer.h" @@ -82,7 +88,6 @@ #include <QFocusEvent> #include <QGuiApplication> #include <QInputMethodEvent> -#include <QLoggingCategory> #include <QTextFormat> #include <QKeyEvent> #include <QMouseEvent> @@ -93,7 +98,6 @@ #include <QWheelEvent> #include <QWindow> #include <QtGui/private/qinputcontrol_p.h> -#include <QtGui/qaccessible.h> namespace QtWebEngineCore { @@ -224,6 +228,8 @@ public: float GetPressure(size_t pointer_index) const override { return touchPoints.at(pointer_index).pressure(); } float GetTiltX(size_t pointer_index) const override { return 0; } float GetTiltY(size_t pointer_index) const override { return 0; } + float GetTwist(size_t) const override { return 0; } + float GetTangentialPressure(size_t) const override { return 0; } base::TimeTicks GetEventTime() const override { return eventTime; } size_t GetHistorySize() const override { return 0; } @@ -244,25 +250,12 @@ private: float dpiScale; }; -bool isAccessibilityEnabled() { - // On Linux accessibility is disabled by default due to performance issues, - // and can be re-enabled by setting the QTWEBENGINE_ENABLE_LINUX_ACCESSIBILITY environment - // variable. For details, see QTBUG-59922. -#ifdef Q_OS_LINUX - static bool accessibility_enabled - = qEnvironmentVariableIsSet("QTWEBENGINE_ENABLE_LINUX_ACCESSIBILITY"); -#else - const bool accessibility_enabled = true; -#endif - return accessibility_enabled; -} - RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost *widget) : content::RenderWidgetHostViewBase::RenderWidgetHostViewBase(widget) , m_gestureProvider(QtGestureProviderConfig(), this) , m_sendMotionActionDown(false) , m_touchMotionStarted(false) - , m_compositor(new Compositor(this)) + , m_compositor(new Compositor(widget)) , m_loadVisuallyCommittedState(NotCommitted) , m_adapterClient(0) , m_imeInProgress(false) @@ -280,13 +273,6 @@ RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost *widget base::checked_cast<uint32_t>(widget->GetRoutingID())) { host()->SetView(this); -#ifndef QT_NO_ACCESSIBILITY - if (isAccessibilityEnabled()) { - QAccessible::installActivationObserver(this); - if (QAccessible::isActive()) - content::BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility(); - } -#endif // QT_NO_ACCESSIBILITY if (GetTextInputManager()) GetTextInputManager()->AddObserver(this); @@ -294,27 +280,31 @@ RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost *widget const QPlatformInputContext *context = QGuiApplicationPrivate::platformIntegration()->inputContext(); m_imeHasHiddenTextCapability = context && context->hasCapability(QPlatformInputContext::HiddenTextCapability); - m_localSurfaceId = m_localSurfaceIdAllocator.GenerateId(); - if (host()->delegate() && host()->delegate()->GetInputEventRouter()) host()->delegate()->GetInputEventRouter()->AddFrameSinkIdOwner(GetFrameSinkId(), this); + + m_touchSelectionControllerClient.reset(new TouchSelectionControllerClientQt(this)); + ui::TouchSelectionController::Config config; + config.max_tap_duration = base::TimeDelta::FromMilliseconds(ui::GestureConfiguration::GetInstance()->long_press_time_in_ms()); + config.tap_slop = ui::GestureConfiguration::GetInstance()->max_touch_move_in_pixels_for_click(); + config.enable_longpress_drag_selection = false; + m_touchSelectionController.reset(new ui::TouchSelectionController(m_touchSelectionControllerClient.get(), config)); } RenderWidgetHostViewQt::~RenderWidgetHostViewQt() { QObject::disconnect(m_adapterClientDestroyedConnection); -#ifndef QT_NO_ACCESSIBILITY - QAccessible::removeActivationObserver(this); -#endif // QT_NO_ACCESSIBILITY if (text_input_manager_) text_input_manager_->RemoveObserver(this); + + m_touchSelectionController.reset(); + m_touchSelectionControllerClient.reset(); } void RenderWidgetHostViewQt::setDelegate(RenderWidgetHostViewQtDelegate* delegate) { m_delegate.reset(delegate); - m_compositor->setViewDelegate(delegate); } void RenderWidgetHostViewQt::setAdapterClient(WebContentsAdapterClient *adapterClient) @@ -682,9 +672,9 @@ void RenderWidgetHostViewQt::DidCreateNewRendererCompositorFrameSink(viz::mojom: void RenderWidgetHostViewQt::SubmitCompositorFrame(const viz::LocalSurfaceId &local_surface_id, viz::CompositorFrame frame, base::Optional<viz::HitTestRegionList>) { bool scrollOffsetChanged = (m_lastScrollOffset != frame.metadata.root_scroll_offset); - bool contentsSizeChanged = (m_lastContentsSize != frame.metadata.root_layer_size); + bool contentsSizeChanged = (m_lastContentsSize != frame.metadata.scrollable_viewport_size); m_lastScrollOffset = frame.metadata.root_scroll_offset; - m_lastContentsSize = frame.metadata.root_layer_size; + m_lastContentsSize = frame.metadata.scrollable_viewport_size; // Force to process swap messages uint32_t frame_token = frame.metadata.frame_token; @@ -696,7 +686,9 @@ void RenderWidgetHostViewQt::SubmitCompositorFrame(const viz::LocalSurfaceId &lo if (dpiScale != 0 && dpiScale != 1) frame.metadata.device_scale_factor /= dpiScale; - m_compositor->submitFrame(std::move(frame)); + m_compositor->submitFrame( + std::move(frame), + base::BindOnce(&RenderWidgetHostViewQtDelegate::update, base::Unretained(m_delegate.get()))); if (m_loadVisuallyCommittedState == NotCommitted) { m_loadVisuallyCommittedState = DidFirstCompositorFrameSwap; @@ -902,7 +894,33 @@ void RenderWidgetHostViewQt::OnGestureEvent(const ui::GestureEventData& gesture) return; } - host()->ForwardGestureEvent(ui::CreateWebGestureEventFromGestureEventData(gesture)); + blink::WebGestureEvent event = ui::CreateWebGestureEventFromGestureEventData(gesture); + + if (m_touchSelectionController && m_touchSelectionControllerClient) { + switch (event.GetType()) { + case blink::WebInputEvent::kGestureLongPress: + m_touchSelectionController->HandleLongPressEvent(event.TimeStamp(), event.PositionInWidget()); + break; + case blink::WebInputEvent::kGestureTap: + m_touchSelectionController->HandleTapEvent(event.PositionInWidget(), event.data.tap.tap_count); + break; + case blink::WebInputEvent::kGestureScrollBegin: + m_touchSelectionControllerClient->onScrollBegin(); + break; + case blink::WebInputEvent::kGestureScrollEnd: + m_touchSelectionControllerClient->onScrollEnd(); + break; + default: + break; + } + } + + host()->ForwardGestureEvent(event); +} + +void RenderWidgetHostViewQt::DidStopFlinging() +{ + m_touchSelectionControllerClient->DidStopFlinging(); } viz::ScopedSurfaceIdAllocator RenderWidgetHostViewQt::DidUpdateVisualProperties(const cc::RenderFrameMetadata &metadata) @@ -918,7 +936,7 @@ void RenderWidgetHostViewQt::OnDidUpdateVisualPropertiesComplete(const cc::Rende if (metadata.local_surface_id) m_localSurfaceIdAllocator.UpdateFromChild(*metadata.local_surface_id); - m_localSurfaceId = m_localSurfaceIdAllocator.GenerateId(); + m_localSurfaceIdAllocator.GenerateId(); host()->SendScreenRects(); if (m_pendingResize) { if (host()->SynchronizeVisualProperties()) @@ -928,7 +946,7 @@ void RenderWidgetHostViewQt::OnDidUpdateVisualPropertiesComplete(const cc::Rende QSGNode *RenderWidgetHostViewQt::updatePaintNode(QSGNode *oldNode) { - return m_compositor->updatePaintNode(oldNode); + return m_compositor->updatePaintNode(oldNode, m_delegate.get()); } void RenderWidgetHostViewQt::notifyResize() @@ -1171,7 +1189,7 @@ float RenderWidgetHostViewQt::dpiScale() const bool RenderWidgetHostViewQt::IsPopup() const { - return popup_type_ != blink::kWebPopupTypeNone; + return widget_type_ == content::WidgetType::kPopup; } void RenderWidgetHostViewQt::handleMouseEvent(QMouseEvent* event) @@ -1313,9 +1331,9 @@ void RenderWidgetHostViewQt::handleInputMethodEvent(QInputMethodEvent *ev) } if (hasSelection) { - content::RenderFrameHostImpl *frameHost = static_cast<content::RenderFrameHostImpl *>(getFocusedFrameHost()); - if (frameHost) - frameHost->GetFrameInputHandler()->SetEditableSelectionOffsets(selectionRange.start(), selectionRange.end()); + content::mojom::FrameInputHandler *frameInputHandler = getFrameInputHandler(); + if (frameInputHandler) + frameInputHandler->SetEditableSelectionOffsets(selectionRange.start(), selectionRange.end()); } int replacementLength = ev->replacementLength(); @@ -1392,16 +1410,6 @@ void RenderWidgetHostViewQt::handleInputMethodQueryEvent(QInputMethodQueryEvent ev->accept(); } -#ifndef QT_NO_ACCESSIBILITY -void RenderWidgetHostViewQt::accessibilityActiveChanged(bool active) -{ - if (active) - content::BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility(); - else - content::BrowserAccessibilityStateImpl::GetInstance()->DisableAccessibility(); -} -#endif // QT_NO_ACCESSIBILITY - void RenderWidgetHostViewQt::handleWheelEvent(QWheelEvent *ev) { if (!m_wheelAckPending) { @@ -1459,9 +1467,6 @@ void RenderWidgetHostViewQt::handleGestureEvent(QNativeGestureEvent *ev) } #endif -Q_DECLARE_LOGGING_CATEGORY(QWEBENGINE_TOUCH_HANDLING); -Q_LOGGING_CATEGORY(QWEBENGINE_TOUCH_HANDLING, "qt.webengine.touch"); - void RenderWidgetHostViewQt::handleTouchEvent(QTouchEvent *ev) { // On macOS instead of handling touch events, we use the OS provided QNativeGestureEvents. @@ -1469,7 +1474,7 @@ void RenderWidgetHostViewQt::handleTouchEvent(QTouchEvent *ev) if (ev->spontaneous()) { return; } else { - qCWarning(QWEBENGINE_TOUCH_HANDLING) + VLOG(1) << "Sending simulated touch events to Chromium does not work properly on macOS. " "Consider using QNativeGestureEvents or QMouseEvents."; } @@ -1486,11 +1491,35 @@ void RenderWidgetHostViewQt::handleTouchEvent(QTouchEvent *ev) eventTimestamp += m_eventsToNowDelta; QList<QTouchEvent::TouchPoint> touchPoints = mapTouchPointIds(ev->touchPoints()); + { + ui::MotionEvent::Action action; + switch (touchPoints[0].state()) { + case Qt::TouchPointPressed: + action = ui::MotionEvent::Action::DOWN; + break; + case Qt::TouchPointMoved: + action = ui::MotionEvent::Action::MOVE; + break; + case Qt::TouchPointReleased: + action = ui::MotionEvent::Action::UP; + break; + default: + action = ui::MotionEvent::Action::NONE; + break; + } + + MotionEventQt motionEvent(touchPoints, eventTimestamp, action, ev->modifiers(), dpiScale(), 0); + if (m_touchSelectionController->WillHandleTouchEvent(motionEvent)) { + ev->accept(); + return; + } + } switch (ev->type()) { case QEvent::TouchBegin: m_sendMotionActionDown = true; m_touchMotionStarted = true; + m_touchSelectionControllerClient->onTouchDown(); break; case QEvent::TouchUpdate: m_touchMotionStarted = true; @@ -1517,6 +1546,7 @@ void RenderWidgetHostViewQt::handleTouchEvent(QTouchEvent *ev) } case QEvent::TouchEnd: clearPreviousTouchMotionState(); + m_touchSelectionControllerClient->onTouchUp(); break; default: break; @@ -1654,11 +1684,6 @@ void RenderWidgetHostViewQt::SetNeedsBeginFrames(bool needs_begin_frames) m_compositor->setNeedsBeginFrames(needs_begin_frames); } -void RenderWidgetHostViewQt::OnBeginFrame(base::TimeTicks frame_time) -{ - host()->ProgressFlingIfNeeded(frame_time); -} - content::RenderFrameHost *RenderWidgetHostViewQt::getFocusedFrameHost() { content::RenderViewHostImpl *viewHost = content::RenderViewHostImpl::From(host()); @@ -1672,6 +1697,15 @@ content::RenderFrameHost *RenderWidgetHostViewQt::getFocusedFrameHost() return focusedFrame->current_frame_host(); } +content::mojom::FrameInputHandler *RenderWidgetHostViewQt::getFrameInputHandler() +{ + content::RenderFrameHostImpl *frameHost = static_cast<content::RenderFrameHostImpl *>(getFocusedFrameHost()); + if (!frameHost) + return nullptr; + + return frameHost->GetFrameInputHandler(); +} + ui::TextInputType RenderWidgetHostViewQt::getTextInputType() const { if (text_input_manager_ && text_input_manager_->GetTextInputState()) @@ -1696,7 +1730,7 @@ const viz::FrameSinkId &RenderWidgetHostViewQt::GetFrameSinkId() const const viz::LocalSurfaceId &RenderWidgetHostViewQt::GetLocalSurfaceId() const { - return m_localSurfaceId; + return m_localSurfaceIdAllocator.GetCurrentLocalSurfaceId(); } void RenderWidgetHostViewQt::TakeFallbackContentFrom(content::RenderWidgetHostView *view) @@ -1710,14 +1744,29 @@ void RenderWidgetHostViewQt::TakeFallbackContentFrom(content::RenderWidgetHostVi void RenderWidgetHostViewQt::EnsureSurfaceSynchronizedForLayoutTest() { - ++m_latestCaptureSequenceNumber; - if (host()) - host()->SynchronizeVisualProperties(); + NOTIMPLEMENTED(); } uint32_t RenderWidgetHostViewQt::GetCaptureSequenceNumber() const { - return m_latestCaptureSequenceNumber; + return 0; +} + +void RenderWidgetHostViewQt::ResetFallbackToFirstNavigationSurface() +{ + Q_UNIMPLEMENTED(); +} + +void RenderWidgetHostViewQt::OnRenderFrameMetadataChangedAfterActivation() +{ + content::RenderWidgetHostViewBase::OnRenderFrameMetadataChangedAfterActivation(); + + const cc::RenderFrameMetadata &metadata = host()->render_frame_metadata_provider()->LastRenderFrameMetadata(); + if (metadata.selection.start != m_selectionStart || metadata.selection.end != m_selectionEnd) { + m_selectionStart = metadata.selection.start; + m_selectionEnd = metadata.selection.end; + m_touchSelectionControllerClient->UpdateClientSelectionBounds(m_selectionStart, m_selectionEnd); + } } } // namespace QtWebEngineCore diff --git a/src/core/render_widget_host_view_qt.h b/src/core/render_widget_host_view_qt.h index a12ffe636..b9efc996f 100644 --- a/src/core/render_widget_host_view_qt.h +++ b/src/core/render_widget_host_view_qt.h @@ -50,14 +50,12 @@ #include "content/browser/renderer_host/input/mouse_wheel_phase_handler.h" #include "content/browser/renderer_host/render_widget_host_view_base.h" #include "content/browser/renderer_host/text_input_manager.h" -#include "content/common/view_messages.h" #include "gpu/ipc/common/gpu_messages.h" #include "ui/events/gesture_detection/filtered_gesture_provider.h" #include "qtwebenginecoreglobal_p.h" #include <QMap> #include <QPoint> #include <QtGlobal> -#include <QtGui/qaccessible.h> #include <QtGui/QTouchEvent> QT_BEGIN_NAMESPACE @@ -67,11 +65,21 @@ QT_END_NAMESPACE namespace content { class RenderFrameHost; class RenderWidgetHostImpl; +namespace mojom { +class FrameInputHandler; +} +} + +namespace ui { +class TouchSelectionController; } namespace QtWebEngineCore { class Compositor; +class TouchHandleDrawableClient; +class TouchSelectionControllerClientQt; +class TouchSelectionMenuController; struct MultipleMouseClickHelper { @@ -94,9 +102,6 @@ class RenderWidgetHostViewQt , public ui::GestureProviderClient , public RenderWidgetHostViewQtDelegateClient , public base::SupportsWeakPtr<RenderWidgetHostViewQt> -#ifndef QT_NO_ACCESSIBILITY - , public QAccessible::ActivationObserver -#endif // QT_NO_ACCESSIBILITY , public content::TextInputManager::Observer { public: @@ -111,8 +116,8 @@ public: RenderWidgetHostViewQtDelegate *delegate() { return m_delegate.get(); } void setDelegate(RenderWidgetHostViewQtDelegate *delegate); + WebContentsAdapterClient *adapterClient() { return m_adapterClient; } void setAdapterClient(WebContentsAdapterClient *adapterClient); - void OnBeginFrame(base::TimeTicks frame_time); void InitAsChild(gfx::NativeView) override; void InitAsPopup(content::RenderWidgetHostView*, const gfx::Rect&) override; @@ -163,6 +168,8 @@ public: void TakeFallbackContentFrom(content::RenderWidgetHostView *view) override; void EnsureSurfaceSynchronizedForLayoutTest() override; uint32_t GetCaptureSequenceNumber() const override; + void ResetFallbackToFirstNavigationSurface() override; + void DidStopFlinging() override; // Overridden from ui::GestureProviderClient. void OnGestureEvent(const ui::GestureEventData& gesture) override; @@ -209,15 +216,20 @@ public: // Overridden from content::BrowserAccessibilityDelegate content::BrowserAccessibilityManager* CreateBrowserAccessibilityManager(content::BrowserAccessibilityDelegate* delegate, bool for_root_frame) override; -#ifndef QT_NO_ACCESSIBILITY - void accessibilityActiveChanged(bool active) override; -#endif // QT_NO_ACCESSIBILITY LoadVisuallyCommittedState getLoadVisuallyCommittedState() const { return m_loadVisuallyCommittedState; } void setLoadVisuallyCommittedState(LoadVisuallyCommittedState state) { m_loadVisuallyCommittedState = state; } + // Overridden from content::RenderFrameMetadataProvider::Observer + void OnRenderFrameMetadataChangedAfterActivation() override; + gfx::SizeF lastContentsSize() const { return m_lastContentsSize; } gfx::Vector2dF lastScrollOffset() const { return m_lastScrollOffset; } + ui::TouchSelectionController *getTouchSelectionController() const { return m_touchSelectionController.get(); } + TouchSelectionControllerClientQt *getTouchSelectionControllerClient() const { return m_touchSelectionControllerClient.get(); } + content::mojom::FrameInputHandler *getFrameInputHandler(); + ui::TextInputType getTextInputType() const; + private: void processMotionEvent(const ui::MotionEvent &motionEvent); void clearPreviousTouchMotionState(); @@ -229,7 +241,6 @@ private: void selectionChanged(); content::RenderFrameHost *getFocusedFrameHost(); - ui::TextInputType getTextInputType() const; ui::FilteredGestureProvider m_gestureProvider; base::TimeDelta m_eventsToNowDelta; @@ -252,7 +263,6 @@ private: gfx::Vector2dF m_lastScrollOffset; gfx::SizeF m_lastContentsSize; - viz::LocalSurfaceId m_localSurfaceId; viz::ParentLocalSurfaceIdAllocator m_localSurfaceIdAllocator; uint m_imState; @@ -270,8 +280,12 @@ private: content::MouseWheelPhaseHandler m_mouseWheelPhaseHandler; viz::FrameSinkId m_frameSinkId; - uint32_t m_latestCaptureSequenceNumber = 0u; std::string m_editCommand; + + std::unique_ptr<TouchSelectionControllerClientQt> m_touchSelectionControllerClient; + std::unique_ptr<ui::TouchSelectionController> m_touchSelectionController; + gfx::SelectionBound m_selectionStart; + gfx::SelectionBound m_selectionEnd; }; } // namespace QtWebEngineCore diff --git a/src/core/renderer/content_renderer_client_qt.cpp b/src/core/renderer/content_renderer_client_qt.cpp index 403448b91..5aaf7ab7b 100644 --- a/src/core/renderer/content_renderer_client_qt.cpp +++ b/src/core/renderer/content_renderer_client_qt.cpp @@ -137,7 +137,8 @@ void ContentRendererClientQt::RenderThreadStarted() // Allow XMLHttpRequests from qrc to file. blink::WebURL qrc(blink::KURL("qrc:")); blink::WebString file(blink::WebString::FromASCII("file")); - blink::WebSecurityPolicy::AddOriginAccessWhitelistEntry(qrc, file, blink::WebString(), true); + blink::WebSecurityPolicy::AddOriginAccessAllowListEntry(qrc, file, blink::WebString(), true, + network::mojom::CORSOriginAccessMatchPriority::kDefaultPriority); } void ContentRendererClientQt::RenderViewCreated(content::RenderView* render_view) @@ -230,6 +231,7 @@ void ContentRendererClientQt::GetNavigationErrorStringsInternal(content::RenderF error_page::LocalizedError::GetStrings( error.reason(), error.domain(), error.url(), isPost, error.stale_copy_in_cache(), false, false, + error_page::LocalizedError::OfflineContentOnNetErrorFeatureState::kDisabled, locale, std::unique_ptr<error_page::ErrorPageParams>(), &errorStrings); resourceId = IDR_NET_ERROR_HTML; diff --git a/src/core/renderer/content_settings_observer_qt.cpp b/src/core/renderer/content_settings_observer_qt.cpp index 045098457..c1495e1fe 100644 --- a/src/core/renderer/content_settings_observer_qt.cpp +++ b/src/core/renderer/content_settings_observer_qt.cpp @@ -93,8 +93,8 @@ bool ContentSettingsObserverQt::OnMessageReceived(const IPC::Message& message) return handled; } -void ContentSettingsObserverQt::DidCommitProvisionalLoad(bool /*is_new_navigation*/, - bool is_same_document_navigation) +void ContentSettingsObserverQt::DidCommitProvisionalLoad(bool is_same_document_navigation, + ui::PageTransition /*transition*/) { blink::WebLocalFrame* frame = render_frame()->GetWebFrame(); if (frame->Parent()) diff --git a/src/core/renderer/content_settings_observer_qt.h b/src/core/renderer/content_settings_observer_qt.h index 981655f20..a9bee3d2d 100644 --- a/src/core/renderer/content_settings_observer_qt.h +++ b/src/core/renderer/content_settings_observer_qt.h @@ -80,8 +80,8 @@ private: // RenderFrameObserver implementation: bool OnMessageReceived(const IPC::Message &message) override; - void DidCommitProvisionalLoad(bool is_new_navigation, - bool is_same_document_navigation) override; + void DidCommitProvisionalLoad(bool is_same_document_navigation, + ui::PageTransition transition) override; void OnDestruct() override; // Message handlers. diff --git a/src/core/renderer/user_resource_controller.cpp b/src/core/renderer/user_resource_controller.cpp index 920fda72e..46f5de2c2 100644 --- a/src/core/renderer/user_resource_controller.cpp +++ b/src/core/renderer/user_resource_controller.cpp @@ -103,7 +103,7 @@ static bool scriptMatchesURL(const UserScriptData &scriptData, const GURL &url) matchFound = false; for (auto it = scriptData.urlPatterns.begin(), end = scriptData.urlPatterns.end(); it != end; ++it) { URLPattern urlPattern(validUserScriptSchemes()); - if (urlPattern.Parse(*it) == URLPattern::PARSE_SUCCESS && urlPattern.MatchesURL(url)) + if (urlPattern.Parse(*it) == URLPattern::ParseResult::kSuccess && urlPattern.MatchesURL(url)) matchFound = true; } if (!matchFound) @@ -137,7 +137,7 @@ public: private: // RenderFrameObserver implementation. - void DidCommitProvisionalLoad(bool is_new_navigation, bool is_same_document_navigation) override; + void DidCommitProvisionalLoad(bool is_same_document_navigation, ui::PageTransition transition) override; void DidClearWindowObject() override; void DidFinishDocumentLoad() override; void DidFinishLoad() override; @@ -230,8 +230,8 @@ UserResourceController::RenderViewObserverHelper::RenderViewObserverHelper(conte { } -void UserResourceController::RenderFrameObserverHelper::DidCommitProvisionalLoad(bool /* is_new_navigation */, - bool is_same_document_navigation) +void UserResourceController::RenderFrameObserverHelper::DidCommitProvisionalLoad(bool is_same_document_navigation, + ui::PageTransition /*transitionbool*/) { if (is_same_document_navigation) return; diff --git a/src/core/renderer/web_channel_ipc_transport.cpp b/src/core/renderer/web_channel_ipc_transport.cpp index 1f496e810..f1da8289a 100644 --- a/src/core/renderer/web_channel_ipc_transport.cpp +++ b/src/core/renderer/web_channel_ipc_transport.cpp @@ -137,6 +137,8 @@ void WebChannelTransport::NativeQtSendMessage(gin::Arguments *args) args->ThrowTypeError("Missing argument"); return; } + v8::Isolate *isolate = blink::MainThreadIsolate(); + v8::HandleScope handleScope(isolate); if (!jsonValue->IsString()) { args->ThrowTypeError("Expected string"); @@ -144,10 +146,10 @@ void WebChannelTransport::NativeQtSendMessage(gin::Arguments *args) } v8::Local<v8::String> jsonString = v8::Local<v8::String>::Cast(jsonValue); - QByteArray json(jsonString->Utf8Length(), 0); - jsonString->WriteUtf8(json.data(), json.size(), - nullptr, - v8::String::REPLACE_INVALID_UTF8); + QByteArray json(jsonString->Utf8Length(isolate), 0); + jsonString->WriteUtf8(isolate, + json.data(), json.size(), + nullptr, v8::String::REPLACE_INVALID_UTF8); QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(json, &error); diff --git a/src/core/renderer_host/pepper/pepper_isolated_file_system_message_filter.cpp b/src/core/renderer_host/pepper/pepper_isolated_file_system_message_filter.cpp index 2c8b1246a..5d7c3973f 100644 --- a/src/core/renderer_host/pepper/pepper_isolated_file_system_message_filter.cpp +++ b/src/core/renderer_host/pepper/pepper_isolated_file_system_message_filter.cpp @@ -45,8 +45,10 @@ #include "pepper_isolated_file_system_message_filter.h" #include "base/macros.h" +#include "base/task/post_task.h" #include "chrome/common/chrome_switches.h" #include "content/public/browser/browser_ppapi_host.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/child_process_security_policy.h" #include "content/public/browser/render_view_host.h" @@ -83,7 +85,7 @@ scoped_refptr<base::TaskRunner> PepperIsolatedFileSystemMessageFilter::OverrideT { // In order to reach ExtensionSystem, we need to get ProfileManager first. // ProfileManager lives in UI thread, so we need to do this in UI thread. - return content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::UI); + return base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::UI}); } int32_t PepperIsolatedFileSystemMessageFilter::OnResourceMessageReceived(const IPC::Message& msg, ppapi::host::HostMessageContext *context) diff --git a/src/core/resource_bundle_qt.cpp b/src/core/resource_bundle_qt.cpp index 428faa34e..dc7507f34 100644 --- a/src/core/resource_bundle_qt.cpp +++ b/src/core/resource_bundle_qt.cpp @@ -71,6 +71,7 @@ gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id) return GetEmptyImage(); } +// static bool ResourceBundle::LocaleDataPakExists(const std::string& locale) { #if defined(OS_LINUX) diff --git a/src/core/service/service_qt.cpp b/src/core/service/service_qt.cpp index 30ed269e8..bb842232c 100644 --- a/src/core/service/service_qt.cpp +++ b/src/core/service/service_qt.cpp @@ -45,7 +45,9 @@ #include "service_qt.h" #include "base/no_destructor.h" +#include "base/task/post_task.h" #include "components/spellcheck/spellcheck_buildflags.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/content_browser_client.h" #include "services/service_manager/public/cpp/binder_registry.h" @@ -82,10 +84,10 @@ private: ServiceQt::IOThreadContext::IOThreadContext() { - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner = - content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::UI); #if BUILDFLAG(ENABLE_SPELLCHECK) - m_registry_with_source_info.AddInterface(base::Bind(&SpellCheckHostChromeImpl::Create), ui_task_runner); + scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner = + base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::UI}); + m_registry_with_source_info.AddInterface(base::BindRepeating(&SpellCheckHostChromeImpl::Create), ui_task_runner); #endif } @@ -97,7 +99,7 @@ void ServiceQt::IOThreadContext::BindConnector(service_manager::mojom::Connector // on the IO thread. Post a task instead. As long as this task is posted // before any code attempts to connect to the chrome service, there's no // race. - content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::IO)->PostTask( + base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::IO})->PostTask( FROM_HERE, base::BindOnce(&IOThreadContext::BindConnectorOnIOThread, base::Unretained(this), diff --git a/src/core/net/qrc_protocol_handler_qt.h b/src/core/touch_handle_drawable_client.h index f2849c1ef..42d907d75 100644 --- a/src/core/net/qrc_protocol_handler_qt.h +++ b/src/core/touch_handle_drawable_client.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** 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. @@ -37,34 +37,24 @@ ** ****************************************************************************/ -#ifndef QRC_PROTOCOL_HANDLER_QT_H_ -#define QRC_PROTOCOL_HANDLER_QT_H_ +#ifndef TOUCH_HANDLE_DRAWABLE_CLIENT_H +#define TOUCH_HANDLE_DRAWABLE_CLIENT_H -#include "net/url_request/url_request_job_factory.h" - -namespace net { - -class NetworkDelegate; -class URLRequestJob; - -} // namespace +#include "qtwebenginecoreglobal_p.h" +#include <QRect> namespace QtWebEngineCore { -extern const char kQrcSchemeQt[]; - -// Implements a ProtocolHandler for qrc file jobs. If |network_delegate_| is NULL, -// then all file requests will fail with ERR_ACCESS_DENIED. -class QrcProtocolHandlerQt : public net::URLRequestJobFactory::ProtocolHandler { - +class QWEBENGINECORE_PRIVATE_EXPORT TouchHandleDrawableClient { public: - QrcProtocolHandlerQt(); - net::URLRequestJob *MaybeCreateJob(net::URLRequest *request, net::NetworkDelegate *networkDelegate) const override; + virtual ~TouchHandleDrawableClient() { } -private: - DISALLOW_COPY_AND_ASSIGN(QrcProtocolHandlerQt); + virtual void setImage(int orientation) = 0; + virtual void setBounds(const QRect &bounds) = 0; + virtual void setVisible(bool visible) = 0; + virtual void setOpacity(float opacity) = 0; }; } // namespace QtWebEngineCore -#endif // QRC_PROTOCOL_HANDLER_QT_H_ +#endif // TOUCH_HANDLE_DRAWABLE_CLIENT_H diff --git a/src/core/touch_handle_drawable_qt.cpp b/src/core/touch_handle_drawable_qt.cpp new file mode 100644 index 000000000..66b1cf40e --- /dev/null +++ b/src/core/touch_handle_drawable_qt.cpp @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +// Copyright 2015 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.Chromium file. + +// This implementation is based on chromium/ui/touch_selection/touch_handle_drawable_aura.cc + +#include "render_widget_host_view_qt.h" +#include "touch_handle_drawable_client.h" +#include "touch_handle_drawable_qt.h" +#include "type_conversion.h" +#include "web_contents_adapter_client.h" + +#include "ui/gfx/image/image.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/resources/grit/ui_resources.h" + +namespace QtWebEngineCore { + +namespace { +// The distance by which a handle image is offset from the focal point (i.e. +// text baseline) downwards. +const int kSelectionHandleVerticalVisualOffset = 2; + +// The padding around the selection handle image can be used to extend the +// handle window so that touch events near the selection handle image are +// targeted to the selection handle window. +const int kSelectionHandlePadding = 0; + +// Epsilon value used to compare float values to zero. +const float kEpsilon = 1e-8f; + +// Returns the appropriate handle image based on the handle orientation. +gfx::Image* GetHandleImage(ui::TouchHandleOrientation orientation) +{ + int resource_id = 0; + switch (orientation) { + case ui::TouchHandleOrientation::LEFT: + resource_id = IDR_TEXT_SELECTION_HANDLE_LEFT; + break; + case ui::TouchHandleOrientation::CENTER: + resource_id = IDR_TEXT_SELECTION_HANDLE_CENTER; + break; + case ui::TouchHandleOrientation::RIGHT: + resource_id = IDR_TEXT_SELECTION_HANDLE_RIGHT; + break; + case ui::TouchHandleOrientation::UNDEFINED: + NOTREACHED() << "Invalid touch handle bound type."; + return nullptr; + }; + return &ui::ResourceBundle::GetSharedInstance().GetImageNamed(resource_id); +} + +bool IsNearlyZero(float value) +{ + return std::abs(value) < kEpsilon; +} + +} // namespace + +TouchHandleDrawableQt::TouchHandleDrawableQt(RenderWidgetHostViewQt *rwhv) + : m_rwhv(rwhv) + , m_enabled(false) + , m_alpha(0) + , m_orientation(ui::TouchHandleOrientation::UNDEFINED) +{ + QMap<int, QImage> images; + for (int orientation = 0; orientation < static_cast<int>(ui::TouchHandleOrientation::UNDEFINED); ++orientation) { + gfx::Image* image = GetHandleImage(static_cast<ui::TouchHandleOrientation>(orientation)); + images.insert(orientation, toQImage(image->AsBitmap())); + } + + Q_ASSERT(m_rwhv); + Q_ASSERT(m_rwhv->adapterClient()); + m_client.reset(m_rwhv->adapterClient()->createTouchHandle(images)); +} + +TouchHandleDrawableQt::~TouchHandleDrawableQt() +{ +} + +void TouchHandleDrawableQt::UpdateBounds() +{ + if (!m_client) + return; + + gfx::RectF newBounds = m_relativeBounds; + newBounds.Offset(m_originPosition.x(), m_originPosition.y()); + m_client->setBounds(toQt(gfx::ToEnclosingRect(newBounds))); +} + +bool TouchHandleDrawableQt::IsVisible() const +{ + return m_enabled && !IsNearlyZero(m_alpha); +} + +void TouchHandleDrawableQt::SetEnabled(bool enabled) +{ + if (!m_client) + return; + + if (enabled == m_enabled) + return; + + m_enabled = enabled; + m_client->setVisible(enabled); +} + +void TouchHandleDrawableQt::SetOrientation(ui::TouchHandleOrientation orientation, bool mirror_vertical, bool mirror_horizontal) +{ + if (!m_client) + return; + + // TODO: Implement adaptive handle orientation logic + DCHECK(!mirror_vertical); + DCHECK(!mirror_horizontal); + + if (m_orientation == orientation) + return; + m_orientation = orientation; + gfx::Image* image = GetHandleImage(orientation); + m_client->setImage(static_cast<int>(orientation)); + + // Calculate the relative bounds. + gfx::Size image_size = image->Size(); + int window_width = image_size.width() + 2 * kSelectionHandlePadding; + int window_height = image_size.height() + 2 * kSelectionHandlePadding; + m_relativeBounds = + gfx::RectF(-kSelectionHandlePadding, + kSelectionHandleVerticalVisualOffset - kSelectionHandlePadding, + window_width, window_height); + UpdateBounds(); +} + +void TouchHandleDrawableQt::SetOrigin(const gfx::PointF& position) +{ + m_originPosition = position; + UpdateBounds(); +} + +void TouchHandleDrawableQt::SetAlpha(float alpha) +{ + if (!m_client) + return; + + if (alpha == m_alpha) + return; + + m_alpha = alpha; + m_client->setOpacity(m_alpha); + m_client->setVisible(IsVisible()); +} + +gfx::RectF TouchHandleDrawableQt::GetVisibleBounds() const +{ + gfx::RectF bounds = m_relativeBounds; + bounds.Offset(m_originPosition.x(), m_originPosition.y()); + + gfx::RectF visibleBounds(bounds); + visibleBounds.Inset(kSelectionHandlePadding, + kSelectionHandlePadding + kSelectionHandleVerticalVisualOffset, + kSelectionHandlePadding, + kSelectionHandlePadding); + return visibleBounds; +} + +float TouchHandleDrawableQt::GetDrawableHorizontalPaddingRatio() const +{ + // Qt does not have any transparent padding for its handle drawable. + return 0.0; +} + +} // namespace QtWebEngineCore diff --git a/src/core/touch_handle_drawable_qt.h b/src/core/touch_handle_drawable_qt.h new file mode 100644 index 000000000..46fa217b7 --- /dev/null +++ b/src/core/touch_handle_drawable_qt.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TOUCH_HANDLE_DRAWABLE_QT_H +#define TOUCH_HANDLE_DRAWABLE_QT_H + +#include "ui/touch_selection/touch_handle.h" +#include "ui/touch_selection/touch_handle_orientation.h" + +#include <QtCore/QScopedPointer> + +namespace QtWebEngineCore { + +class RenderWidgetHostViewQt; +class TouchHandleDrawableClient; + +class TouchHandleDrawableQt : public ui::TouchHandleDrawable +{ +public: + explicit TouchHandleDrawableQt(RenderWidgetHostViewQt *rwhv); + ~TouchHandleDrawableQt() override; + +private: + void UpdateBounds(); + bool IsVisible() const; + + // ui::TouchHandleDrawable overrides + void SetEnabled(bool enabled) override; + void SetOrientation(ui::TouchHandleOrientation orientation, + bool mirror_vertical, + bool mirror_horizontal) override; + void SetOrigin(const gfx::PointF& position) override; + void SetAlpha(float alpha) override; + gfx::RectF GetVisibleBounds() const override; + float GetDrawableHorizontalPaddingRatio() const override; + + RenderWidgetHostViewQt *m_rwhv; + QScopedPointer<TouchHandleDrawableClient> m_client; + + bool m_enabled; + float m_alpha; + ui::TouchHandleOrientation m_orientation; + gfx::RectF m_relativeBounds; + gfx::PointF m_originPosition; + + DISALLOW_COPY_AND_ASSIGN(TouchHandleDrawableQt); +}; + +} // namespace QtWebEngineCore + +#endif // TOUCH_HANDLE_DRAWABLE_QT_H diff --git a/src/core/touch_selection_controller_client_qt.cpp b/src/core/touch_selection_controller_client_qt.cpp new file mode 100644 index 000000000..da3c78b8a --- /dev/null +++ b/src/core/touch_selection_controller_client_qt.cpp @@ -0,0 +1,343 @@ +/**************************************************************************** +** +** 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 "render_widget_host_view_qt.h" +#include "touch_handle_drawable_qt.h" +#include "touch_selection_controller_client_qt.h" +#include "touch_selection_menu_controller.h" +#include "type_conversion.h" +#include "web_contents_adapter.h" +#include "web_contents_adapter_client.h" + +#include "content/browser/frame_host/render_frame_host_impl.h" +#include "content/browser/renderer_host/render_widget_host_impl.h" +#include "ui/gfx/geometry/size_conversions.h" + +#include <QClipboard> +#include <QGuiApplication> + +namespace QtWebEngineCore { + +TouchSelectionControllerClientQt::TouchSelectionControllerClientQt(RenderWidgetHostViewQt *rwhv) + : m_rwhv(rwhv) + , m_menuController(new TouchSelectionMenuController(this)) + , m_menuShowing(false) + , m_menuRequested(false) + , m_touchDown(false) + , m_scrollInProgress(false) + , m_handleDragInProgress(false) +{ + Q_ASSERT(rwhv); +} + +TouchSelectionControllerClientQt::~TouchSelectionControllerClientQt() +{ +} + +bool TouchSelectionControllerClientQt::handleContextMenu(const content::ContextMenuParams& params) +{ + if ((params.source_type == ui::MENU_SOURCE_LONG_PRESS || + params.source_type == ui::MENU_SOURCE_LONG_TAP) && + params.is_editable && params.selection_text.empty()) { + m_menuRequested = true; + updateMenu(); + return true; + } + + const bool from_touch = params.source_type == ui::MENU_SOURCE_LONG_PRESS || + params.source_type == ui::MENU_SOURCE_LONG_TAP || + params.source_type == ui::MENU_SOURCE_TOUCH; + if (from_touch && !params.selection_text.empty()) + return true; + + GetTouchSelectionController()->HideAndDisallowShowingAutomatically(); + return false; +} + +void TouchSelectionControllerClientQt::onTouchDown() +{ + m_touchDown = true; + updateMenu(); +} + +void TouchSelectionControllerClientQt::onTouchUp() +{ + m_touchDown = false; + updateMenu(); +} + +void TouchSelectionControllerClientQt::onScrollBegin() +{ + m_scrollInProgress = true; + GetTouchSelectionController()->SetTemporarilyHidden(true); + updateMenu(); +} + +void TouchSelectionControllerClientQt::onScrollEnd() +{ + m_scrollInProgress = false; + GetTouchSelectionController()->SetTemporarilyHidden(false); + updateMenu(); +} + +bool TouchSelectionControllerClientQt::IsCommandIdEnabled(int command_id) const +{ + bool editable = m_rwhv->getTextInputType() != ui::TEXT_INPUT_TYPE_NONE; + bool readable = m_rwhv->getTextInputType() != ui::TEXT_INPUT_TYPE_PASSWORD; + bool hasSelection = !m_rwhv->GetSelectedText().empty(); + + switch (command_id) { + case TouchSelectionMenuController::Cut: + return editable && readable && hasSelection; + case TouchSelectionMenuController::Copy: + return readable && hasSelection; + case TouchSelectionMenuController::Paste: + return editable && !QGuiApplication::clipboard()->text().isEmpty(); + default: + return false; + } +} + +void TouchSelectionControllerClientQt::ExecuteCommand(int command_id, int event_flags) +{ + Q_UNUSED(event_flags); + GetTouchSelectionController()->HideAndDisallowShowingAutomatically(); + + WebContentsAdapterClient *adapterClient = m_rwhv->adapterClient(); + Q_ASSERT(adapterClient); + WebContentsAdapter *adapter = adapterClient->webContentsAdapter(); + Q_ASSERT(adapter); + + switch (command_id) { + case TouchSelectionMenuController::Cut: + adapter->cut(); + break; + case TouchSelectionMenuController::Copy: + adapter->copy(); + break; + case TouchSelectionMenuController::Paste: + adapter->paste(); + break; + default: + NOTREACHED(); + break; + } +} + +void TouchSelectionControllerClientQt::RunContextMenu() +{ + gfx::RectF anchorRect = GetTouchSelectionController()->GetRectBetweenBounds(); + gfx::PointF anchorPoint = gfx::PointF(anchorRect.CenterPoint().x(), anchorRect.y()); + + content::RenderWidgetHostImpl *host = m_rwhv->host(); + host->ShowContextMenuAtPoint(gfx::ToRoundedPoint(anchorPoint), + ui::MENU_SOURCE_TOUCH_EDIT_MENU); + + // Hide selection handles after getting rect-between-bounds from touch + // selection controller; otherwise, rect would be empty and the above + // calculations would be invalid. + GetTouchSelectionController()->HideAndDisallowShowingAutomatically(); +} + +void TouchSelectionControllerClientQt::DidStopFlinging() +{ + onScrollEnd(); +} + +void TouchSelectionControllerClientQt::UpdateClientSelectionBounds(const gfx::SelectionBound& start, + const gfx::SelectionBound& end) +{ + UpdateClientSelectionBounds(start, end, this, this); +} + +void TouchSelectionControllerClientQt::UpdateClientSelectionBounds(const gfx::SelectionBound& start, + const gfx::SelectionBound& end, + ui::TouchSelectionControllerClient* client, + ui::TouchSelectionMenuClient* menu_client) +{ + Q_UNUSED(client); + Q_UNUSED(menu_client); + + GetTouchSelectionController()->OnSelectionBoundsChanged(start, end); +} + +void TouchSelectionControllerClientQt::InvalidateClient(ui::TouchSelectionControllerClient* client) +{ + Q_UNUSED(client); +} + +ui::TouchSelectionController* TouchSelectionControllerClientQt::GetTouchSelectionController() +{ + return m_rwhv->getTouchSelectionController(); +} + +void TouchSelectionControllerClientQt::AddObserver(Observer* observer) +{ + Q_UNUSED(observer); +} + +void TouchSelectionControllerClientQt::RemoveObserver(Observer* observer) +{ + Q_UNUSED(observer); +} + +bool TouchSelectionControllerClientQt::SupportsAnimation() const +{ + return false; +} + +void TouchSelectionControllerClientQt::SetNeedsAnimate() +{ + NOTREACHED(); +} + +void TouchSelectionControllerClientQt::MoveCaret(const gfx::PointF& position) +{ + content::mojom::FrameInputHandler *frameInputHandler = m_rwhv->getFrameInputHandler(); + if (!frameInputHandler) + return; + + frameInputHandler->MoveCaret(gfx::ToRoundedPoint(position)); +} + +void TouchSelectionControllerClientQt::MoveRangeSelectionExtent(const gfx::PointF& extent) +{ + content::mojom::FrameInputHandler *frameInputHandler = m_rwhv->getFrameInputHandler(); + if (!frameInputHandler) + return; + + frameInputHandler->MoveRangeSelectionExtent(gfx::ToRoundedPoint(extent)); +} + +void TouchSelectionControllerClientQt::SelectBetweenCoordinates(const gfx::PointF& base, const gfx::PointF& extent) +{ + content::mojom::FrameInputHandler *frameInputHandler = m_rwhv->getFrameInputHandler(); + if (!frameInputHandler) + return; + + frameInputHandler->SelectRange(gfx::ToRoundedPoint(base), gfx::ToRoundedPoint(extent)); +} + +void TouchSelectionControllerClientQt::OnSelectionEvent(ui::SelectionEventType event) +{ + switch (event) { + case ui::SELECTION_HANDLES_SHOWN: + m_menuRequested = true; + break; + case ui::INSERTION_HANDLE_SHOWN: + break; + case ui::SELECTION_HANDLES_CLEARED: + case ui::INSERTION_HANDLE_CLEARED: + m_menuRequested = false; + break; + case ui::SELECTION_HANDLE_DRAG_STARTED: + case ui::INSERTION_HANDLE_DRAG_STARTED: + m_handleDragInProgress = true; + break; + case ui::SELECTION_HANDLE_DRAG_STOPPED: + case ui::INSERTION_HANDLE_DRAG_STOPPED: + m_handleDragInProgress = false; + break; + case ui::SELECTION_HANDLES_MOVED: + case ui::INSERTION_HANDLE_MOVED: + break; + case ui::INSERTION_HANDLE_TAPPED: + m_menuRequested = !m_menuRequested; + break; + } + + updateMenu(); +} + +void TouchSelectionControllerClientQt::OnDragUpdate(const gfx::PointF& position) +{ + Q_UNUSED(position); +} + +std::unique_ptr<ui::TouchHandleDrawable> TouchSelectionControllerClientQt::CreateDrawable() +{ + return std::unique_ptr<ui::TouchHandleDrawable>(new TouchHandleDrawableQt(m_rwhv)); +} + +void TouchSelectionControllerClientQt::DidScroll() +{ +} + +void TouchSelectionControllerClientQt::showMenu() +{ + gfx::RectF rect = GetTouchSelectionController()->GetRectBetweenBounds(); + gfx::PointF origin = rect.origin(); + gfx::PointF bottom_right = rect.bottom_right(); + + gfx::Vector2dF diagonal = bottom_right - origin; + gfx::SizeF size(diagonal.x(), diagonal.y()); + gfx::RectF anchor_rect(origin, size); + + // Calculate maximum handle image size; + gfx::SizeF max_handle_size = GetTouchSelectionController()->GetStartHandleRect().size(); + max_handle_size.SetToMax(GetTouchSelectionController()->GetEndHandleRect().size()); + + WebContentsAdapterClient *adapterClient = m_rwhv->adapterClient(); + Q_ASSERT(adapterClient); + adapterClient->showTouchSelectionMenu(m_menuController.get(), + QRect(toQt(gfx::ToEnclosingRect(anchor_rect))), + QSize(toQt(gfx::ToRoundedSize(max_handle_size)))); + m_menuShowing = true; +} + +void TouchSelectionControllerClientQt::hideMenu() +{ + WebContentsAdapterClient *adapterClient = m_rwhv->adapterClient(); + Q_ASSERT(adapterClient); + adapterClient->hideTouchSelectionMenu(); + m_menuShowing = false; +} + +void TouchSelectionControllerClientQt::updateMenu() +{ + if (m_menuShowing) + hideMenu(); + + if (m_menuRequested && !m_touchDown && + !m_scrollInProgress && !m_handleDragInProgress) { + showMenu(); + } +} + +} // namespace QtWebEngineCore diff --git a/src/core/touch_selection_controller_client_qt.h b/src/core/touch_selection_controller_client_qt.h new file mode 100644 index 000000000..cdc45cac3 --- /dev/null +++ b/src/core/touch_selection_controller_client_qt.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TOUCH_SELECTION_CONTROLLER_CLIENT_QT_H +#define TOUCH_SELECTION_CONTROLLER_CLIENT_QT_H + +#include "content/public/browser/touch_selection_controller_client_manager.h" +#include "content/public/common/context_menu_params.h" +#include "ui/touch_selection/touch_selection_controller.h" +#include "ui/touch_selection/touch_selection_menu_runner.h" + +#include <QtCore/QScopedPointer> + +namespace QtWebEngineCore { + +class RenderWidgetHostViewQt; +class TouchSelectionMenuController; + +class TouchSelectionControllerClientQt + : public ui::TouchSelectionControllerClient + , public ui::TouchSelectionMenuClient + , public content::TouchSelectionControllerClientManager +{ +public: + explicit TouchSelectionControllerClientQt(RenderWidgetHostViewQt *rwhv); + ~TouchSelectionControllerClientQt() override; + + void UpdateClientSelectionBounds(const gfx::SelectionBound& start, + const gfx::SelectionBound& end); + bool handleContextMenu(const content::ContextMenuParams& params); + void onTouchDown(); + void onTouchUp(); + void onScrollBegin(); + void onScrollEnd(); + + // ui::TouchSelectionMenuClient overrides + bool IsCommandIdEnabled(int command_id) const override; + void ExecuteCommand(int command_id, int event_flags) override; + void RunContextMenu() override; + + // content::TouchSelectionControllerClientManager overrides + void DidStopFlinging() override; + void UpdateClientSelectionBounds(const gfx::SelectionBound& start, + const gfx::SelectionBound& end, + ui::TouchSelectionControllerClient* client, + ui::TouchSelectionMenuClient* menu_client) override; + void InvalidateClient(ui::TouchSelectionControllerClient* client) override; + ui::TouchSelectionController* GetTouchSelectionController() override; + void AddObserver(Observer* observer) override; + void RemoveObserver(Observer* observer) override; + + // ui::TouchSelectionControllerClient overrides + bool SupportsAnimation() const override; + void SetNeedsAnimate() override; + void MoveCaret(const gfx::PointF& position) override; + void MoveRangeSelectionExtent(const gfx::PointF& extent) override; + void SelectBetweenCoordinates(const gfx::PointF& base, const gfx::PointF& extent) override; + void OnSelectionEvent(ui::SelectionEventType event) override; + void OnDragUpdate(const gfx::PointF& position) override; + std::unique_ptr<ui::TouchHandleDrawable> CreateDrawable() override; + void DidScroll() override; + +private: + void showMenu(); + void hideMenu(); + void updateMenu(); + + RenderWidgetHostViewQt *m_rwhv; + QScopedPointer<TouchSelectionMenuController> m_menuController; + + bool m_menuShowing; + bool m_menuRequested; + bool m_touchDown; + bool m_scrollInProgress; + bool m_handleDragInProgress; +}; + +} // namespace QtWebEngineCore + +#endif // TOUCH_SELECTION_CONTROLLER_CLIENT_QT_H diff --git a/src/core/touch_selection_menu_controller.cpp b/src/core/touch_selection_menu_controller.cpp new file mode 100644 index 000000000..cdec9a064 --- /dev/null +++ b/src/core/touch_selection_menu_controller.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** 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 "touch_selection_controller_client_qt.h" +#include "touch_selection_menu_controller.h" + +namespace QtWebEngineCore { + +TouchSelectionMenuController::TouchSelectionMenuController(TouchSelectionControllerClientQt *touchSelectionControllerClient) + : m_touchSelectionControllerClient(touchSelectionControllerClient) +{ +} + +TouchSelectionMenuController::~TouchSelectionMenuController() +{ +} + +int TouchSelectionMenuController::buttonCount() +{ + int buttonCount = 1; + + for (int commandId = 0; commandId <= static_cast<int>(Paste); ++commandId) { + if (m_touchSelectionControllerClient->IsCommandIdEnabled(commandId)) + buttonCount++; + } + + return buttonCount; +} + +bool TouchSelectionMenuController::isCommandEnabled(TouchSelectionCommand command) +{ + return m_touchSelectionControllerClient->IsCommandIdEnabled(static_cast<int>(command)); +} + +void TouchSelectionMenuController::cut() +{ + m_touchSelectionControllerClient->ExecuteCommand(static_cast<int>(Cut), 0); +} + +void TouchSelectionMenuController::copy() +{ + m_touchSelectionControllerClient->ExecuteCommand(static_cast<int>(Copy), 0); +} + +void TouchSelectionMenuController::paste() +{ + m_touchSelectionControllerClient->ExecuteCommand(static_cast<int>(Paste), 0); +} + +void TouchSelectionMenuController::runContextMenu() +{ + return m_touchSelectionControllerClient->RunContextMenu(); +} + +} // namespace QtWebEngineCore diff --git a/src/core/touch_selection_menu_controller.h b/src/core/touch_selection_menu_controller.h new file mode 100644 index 000000000..fd61ae709 --- /dev/null +++ b/src/core/touch_selection_menu_controller.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TOUCH_SELECTION_MENU_CONTROLLER_H +#define TOUCH_SELECTION_MENU_CONTROLLER_H + +#include "qtwebenginecoreglobal_p.h" +#include <QtCore/QObject> + +namespace QtWebEngineCore { + +class TouchSelectionControllerClientQt; + +class QWEBENGINECORE_PRIVATE_EXPORT TouchSelectionMenuController : public QObject { + Q_OBJECT +public: + enum TouchSelectionCommand { + Cut, + Copy, + Paste + }; + + TouchSelectionMenuController(TouchSelectionControllerClientQt *touchSelectionControllerClient); + ~TouchSelectionMenuController(); + int buttonCount(); + bool isCommandEnabled(TouchSelectionCommand); + +public Q_SLOTS: + void cut(); + void copy(); + void paste(); + void runContextMenu(); + +private: + TouchSelectionControllerClientQt *m_touchSelectionControllerClient; +}; + +} // namespace QtWebEngineCore + +#endif // TOUCH_SELECTION_CONTROLLER_CLIENT_QT_H diff --git a/src/core/type_conversion.cpp b/src/core/type_conversion.cpp index d35426ac3..ef16e2add 100644 --- a/src/core/type_conversion.cpp +++ b/src/core/type_conversion.cpp @@ -42,7 +42,9 @@ #include <content/public/common/favicon_url.h> #include <ui/events/event_constants.h> #include <ui/gfx/image/image_skia.h> + #include <QtCore/qcoreapplication.h> +#include <QtGui/qmatrix4x4.h> namespace QtWebEngineCore { @@ -131,7 +133,7 @@ QImage toQImage(const SkBitmap &bitmap) QImage toQImage(const gfx::ImageSkiaRep &imageSkiaRep) { - QImage image = toQImage(imageSkiaRep.sk_bitmap()); + QImage image = toQImage(imageSkiaRep.GetBitmap()); if (!image.isNull() && imageSkiaRep.scale() != 1.0f) image.setDevicePixelRatio(imageSkiaRep.scale()); return image; @@ -243,4 +245,15 @@ FaviconInfo toFaviconInfo(const content::FaviconURL &favicon_url) return info; } +void convertToQt(const SkMatrix44 &m, QMatrix4x4 &c) +{ + QMatrix4x4 qtMatrix( + m.get(0, 0), m.get(0, 1), m.get(0, 2), m.get(0, 3), + m.get(1, 0), m.get(1, 1), m.get(1, 2), m.get(1, 3), + m.get(2, 0), m.get(2, 1), m.get(2, 2), m.get(2, 3), + m.get(3, 0), m.get(3, 1), m.get(3, 2), m.get(3, 3)); + qtMatrix.optimize(); + c = qtMatrix; +} + } // namespace QtWebEngineCore diff --git a/src/core/type_conversion.h b/src/core/type_conversion.h index afc3c3336..96b4ecadc 100644 --- a/src/core/type_conversion.h +++ b/src/core/type_conversion.h @@ -45,7 +45,6 @@ #include <QDir> #include <QIcon> #include <QImage> -#include <QMatrix4x4> #include <QNetworkCookie> #include <QRect> #include <QString> @@ -64,6 +63,8 @@ #include "ui/gfx/geometry/rect_f.h" #include "url/gurl.h" +QT_FORWARD_DECLARE_CLASS(QMatrix4x4) + namespace content { struct FaviconURL; } @@ -198,16 +199,7 @@ SkBitmap toSkBitmap(const QImage &image); QIcon toQIcon(const std::vector<SkBitmap> &bitmaps); -inline QMatrix4x4 toQt(const SkMatrix44 &m) -{ - QMatrix4x4 qtMatrix( - m.get(0, 0), m.get(0, 1), m.get(0, 2), m.get(0, 3), - m.get(1, 0), m.get(1, 1), m.get(1, 2), m.get(1, 3), - m.get(2, 0), m.get(2, 1), m.get(2, 2), m.get(2, 3), - m.get(3, 0), m.get(3, 1), m.get(3, 2), m.get(3, 3)); - qtMatrix.optimize(); - return qtMatrix; -} +void convertToQt(const SkMatrix44 &m, QMatrix4x4 &c); inline QDateTime toQt(base::Time time) { diff --git a/src/core/visited_links_manager_qt.cpp b/src/core/visited_links_manager_qt.cpp index ac27446b8..d4885e8e8 100644 --- a/src/core/visited_links_manager_qt.cpp +++ b/src/core/visited_links_manager_qt.cpp @@ -39,7 +39,6 @@ #include "visited_links_manager_qt.h" -#include "profile_adapter.h" #include "content_browser_client_qt.h" #include "profile_qt.h" #include "type_conversion.h" @@ -106,14 +105,13 @@ static void ensureDirectoryExists(const base::FilePath &path) errorstr.c_str()); } -VisitedLinksManagerQt::VisitedLinksManagerQt(ProfileAdapter *adapter) +VisitedLinksManagerQt::VisitedLinksManagerQt(ProfileQt *profile, bool persistVisitedLinks) : m_delegate(new VisitedLinkDelegateQt) { - Q_ASSERT(adapter && adapter->profile()); - ProfileQt *profile = adapter->profile(); - if (adapter->persistVisitedLinks()) + Q_ASSERT(profile); + if (persistVisitedLinks) ensureDirectoryExists(profile->GetPath()); - m_visitedLinkMaster.reset(new visitedlink::VisitedLinkMaster(profile, m_delegate.data(), adapter->persistVisitedLinks())); + m_visitedLinkMaster.reset(new visitedlink::VisitedLinkMaster(profile, m_delegate.data(), persistVisitedLinks)); m_visitedLinkMaster->Init(); } diff --git a/src/core/visited_links_manager_qt.h b/src/core/visited_links_manager_qt.h index 8d9a7495b..5bbcc5983 100644 --- a/src/core/visited_links_manager_qt.h +++ b/src/core/visited_links_manager_qt.h @@ -67,14 +67,14 @@ class GURL; namespace QtWebEngineCore { -class ProfileAdapter; +class ProfileQt; class VisitedLinkDelegateQt; class QWEBENGINECORE_PRIVATE_EXPORT VisitedLinksManagerQt { public: virtual~VisitedLinksManagerQt(); - VisitedLinksManagerQt(ProfileAdapter *profileAdapter); + VisitedLinksManagerQt(ProfileQt *profile, bool persistVisitedLinks); void deleteAllVisitedLinkData(); void deleteVisitedLinkDataForUrls(const QList<QUrl> &); diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp index 21540f5da..68bbc3daa 100644 --- a/src/core/web_contents_adapter.cpp +++ b/src/core/web_contents_adapter.cpp @@ -43,15 +43,14 @@ #include "web_contents_adapter.h" -#include "browser_accessibility_qt.h" -#include "profile_adapter_client.h" -#include "profile_adapter.h" #include "devtools_frontend_qt.h" #include "download_manager_delegate_qt.h" #include "media_capture_devices_dispatcher.h" #if QT_CONFIG(webengine_printing_and_pdf) #include "printing/print_view_manager_qt.h" #endif +#include "profile_adapter_client.h" +#include "profile_adapter.h" #include "profile_qt.h" #include "qwebenginecallback_p.h" #include "render_view_observer_host_qt.h" @@ -63,9 +62,11 @@ #include "base/command_line.h" #include "base/run_loop.h" +#include "base/task/post_task.h" #include "base/values.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/child_process_security_policy.h" #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/download_manager.h" @@ -83,7 +84,6 @@ #include "content/public/common/url_constants.h" #include "content/public/common/web_preferences.h" #include "content/public/common/webrtc_ip_handling_policy.h" -#include "third_party/blink/public/web/web_find_options.h" #include "third_party/blink/public/web/web_media_player_action.h" #include "printing/buildflags/buildflags.h" #include "ui/base/clipboard/clipboard.h" @@ -105,10 +105,14 @@ #include <QtCore/qelapsedtimer.h> #include <QtCore/qmimedata.h> #include <QtCore/qtemporarydir.h> -#include <QtGui/qaccessible.h> #include <QtGui/qdrag.h> #include <QtGui/qpixmap.h> +// Can't include headers as qaccessible.h conflicts with Chromium headers. +namespace content { +extern QAccessibleInterface *toQAccessibleInterface(BrowserAccessibility *acc); +} + namespace QtWebEngineCore { #define CHECK_INITIALIZED(return_value) \ @@ -117,7 +121,7 @@ namespace QtWebEngineCore { #define CHECK_VALID_RENDER_WIDGET_HOST_VIEW(render_view_host) \ if (!render_view_host->IsRenderViewLive() && render_view_host->GetWidget()->GetView()) { \ - qWarning("Ignore navigation due to terminated render process with invalid RenderWidgetHostView."); \ + LOG(WARNING) << "Ignore navigation due to terminated render process with invalid RenderWidgetHostView."; \ return; \ } @@ -190,7 +194,7 @@ static QVariant fromJSValue(const base::Value *result) } case base::Value::Type::BINARY: { - QByteArray data(result->GetBlob().data(), result->GetBlob().size()); + QByteArray data(reinterpret_cast<const char *>(result->GetBlob().data()), result->GetBlob().size()); ret.setValue(data); break; } @@ -210,10 +214,10 @@ static void callbackOnEvaluateJS(WebContentsAdapterClient *adapterClient, quint6 #if QT_CONFIG(webengine_printing_and_pdf) static void callbackOnPrintingFinished(WebContentsAdapterClient *adapterClient, int requestId, - const std::vector<char>& result) + QSharedPointer<QByteArray> result) { if (requestId) - adapterClient->didPrintPage(requestId, QByteArray(result.data(), result.size())); + adapterClient->didPrintPage(requestId, result); } static void callbackOnPdfSavingFinished(WebContentsAdapterClient *adapterClient, @@ -662,8 +666,8 @@ void WebContentsAdapter::load(const QWebEngineHttpRequest &request) if (resizeNeeded) { // Schedule navigation on the event loop. - content::BrowserThread::PostTask( - content::BrowserThread::UI, FROM_HERE, base::BindOnce(navigate, this, std::move(params))); + base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(navigate, this, std::move(params))); } else { navigate(this, params); } @@ -800,7 +804,7 @@ void WebContentsAdapter::selectAll() void WebContentsAdapter::requestClose() { CHECK_INITIALIZED(); - m_webContents->DispatchBeforeUnload(); + m_webContents->DispatchBeforeUnload(false /* auto_cancel */); } void WebContentsAdapter::unselect() @@ -932,8 +936,8 @@ QAccessibleInterface *WebContentsAdapter::browserAccessible() if (!manager) // FIXME! return nullptr; content::BrowserAccessibility *acc = manager->GetRoot(); - content::BrowserAccessibilityQt *accQt = static_cast<content::BrowserAccessibilityQt*>(acc); - return accQt; + + return content::toQAccessibleInterface(acc); } #endif // QT_NO_ACCESSIBILITY @@ -990,16 +994,16 @@ quint64 WebContentsAdapter::findText(const QString &subString, bool caseSensitiv m_adapterClient->didFindText(m_lastFindRequestId, 0); } - blink::WebFindOptions options; - options.forward = !findBackward; - options.match_case = caseSensitively; - options.find_next = subString == m_webContentsDelegate->lastSearchedString(); + blink::mojom::FindOptionsPtr options = blink::mojom::FindOptions::New(); + options->forward = !findBackward; + options->match_case = caseSensitively; + options->find_next = subString == m_webContentsDelegate->lastSearchedString(); m_webContentsDelegate->setLastSearchedString(subString); // Find already allows a request ID as input, but only as an int. // Use the same counter but mod it to MAX_INT, this keeps the same likeliness of request ID clashing. int shrunkRequestId = m_nextRequestId++ & 0x7fffffff; - m_webContents->Find(shrunkRequestId, toString16(subString), options); + m_webContents->Find(shrunkRequestId, toString16(subString), std::move(options)); m_lastFindRequestId = shrunkRequestId; return shrunkRequestId; } diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h index 55cbe13dd..4a36e77f9 100644 --- a/src/core/web_contents_adapter_client.h +++ b/src/core/web_contents_adapter_client.h @@ -66,6 +66,7 @@ QT_FORWARD_DECLARE_CLASS(QKeyEvent) QT_FORWARD_DECLARE_CLASS(QVariant) QT_FORWARD_DECLARE_CLASS(QWebEngineQuotaRequest) QT_FORWARD_DECLARE_CLASS(QWebEngineRegisterProtocolHandlerRequest) +QT_FORWARD_DECLARE_CLASS(QWebEngineUrlRequestInfo) namespace content { struct DropData; @@ -81,6 +82,8 @@ class JavaScriptDialogController; class RenderWidgetHostViewQt; class RenderWidgetHostViewQtDelegate; class RenderWidgetHostViewQtDelegateClient; +class TouchHandleDrawableClient; +class TouchSelectionMenuController; class WebContentsAdapter; class WebContentsDelegateQt; class WebEngineSettings; @@ -444,7 +447,7 @@ public: virtual void didFetchDocumentMarkup(quint64 requestId, const QString& result) = 0; virtual void didFetchDocumentInnerText(quint64 requestId, const QString& result) = 0; virtual void didFindText(quint64 requestId, int matchCount) = 0; - virtual void didPrintPage(quint64 requestId, const QByteArray &result) = 0; + virtual void didPrintPage(quint64 requestId, QSharedPointer<QByteArray>) = 0; virtual void didPrintPageToPdf(const QString &filePath, bool success) = 0; virtual void passOnFocus(bool reverse) = 0; // returns the last QObject (QWidget/QQuickItem) based object in the accessibility @@ -475,6 +478,10 @@ public: virtual ClientType clientType() = 0; virtual void printRequested() = 0; virtual void widgetChanged(RenderWidgetHostViewQtDelegate *newWidget) = 0; + virtual void interceptRequest(QWebEngineUrlRequestInfo &) { } + virtual TouchHandleDrawableClient *createTouchHandle(const QMap<int, QImage> &images) = 0; + virtual void showTouchSelectionMenu(TouchSelectionMenuController *menuController, const QRect &bounds, const QSize &handleSize) = 0; + virtual void hideTouchSelectionMenu() = 0; virtual ProfileAdapter *profileAdapter() = 0; virtual WebContentsAdapter* webContentsAdapter() = 0; diff --git a/src/core/web_contents_delegate_qt.cpp b/src/core/web_contents_delegate_qt.cpp index 4bde93fd3..963b89a3f 100644 --- a/src/core/web_contents_delegate_qt.cpp +++ b/src/core/web_contents_delegate_qt.cpp @@ -65,6 +65,7 @@ #include "components/web_cache/browser/web_cache_manager.h" #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/public/browser/browser_context.h" +#include "content/public/browser/file_select_listener.h" #include "content/public/browser/invalidate_type.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/navigation_handle.h" @@ -73,7 +74,6 @@ #include "content/public/browser/render_process_host.h" #include "content/public/browser/web_contents.h" #include "content/public/common/favicon_url.h" -#include "content/public/common/file_chooser_params.h" #include "content/public/common/frame_navigate_params.h" #include "content/public/common/url_constants.h" #include "content/public/common/web_preferences.h" @@ -451,12 +451,14 @@ bool WebContentsDelegateQt::IsFullscreenForTabOrPending(const content::WebConten return m_viewClient->isFullScreenMode(); } -ASSERT_ENUMS_MATCH(FilePickerController::Open, content::FileChooserParams::Open) -ASSERT_ENUMS_MATCH(FilePickerController::OpenMultiple, content::FileChooserParams::OpenMultiple) -ASSERT_ENUMS_MATCH(FilePickerController::UploadFolder, content::FileChooserParams::UploadFolder) -ASSERT_ENUMS_MATCH(FilePickerController::Save, content::FileChooserParams::Save) +ASSERT_ENUMS_MATCH(FilePickerController::Open, blink::mojom::FileChooserParams::Mode::kOpen) +ASSERT_ENUMS_MATCH(FilePickerController::OpenMultiple, blink::mojom::FileChooserParams::Mode::kOpenMultiple) +ASSERT_ENUMS_MATCH(FilePickerController::UploadFolder, blink::mojom::FileChooserParams::Mode::kUploadFolder) +ASSERT_ENUMS_MATCH(FilePickerController::Save, blink::mojom::FileChooserParams::Mode::kSave) -void WebContentsDelegateQt::RunFileChooser(content::RenderFrameHost *frameHost, const content::FileChooserParams ¶ms) +void WebContentsDelegateQt::RunFileChooser(content::RenderFrameHost * /*frameHost*/, + std::unique_ptr<content::FileSelectListener> listener, + const blink::mojom::FileChooserParams& params) { QStringList acceptedMimeTypes; acceptedMimeTypes.reserve(params.accept_types.size()); @@ -464,7 +466,7 @@ void WebContentsDelegateQt::RunFileChooser(content::RenderFrameHost *frameHost, acceptedMimeTypes.append(toQt(*it)); m_filePickerController.reset(new FilePickerController(static_cast<FilePickerController::FileChooserMode>(params.mode), - frameHost, toQt(params.default_file_name.value()), acceptedMimeTypes)); + std::move(listener), toQt(params.default_file_name.value()), acceptedMimeTypes)); // Defer the call to not block base::MessageLoop::RunTask with modal dialogs. QTimer::singleShot(0, [this] () { @@ -633,7 +635,9 @@ void WebContentsDelegateQt::BeforeUnloadFired(content::WebContents *tab, bool pr m_viewClient->windowCloseRejected(); } -void WebContentsDelegateQt::BeforeUnloadFired(const base::TimeTicks &proceed_time) { +void WebContentsDelegateQt::BeforeUnloadFired(bool proceed, const base::TimeTicks &proceed_time) +{ + Q_UNUSED(proceed); Q_UNUSED(proceed_time); } diff --git a/src/core/web_contents_delegate_qt.h b/src/core/web_contents_delegate_qt.h index 9c0f8f484..40f585767 100644 --- a/src/core/web_contents_delegate_qt.h +++ b/src/core/web_contents_delegate_qt.h @@ -114,7 +114,9 @@ public: void EnterFullscreenModeForTab(content::WebContents *web_contents, const GURL &origin, const blink::WebFullscreenOptions &) override; void ExitFullscreenModeForTab(content::WebContents*) override; bool IsFullscreenForTabOrPending(const content::WebContents* web_contents) const override; - void RunFileChooser(content::RenderFrameHost* render_frame_host, const content::FileChooserParams& params) override; + void RunFileChooser(content::RenderFrameHost* render_frame_host, + std::unique_ptr<content::FileSelectListener> listener, + const blink::mojom::FileChooserParams& params) override; bool DidAddMessageToConsole(content::WebContents* source, int32_t level, const base::string16& message, int32_t line_no, const base::string16& source_id) override; void FindReply(content::WebContents *source, int request_id, int number_of_matches, const gfx::Rect& selection_rect, int active_match_ordinal, bool final_update) override; void RequestMediaAccessPermission(content::WebContents *web_contents, @@ -135,7 +137,7 @@ public: void DidFinishNavigation(content::NavigationHandle *navigation_handle) override; void DidFailLoad(content::RenderFrameHost* render_frame_host, const GURL& validated_url, int error_code, const base::string16& error_description) override; void DidFinishLoad(content::RenderFrameHost *render_frame_host, const GURL &validated_url) override; - void BeforeUnloadFired(const base::TimeTicks& proceed_time) override; + void BeforeUnloadFired(bool proceed, const base::TimeTicks& proceed_time) override; void DidUpdateFaviconURL(const std::vector<content::FaviconURL> &candidates) override; void OnVisibilityChanged(content::Visibility visibility) override; void DidFirstVisuallyNonEmptyPaint() override; diff --git a/src/core/web_contents_view_qt.cpp b/src/core/web_contents_view_qt.cpp index 3c4465ae3..648e0fd8f 100644 --- a/src/core/web_contents_view_qt.cpp +++ b/src/core/web_contents_view_qt.cpp @@ -41,18 +41,22 @@ #include "profile_adapter.h" #include "content_browser_client_qt.h" +#include "render_widget_host_view_qt.h" #include "render_widget_host_view_qt_delegate.h" #include "render_widget_host_view_qt.h" +#include "touch_selection_controller_client_qt.h" #include "type_conversion.h" +#include "web_contents_adapter_client.h" #include "web_contents_adapter.h" #include "web_engine_context.h" +#include "web_contents_delegate_qt.h" #include "components/spellcheck/spellcheck_buildflags.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/common/context_menu_params.h" -#include <ui/gfx/image/image_skia.h> +#include "ui/gfx/image/image_skia.h" #include <QtGui/qpixmap.h> @@ -82,7 +86,7 @@ content::RenderWidgetHostViewBase* WebContentsViewQt::CreateViewForWidget(conten return view; } -content::RenderWidgetHostViewBase* WebContentsViewQt::CreateViewForPopupWidget(content::RenderWidgetHost* render_widget_host) +content::RenderWidgetHostViewBase* WebContentsViewQt::CreateViewForChildWidget(content::RenderWidgetHost* render_widget_host) { RenderWidgetHostViewQt *view = new RenderWidgetHostViewQt(render_widget_host); @@ -186,6 +190,11 @@ static inline WebEngineContextMenuData fromParams(const content::ContextMenuPara void WebContentsViewQt::ShowContextMenu(content::RenderFrameHost *, const content::ContextMenuParams ¶ms) { + if (auto rwhv = static_cast<RenderWidgetHostViewQt *>(m_webContents->GetRenderWidgetHostView())) { + if (rwhv && rwhv->getTouchSelectionControllerClient()->handleContextMenu(params)) + return; + } + WebEngineContextMenuData contextMenuData(fromParams(params)); #if QT_CONFIG(webengine_spellchecker) // Do not use params.spellcheck_enabled, since it is never diff --git a/src/core/web_contents_view_qt.h b/src/core/web_contents_view_qt.h index d1a2ff81e..c7bac1d97 100644 --- a/src/core/web_contents_view_qt.h +++ b/src/core/web_contents_view_qt.h @@ -63,7 +63,6 @@ public: : m_webContents(webContents) , m_client(nullptr) , m_factoryClient(nullptr) - , m_allowOtherViews(false) { } void initialize(WebContentsAdapterClient* client); @@ -73,7 +72,7 @@ public: void CreateView(const gfx::Size& initial_size, gfx::NativeView context) override; - content::RenderWidgetHostViewBase* CreateViewForPopupWidget(content::RenderWidgetHost* render_widget_host) override; + content::RenderWidgetHostViewBase *CreateViewForChildWidget(content::RenderWidgetHost* render_widget_host) override; void SetPageTitle(const base::string16& title) override { } @@ -83,29 +82,29 @@ public: void RenderViewHostChanged(content::RenderViewHost*, content::RenderViewHost*) override { } - void SetOverscrollControllerEnabled(bool enabled) override { QT_NOT_YET_IMPLEMENTED } + void SetOverscrollControllerEnabled(bool enabled) override { } gfx::NativeView GetNativeView() const override; - gfx::NativeView GetContentNativeView() const override { QT_NOT_USED return 0; } + gfx::NativeView GetContentNativeView() const override { return 0; } - gfx::NativeWindow GetTopLevelNativeWindow() const override { QT_NOT_USED return 0; } + gfx::NativeWindow GetTopLevelNativeWindow() const override { return 0; } void GetContainerBounds(gfx::Rect* out) const override; - void SizeContents(const gfx::Size& size) override { QT_NOT_YET_IMPLEMENTED } + void SizeContents(const gfx::Size& size) override { } void Focus() override; void SetInitialFocus() override; - void StoreFocus() override { QT_NOT_USED } + void StoreFocus() override { } - void RestoreFocus() override { QT_NOT_USED } + void RestoreFocus() override { } content::DropData* GetDropData() const override { QT_NOT_YET_IMPLEMENTED return nullptr; } - gfx::Rect GetViewBounds() const override { QT_NOT_YET_IMPLEMENTED return gfx::Rect(); } + gfx::Rect GetViewBounds() const override { return gfx::Rect(); } void StartDragging(const content::DropData& drop_data, blink::WebDragOperationsMask allowed_ops, const gfx::ImageSkia& image, const gfx::Vector2d& image_offset, @@ -122,8 +121,6 @@ public: #if defined(OS_MACOSX) - void SetAllowOtherViews(bool allow) override { m_allowOtherViews = allow; } - bool GetAllowOtherViews() const override { return m_allowOtherViews; } void CloseTabAfterEventTracking() override { QT_NOT_YET_IMPLEMENTED } bool IsEventTracking() const override { QT_NOT_YET_IMPLEMENTED; return false; } #endif // defined(OS_MACOSX) @@ -132,7 +129,6 @@ private: content::WebContents *m_webContents; WebContentsAdapterClient *m_client; WebContentsAdapterClient *m_factoryClient; - bool m_allowOtherViews; }; } // namespace QtWebEngineCore diff --git a/src/core/web_engine_context.cpp b/src/core/web_engine_context.cpp index ca52a5e67..a357196b0 100644 --- a/src/core/web_engine_context.cpp +++ b/src/core/web_engine_context.cpp @@ -46,6 +46,7 @@ #include "base/files/file_path.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" +#include "base/task/post_task.h" #include "base/threading/thread_restrictions.h" #include "cc/base/switches.h" #if QT_CONFIG(webengine_printing_and_pdf) @@ -54,13 +55,13 @@ #include "components/viz/common/features.h" #include "components/web_cache/browser/web_cache_manager.h" #include "content/browser/devtools/devtools_http_handler.h" -#include "content/browser/gpu/gpu_main_thread_factory.h" -#include "content/browser/renderer_host/render_process_host_impl.h" -#include "content/browser/utility_process_host.h" -#include "content/gpu/in_process_gpu_thread.h" +#include "content/browser/scheduler/browser_task_executor.h" +#include "content/browser/startup_helper.h" #include "content/public/app/content_main.h" #include "content/public/app/content_main_runner.h" #include "content/public/browser/browser_main_runner.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/plugin_service.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" @@ -68,9 +69,8 @@ #include "content/public/common/content_paths.h" #include "content/public/common/content_switches.h" #include "content/public/common/main_function_params.h" -#include "content/renderer/in_process_renderer_thread.h" -#include "content/utility/in_process_utility_thread.h" #include "gpu/command_buffer/service/gpu_switches.h" +#include "gpu/command_buffer/service/sync_point_manager.h" #include "gpu/ipc/host/gpu_switches.h" #include "media/audio/audio_manager.h" #include "mojo/core/embedder/embedder.h" @@ -86,20 +86,24 @@ #include "content/public/app/sandbox_helper_win.h" #endif // OS_WIN +#ifndef QT_NO_ACCESSIBILITY +#include "accessibility_activation_observer.h" +#endif #include "api/qwebengineurlscheme.h" -#include "profile_adapter.h" #include "content_browser_client_qt.h" #include "content_client_qt.h" #include "content_main_delegate_qt.h" #include "devtools_manager_delegate_qt.h" #include "media_capture_devices_dispatcher.h" #include "net/webui_controller_factory_qt.h" -#include "type_conversion.h" #include "ozone/gl_context_qt.h" +#include "profile_adapter.h" +#include "type_conversion.h" #include "web_engine_library_info.h" #include <QFileInfo> #include <QGuiApplication> +#include <QMutex> #include <QOffscreenSurface> #ifndef QT_NO_OPENGL # include <QOpenGLContext> @@ -166,6 +170,8 @@ void dummyGetPluginCallback(const std::vector<content::WebPluginInfo>&) namespace QtWebEngineCore { +extern std::unique_ptr<base::MessagePump> messagePumpFactory(); + bool usingSoftwareDynamicGL() { if (QCoreApplication::testAttribute(Qt::AA_UseSoftwareOpenGL)) @@ -224,6 +230,12 @@ void WebEngineContext::destroy() { if (m_devtoolsServer) m_devtoolsServer->stop(); + + // Normally the GPU thread is shut down when the GpuProcessHost is destroyed + // on IO thread (triggered by ~BrowserMainRunner). But by that time the UI + // task runner is not working anymore so we need to do this earlier. + destroyGpuProcess(); + base::MessagePump::Delegate *delegate = static_cast<base::MessageLoop *>(m_runLoop->delegate_); // Flush the UI message loop before quitting. @@ -273,6 +285,7 @@ WebEngineContext::~WebEngineContext() Q_ASSERT(!m_devtoolsServer); Q_ASSERT(!m_browserRunner); Q_ASSERT(m_profileAdapters.isEmpty()); + delete s_syncPointManager.fetchAndStoreRelaxed(nullptr); } WebEngineContext *WebEngineContext::current() @@ -312,7 +325,7 @@ void WebEngineContext::destroyContextPostRoutine() // Destroy WebEngineContext before its static pointer is zeroed and destructor called. // Before destroying MessageLoop via destroying BrowserMainRunner destructor // WebEngineContext's pointer is used. - m_handle->destroy(); + m_handle->destroy(); #if !defined(NDEBUG) if (!m_handle->HasOneRef()) qWarning("WebEngineContext leaked on exit, likely due to leaked WebEngine View or Page"); @@ -421,8 +434,6 @@ WebEngineContext::WebEngineContext() parsedCommandLine->AppendSwitch(switches::kDisablePepper3DImageChromium); // Same problem with select popups. parsedCommandLine->AppendSwitch(switches::kDisableNativeGpuMemoryBuffers); - // SandboxV2 doesn't currently work for us - appendToFeatureSwitch(parsedCommandLine, switches::kDisableFeatures, features::kMacV2Sandbox.name); #endif #if defined(Q_OS_WIN) @@ -430,10 +441,23 @@ WebEngineContext::WebEngineContext() // an OpenGL Core Profile context. If the switch is not set, it would always try to create a // Core Profile context, even if Qt uses a legacy profile, which causes // "Could not share GL contexts" warnings, because it's not possible to share between Core and - // legacy profiles. - // Given that Core profile is not currently supported on Windows anyway, pass this switch to - // get rid of the warnings. - parsedCommandLine->AppendSwitch(switches::kDisableES3GLContext); + // legacy profiles. See GLContextWGL::Initialize(). + // Given that Desktop GL Core profile is not currently supported on Windows anyway, pass this + // switch to get rid of the warnings. + // + // The switch is also used to determine which version of OpenGL ES to use (2 or 3) when using + // ANGLE. + // If the switch is not set, Chromium will always try to create an ES3 context, even if Qt uses + // an ES2 context, which causes resource sharing issues (black screen), + // see gpu::gles2::GenerateGLContextAttribs(). + // Make sure to disable ES3 context creation when using ES2. + const bool isGLES2Context = qt_gl_global_share_context() + && qt_gl_global_share_context()->isOpenGLES() + && qt_gl_global_share_context()->format().majorVersion() == 2; + const bool isDesktopGLOrSoftware = !usingANGLE(); + + if (isDesktopGLOrSoftware || isGLES2Context) + parsedCommandLine->AppendSwitch(switches::kDisableES3GLContext); #endif // Needed to allow navigations within pages that were set using setHtml(). One example is // tst_QWebEnginePage::acceptNavigationRequest. @@ -444,8 +468,8 @@ WebEngineContext::WebEngineContext() appendToFeatureSwitch(parsedCommandLine, switches::kDisableFeatures, features::kEnableSurfaceSynchronization.name); // The video-capture service is not functioning at this moment (since 69) appendToFeatureSwitch(parsedCommandLine, switches::kDisableFeatures, features::kMojoVideoCapture.name); - // We do not yet support the internal video capture API. - appendToFeatureSwitch(parsedCommandLine, switches::kDisableFeatures, features::kUseVideoCaptureApiForDevToolsSnapshots.name); + + appendToFeatureSwitch(parsedCommandLine, switches::kDisableFeatures, features::kBackgroundFetch.name); if (useEmbeddedSwitches) { // embedded switches are based on the switches for Android, see content/browser/android/content_startup_flags.cc @@ -466,8 +490,7 @@ WebEngineContext::WebEngineContext() #ifndef QT_NO_OPENGL bool tryGL = - !usingANGLE() - && (!usingSoftwareDynamicGL() + (!usingSoftwareDynamicGL() // If user requested WebGL support instead of using Skia rendering to // bitmaps, use software rendering via software OpenGL. This might be less // performant, but at least provides WebGL support. @@ -477,10 +500,13 @@ WebEngineContext::WebEngineContext() if (tryGL) { if (qt_gl_global_share_context() && qt_gl_global_share_context()->isValid()) { - // If the native handle is QEGLNativeContext try to use GL ES/2, if there is no native handle - // assume we are using wayland and try GL ES/2, and finally Ozone demands GL ES/2 too. + // If the native handle is QEGLNativeContext try to use GL ES/2. + // If there is no native handle, assume we are using wayland and try GL ES/2. + // If we are using ANGLE on Windows, use OpenGL ES (2 or 3). if (qt_gl_global_share_context()->nativeHandle().isNull() - || !strcmp(qt_gl_global_share_context()->nativeHandle().typeName(), "QEGLNativeContext")) + || !strcmp(qt_gl_global_share_context()->nativeHandle().typeName(), + "QEGLNativeContext") + || usingANGLE()) { if (qt_gl_global_share_context()->isOpenGLES()) { glType = gl::kGLImplementationEGLName; @@ -546,9 +572,16 @@ WebEngineContext::WebEngineContext() parsedCommandLine->AppendSwitch(switches::kDisableGpu); } - content::UtilityProcessHost::RegisterUtilityMainThreadFactory(content::CreateInProcessUtilityThread); - content::RenderProcessHostImpl::RegisterRendererMainThreadFactory(content::CreateInProcessRendererThread); - content::RegisterGpuMainThreadFactory(content::CreateInProcessGpuThread); + bool threadedGpu = true; +#ifndef QT_NO_OPENGL + threadedGpu = QOpenGLContext::supportsThreadedOpenGL(); +#endif + registerMainThreadFactories(threadedGpu); + + SetContentClient(new ContentClientQt); + + content::StartBrowserTaskScheduler(); + content::BrowserTaskExecutor::Create(); mojo::core::Init(); @@ -600,6 +633,10 @@ WebEngineContext::WebEngineContext() m_printJobManager.reset(new printing::PrintJobManager()); #endif +#ifndef QT_NO_ACCESSIBILITY + m_accessibilityActivationObserver.reset(new AccessibilityActivationObserver()); +#endif + content::WebUIControllerFactory::RegisterFactory(WebUIControllerFactoryQt::GetInstance()); } @@ -609,4 +646,18 @@ printing::PrintJobManager* WebEngineContext::getPrintJobManager() return m_printJobManager.get(); } #endif + +static QMutex s_spmMutex; +QAtomicPointer<gpu::SyncPointManager> WebEngineContext::s_syncPointManager; + +gpu::SyncPointManager *WebEngineContext::syncPointManager() +{ + if (gpu::SyncPointManager *spm = s_syncPointManager.loadAcquire()) + return spm; + QMutexLocker lock(&s_spmMutex); + if (!s_syncPointManager) + s_syncPointManager.store(new gpu::SyncPointManager()); + return s_syncPointManager.load(); +} + } // namespace diff --git a/src/core/web_engine_context.h b/src/core/web_engine_context.h index 604c85a61..ad02ddf4d 100644 --- a/src/core/web_engine_context.h +++ b/src/core/web_engine_context.h @@ -52,6 +52,14 @@ class RunLoop; namespace content { class BrowserMainRunner; class ContentMainRunner; +class GpuProcess; +class GpuThreadController; +class InProcessChildThreadParams; +} + +namespace gpu { +struct GpuPreferences; +class SyncPointManager; } #if QT_CONFIG(webengine_printing_and_pdf) @@ -64,9 +72,10 @@ QT_FORWARD_DECLARE_CLASS(QObject) namespace QtWebEngineCore { -class ProfileAdapter; +class AccessibilityActivationObserver; class ContentMainDelegateQt; class DevToolsServerQt; +class ProfileAdapter; bool usingSoftwareDynamicGL(); @@ -87,12 +96,17 @@ public: void removeProfileAdapter(ProfileAdapter *profileAdapter); void destroy(); + static gpu::SyncPointManager *syncPointManager(); + private: friend class base::RefCounted<WebEngineContext>; friend class ProfileAdapter; WebEngineContext(); ~WebEngineContext(); + static void registerMainThreadFactories(bool threaded); + static void destroyGpuProcess(); + std::unique_ptr<base::RunLoop> m_runLoop; std::unique_ptr<ContentMainDelegateQt> m_mainDelegate; std::unique_ptr<content::ContentMainRunner> m_contentRunner; @@ -101,12 +115,16 @@ private: std::unique_ptr<ProfileAdapter> m_defaultProfileAdapter; std::unique_ptr<DevToolsServerQt> m_devtoolsServer; QVector<ProfileAdapter*> m_profileAdapters; +#ifndef QT_NO_ACCESSIBILITY + std::unique_ptr<AccessibilityActivationObserver> m_accessibilityActivationObserver; +#endif #if QT_CONFIG(webengine_printing_and_pdf) std::unique_ptr<printing::PrintJobManager> m_printJobManager; #endif static scoped_refptr<QtWebEngineCore::WebEngineContext> m_handle; static bool m_destroyed; + static QAtomicPointer<gpu::SyncPointManager> s_syncPointManager; }; } // namespace diff --git a/src/core/web_engine_context_threads.cpp b/src/core/web_engine_context_threads.cpp new file mode 100644 index 000000000..07a86cc69 --- /dev/null +++ b/src/core/web_engine_context_threads.cpp @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** 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 "web_engine_context.h" + +#include "base/bind.h" +#include "base/task/post_task.h" +#include "base/threading/platform_thread.h" +#include "base/threading/thread_restrictions.h" +#include "content/browser/gpu/gpu_main_thread_factory.h" +#include "content/browser/renderer_host/render_process_host_impl.h" +#include "content/browser/utility_process_host.h" +#include "content/gpu/gpu_child_thread.h" +#include "content/gpu/gpu_process.h" +#include "content/gpu/in_process_gpu_thread.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" +#include "content/renderer/in_process_renderer_thread.h" +#include "content/utility/in_process_utility_thread.h" + +#include <memory> + +namespace QtWebEngineCore { + +struct GpuThreadControllerQt : content::GpuThreadController +{ + GpuThreadControllerQt(const content::InProcessChildThreadParams ¶ms, const gpu::GpuPreferences &gpuPreferences) + { + base::PostTaskWithTraits( + FROM_HERE, { content::BrowserThread::UI }, + base::BindOnce(&GpuThreadControllerQt::createGpuProcess, params, gpuPreferences)); + } + ~GpuThreadControllerQt() override + { + base::PostTaskWithTraits( + FROM_HERE, { content::BrowserThread::UI }, + base::BindOnce(&GpuThreadControllerQt::destroyGpuProcess)); + } + + static void createGpuProcess( + const content::InProcessChildThreadParams ¶ms, + const gpu::GpuPreferences &gpuPreferences) + { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (s_gpuProcessDestroyed) + return; + + s_gpuProcess = std::make_unique<content::GpuProcess>(base::ThreadPriority::NORMAL); + auto gpuInit = std::make_unique<gpu::GpuInit>(); + gpuInit->InitializeInProcess(base::CommandLine::ForCurrentProcess(), gpuPreferences); + auto childThread = new content::GpuChildThread(params, std::move(gpuInit)); + childThread->Init(base::Time::Now()); + s_gpuProcess->set_main_thread(childThread); + } + + static void destroyGpuProcess() + { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (s_gpuProcessDestroyed) + return; + + // viz::GpuServiceImpl::~GpuServiceImpl waits for io task. + base::ScopedAllowBaseSyncPrimitivesForTesting allow; + s_gpuProcess.reset(); + s_gpuProcessDestroyed = true; + } + + static std::unique_ptr<content::GpuProcess> s_gpuProcess; + static bool s_gpuProcessDestroyed; +}; + +std::unique_ptr<content::GpuProcess> GpuThreadControllerQt::s_gpuProcess; +bool GpuThreadControllerQt::s_gpuProcessDestroyed = false; + +static std::unique_ptr<content::GpuThreadController> createGpuThreadController( + const content::InProcessChildThreadParams ¶ms, + const gpu::GpuPreferences &gpuPreferences) +{ + return std::make_unique<GpuThreadControllerQt>(params, gpuPreferences); +} + +// static +void WebEngineContext::destroyGpuProcess() +{ + GpuThreadControllerQt::destroyGpuProcess(); +} + +// static +void WebEngineContext::registerMainThreadFactories(bool threaded) +{ + content::UtilityProcessHost::RegisterUtilityMainThreadFactory(content::CreateInProcessUtilityThread); + content::RenderProcessHostImpl::RegisterRendererMainThreadFactory(content::CreateInProcessRendererThread); + if (threaded) + content::RegisterGpuMainThreadFactory(content::CreateInProcessGpuThread); + else + content::RegisterGpuMainThreadFactory(createGpuThreadController); +} + +} // namespace QtWebEngineCore diff --git a/src/core/web_engine_library_info.cpp b/src/core/web_engine_library_info.cpp index 3899ced25..12ffd91e6 100644 --- a/src/core/web_engine_library_info.cpp +++ b/src/core/web_engine_library_info.cpp @@ -54,6 +54,7 @@ #include <QDir> #include <QFileInfo> #include <QLibraryInfo> +#include <QLocale> #include <QStandardPaths> #include <QString> diff --git a/src/core/web_event_factory.cpp b/src/core/web_event_factory.cpp index fc6287dd9..339a3ef0e 100644 --- a/src/core/web_event_factory.cpp +++ b/src/core/web_event_factory.cpp @@ -101,7 +101,7 @@ static KeyboardDriver keyboardDriverImpl() if (platformName == QLatin1Literal("xcb") || platformName == QLatin1Literal("wayland")) return KeyboardDriver::Xkb; -#if QT_CONFIG(libinput) && QT_CONFIG(xkbcommon) +#if QT_CONFIG(libinput) // Based on QEglFSIntegration::createInputHandlers and QLibInputKeyboard::processKey. if (platformName == QLatin1Literal("eglfs") && !qEnvironmentVariableIntValue("QT_QPA_EGLFS_NO_LIBINPUT")) return KeyboardDriver::Xkb; diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 32fce325b..6698a9736 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -1,2 +1,2 @@ TEMPLATE = subdirs -qtHaveModule(designer):qtHaveModule(webenginewidgets): SUBDIRS += qwebengineview +qtHaveModule(designer): SUBDIRS += qwebengineview diff --git a/src/src.pro b/src/src.pro index 218cdb66d..30562686a 100644 --- a/src/src.pro +++ b/src/src.pro @@ -14,11 +14,7 @@ core.depends = buildtools SUBDIRS += buildtools \ core \ - process \ - webengine \ - webengine_plugin \ - plugins - + process qtConfig(webengine-spellchecker):!qtConfig(webengine-native-spellchecker):!cross_compile { SUBDIRS += qwebengine_convert_dict @@ -26,19 +22,11 @@ qtConfig(webengine-spellchecker):!qtConfig(webengine-native-spellchecker):!cross qwebengine_convert_dict.depends = core } -qtConfig(webengine-testsupport) { - webengine_testsupport_plugin.subdir = webengine/plugin/testsupport - webengine_testsupport_plugin.target = sub-webengine-testsupport-plugin - webengine_testsupport_plugin.depends = webengine - SUBDIRS += webengine_testsupport_plugin -} - -qtConfig(webengine-ui-delegates) { - SUBDIRS += webengine/ui \ - webengine/ui2 +qtConfig(webengine-qml) { + SUBDIRS += webengine } -qtHaveModule(widgets) { - SUBDIRS += webenginewidgets +qtConfig(webengine-widgets) { + SUBDIRS += plugins webenginewidgets plugins.depends = webenginewidgets } diff --git a/src/webengine/api/qquickwebengineclientcertificateselection.cpp b/src/webengine/api/qquickwebengineclientcertificateselection.cpp new file mode 100644 index 000000000..56cf1ff64 --- /dev/null +++ b/src/webengine/api/qquickwebengineclientcertificateselection.cpp @@ -0,0 +1,239 @@ +/**************************************************************************** +** +** 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 "qquickwebengineclientcertificateselection_p.h" + +#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) + +#include "client_cert_select_controller.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype WebEngineClientCertificateOption + \instantiates QQuickWebEngineClientCertificateOption + \inqmlmodule QtWebEngine + \since QtWebEngine 1.9 + \brief Represents a client certificate option. + + \sa {WebEngineClientCertificateSelection::certificates} {WebEngineClientCertificateSelection.certificates} +*/ + +QQuickWebEngineClientCertificateOption::QQuickWebEngineClientCertificateOption() = default; + +QQuickWebEngineClientCertificateOption::QQuickWebEngineClientCertificateOption(QQuickWebEngineClientCertificateSelection *selection, int index) + : QObject(), m_selection(selection), m_index(index) +{} + +QQuickWebEngineClientCertificateOption::QQuickWebEngineClientCertificateOption(const QQuickWebEngineClientCertificateOption &other) + : QObject(), m_selection(other.m_selection), m_index(other.m_index) +{} + +QQuickWebEngineClientCertificateOption &QQuickWebEngineClientCertificateOption::operator=(const QQuickWebEngineClientCertificateOption &other) +{ + m_selection = other.m_selection; + m_index = other.m_index; + return *this; +} + +/*! + \qmlproperty string WebEngineClientCertificateOption::issuer + \brief The issuer of the certificate. +*/ + +QString QQuickWebEngineClientCertificateOption::issuer() const +{ + return m_selection->d_ptr->certificates().at(m_index).issuerDisplayName(); +} + +/*! + \qmlproperty string WebEngineClientCertificateOption::subject + \brief The subject of the certificate. +*/ +QString QQuickWebEngineClientCertificateOption::subject() const +{ + return m_selection->d_ptr->certificates().at(m_index).subjectDisplayName(); +} + +/*! + \qmlproperty datetime WebEngineClientCertificateOption::effectiveDate + \brief The date and time when the certificate becomes valid. +*/ +QDateTime QQuickWebEngineClientCertificateOption::effectiveDate() const +{ + return m_selection->d_ptr->certificates().at(m_index).effectiveDate(); +} + +/*! + \qmlproperty datetime WebEngineClientCertificateOption::expiryDate + \brief The date and time when the certificate becomes invalid. +*/ +QDateTime QQuickWebEngineClientCertificateOption::expiryDate() const +{ + return m_selection->d_ptr->certificates().at(m_index).expiryDate(); +} + +/*! + \qmlproperty bool WebEngineClientCertificateOption::isSelfSigned + \brief Whether the certificate is only self-signed. +*/ +bool QQuickWebEngineClientCertificateOption::isSelfSigned() const +{ + return m_selection->d_ptr->certificates().at(m_index).isSelfSigned(); +} + +/*! + \qmlmethod void WebEngineClientCertificateOption::select() + + Selects this client certificate option. +*/ +void QQuickWebEngineClientCertificateOption::select() +{ + m_selection->select(m_index); +} + +/*! + \qmltype WebEngineClientCertificateSelection + \instantiates QQuickWebEngineClientCertificateSelection + \inqmlmodule QtWebEngine + \since QtWebEngine 1.9 + \brief Provides a selection of client certificates. + + When a web site requests an SSL client certificate, and one or more certificates + are found in the system's client certificate store, this type provides access to + the certificates to choose from, as well as a method for selecting one. + + The selection is asynchronous. If no certificate is selected and no copy of the + object is kept alive, loading will continue without a certificate. + + \sa {WebEngineView::selectClientCertificate}{WebEngineView.selectClientCertificate} +*/ + +QQuickWebEngineClientCertificateSelection::QQuickWebEngineClientCertificateSelection(QSharedPointer<ClientCertSelectController> selectController) + : QObject(), d_ptr(selectController) +{} + +int QQuickWebEngineClientCertificateSelection::certificates_count( + QQmlListProperty<QQuickWebEngineClientCertificateOption> *p) +{ + Q_ASSERT(p && p->object); + QQuickWebEngineClientCertificateSelection *d = static_cast<QQuickWebEngineClientCertificateSelection *>(p->object); + return d->m_certificates.size(); +} + +QQuickWebEngineClientCertificateOption *QQuickWebEngineClientCertificateSelection::certificates_at( + QQmlListProperty<QQuickWebEngineClientCertificateOption> *p, int idx) +{ + Q_ASSERT(p && p->object); + QQuickWebEngineClientCertificateSelection *d = static_cast<QQuickWebEngineClientCertificateSelection *>(p->object); + if (idx < 0 || idx >= d->m_certificates.size()) + return nullptr; + return &d->m_certificates[idx]; +} + +/*! + \qmlproperty list<WebEngineClientCertificateOption> WebEngineClientCertificateSelection::certificates + \brief The client certificates available to choose from. +*/ + +QQmlListProperty<QQuickWebEngineClientCertificateOption> QQuickWebEngineClientCertificateSelection::certificates() +{ + if (m_certificates.empty()) { + QVector<QSslCertificate> certificates = d_ptr->certificates(); + for (int i = 0; i < certificates.count(); ++i) + m_certificates.push_back(QQuickWebEngineClientCertificateOption(this, i)); + } + + return QQmlListProperty<QQuickWebEngineClientCertificateOption>( + this, nullptr, + certificates_count, + certificates_at); +} + +/*! + \qmlmethod void WebEngineClientCertificateSelection::select(WebEngineClientCertificateOption certificate) + + Selects the client certificate \a certificate. The certificate must be one + of the offered certificates. + + \sa selectNone() +*/ +void QQuickWebEngineClientCertificateSelection::select(const QQuickWebEngineClientCertificateOption *certificate) +{ + select(certificate->m_index); +} + +/*! + \qmlmethod void WebEngineClientCertificateSelection::select(int index) + + Selects the client certificate at the index \a index in the list of offered certificates. + + \sa selectNone() +*/ +void QQuickWebEngineClientCertificateSelection::select(int index) +{ + d_ptr->select(index); +} + +/*! + \qmlmethod void WebEngineClientCertificateSelection::selectNone() + + Continues without using any of the offered certificates. This is the same + action as taken when destroying the last copy of this object if no + selection has been made. + + \sa select() +*/ +void QQuickWebEngineClientCertificateSelection::selectNone() +{ + d_ptr->selectNone(); +} + +/*! + \qmlproperty url WebEngineClientCertificateSelection::host + \brief The host and port of the server requesting the client certificate. +*/ +QUrl QQuickWebEngineClientCertificateSelection::host() const +{ + return d_ptr->hostAndPort(); +} + +QT_END_NAMESPACE + +#endif // QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) diff --git a/src/webengine/api/qquickwebengineclientcertificateselection_p.h b/src/webengine/api/qquickwebengineclientcertificateselection_p.h new file mode 100644 index 000000000..7f5a26296 --- /dev/null +++ b/src/webengine/api/qquickwebengineclientcertificateselection_p.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKWEBENGINECERTSELECTION_P_H +#define QQUICKWEBENGINECERTSELECTION_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 <QtWebEngine/private/qtwebengineglobal_p.h> + +#include <QtCore/QDateTime> +#include <QtCore/QObject> +#include <QtCore/QSharedPointer> +#include <QtCore/QUrl> +#include <QtCore/QVector> +#include <QtQml/QQmlListProperty> + +#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) + +QT_BEGIN_NAMESPACE + +class ClientCertSelectController; +class QQuickWebEngineClientCertificateSelection; + +class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineClientCertificateOption : public QObject { + Q_OBJECT + Q_PROPERTY(QString issuer READ issuer CONSTANT FINAL) + Q_PROPERTY(QString subject READ subject CONSTANT FINAL) + Q_PROPERTY(QDateTime effectiveDate READ effectiveDate CONSTANT FINAL) + Q_PROPERTY(QDateTime expiryDate READ expiryDate CONSTANT FINAL) + Q_PROPERTY(bool isSelfSigned READ isSelfSigned CONSTANT FINAL) + +public: + QQuickWebEngineClientCertificateOption(); + QQuickWebEngineClientCertificateOption(const QQuickWebEngineClientCertificateOption &); + QQuickWebEngineClientCertificateOption &operator=(const QQuickWebEngineClientCertificateOption &); + + QString issuer() const; + QString subject() const; + QDateTime effectiveDate() const; + QDateTime expiryDate() const; + bool isSelfSigned() const; + + Q_INVOKABLE void select(); + +private: + friend class QQuickWebEngineClientCertificateSelection; + QQuickWebEngineClientCertificateOption(QQuickWebEngineClientCertificateSelection *selection, int index); + + QQuickWebEngineClientCertificateSelection *m_selection; + int m_index; +}; + +class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineClientCertificateSelection : public QObject { + Q_OBJECT + Q_PROPERTY(QUrl host READ host CONSTANT FINAL) + Q_PROPERTY(QQmlListProperty<QQuickWebEngineClientCertificateOption> certificates READ certificates CONSTANT FINAL) + +public: + QQuickWebEngineClientCertificateSelection() = default; + + QUrl host() const; + + Q_INVOKABLE void select(int idx); + Q_INVOKABLE void select(const QQuickWebEngineClientCertificateOption *certificate); + Q_INVOKABLE void selectNone(); + QQmlListProperty<QQuickWebEngineClientCertificateOption> certificates(); + +private: + friend class QQuickWebEngineViewPrivate; + friend class QQuickWebEngineClientCertificateOption; + + static int certificates_count(QQmlListProperty<QQuickWebEngineClientCertificateOption> *p); + static QQuickWebEngineClientCertificateOption *certificates_at(QQmlListProperty<QQuickWebEngineClientCertificateOption> *p, int idx); + + explicit QQuickWebEngineClientCertificateSelection(QSharedPointer<ClientCertSelectController>); + + mutable QVector<QQuickWebEngineClientCertificateOption> m_certificates; + QSharedPointer<ClientCertSelectController> d_ptr; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QQuickWebEngineClientCertificateOption *) +Q_DECLARE_METATYPE(QQmlListProperty<QQuickWebEngineClientCertificateOption>) +Q_DECLARE_METATYPE(QQuickWebEngineClientCertificateSelection *) + +#endif + +#endif // QQUICKWEBENGINECERTSELECTION_P_H diff --git a/src/webengine/api/qquickwebengineprofile.cpp b/src/webengine/api/qquickwebengineprofile.cpp index ddc71602b..4448d44d1 100644 --- a/src/webengine/api/qquickwebengineprofile.cpp +++ b/src/webengine/api/qquickwebengineprofile.cpp @@ -279,6 +279,12 @@ void QQuickWebEngineProfilePrivate::downloadUpdated(const DownloadItemInfo &info } } +void QQuickWebEngineProfilePrivate::useForGlobalCertificateVerificationChanged() +{ + Q_Q(QQuickWebEngineProfile); + Q_EMIT q->useForGlobalCertificateVerificationChanged(); +} + void QQuickWebEngineProfilePrivate::userScripts_append(QQmlListProperty<QQuickWebEngineScript> *p, QQuickWebEngineScript *script) { Q_ASSERT(p && p->data); @@ -789,6 +795,102 @@ bool QQuickWebEngineProfile::isSpellCheckEnabled() const } /*! + \property QQuickWebEngineProfile::useForGlobalCertificateVerification + \since 5.13 + + This property holds whether this profile is used for downloading and + caching during global certificate verification when using the online + certificate status protocol (OCSP), certificate revokation lists (CRLs), + and authority information access (AIA), for example. + + As long as one profile has this option enabled, all other profiles will be + able to use it for certificate verification. Only one profile at a time can + have this option enabled. It is recommended that the profile has a disk HTTP + cache to avoid needlessly re-downloading. + + By default, no profile has this property enabled. + + Currently, only affects Linux/NSS installations, where having a profile with + this role enables OCSP. +*/ + +/*! + \qmlproperty bool WebEngineProfile::useForGlobalCertificateVerification + \since QtWebEngine 1.9 + + This property holds whether this profile is used for downloading and + caching during global certificate verification when using the online + certificate status protocol (OCSP), certificate revokation lists (CRLs), + and authority information access (AIA), for example. + + As long as one profile has this option enabled, all other profiles will be + able to use it for certificate verification. Only one profile at a time can + have this option enabled. It is recommended that the profile has a disk HTTP + cache to avoid needlessly re-downloading. + + By default, no profile has this property enabled. + + Currently, only affects Linux/NSS installations, where having a profile with + this role enables OCSP. +*/ + +void QQuickWebEngineProfile::setUseForGlobalCertificateVerification(bool enable) +{ + Q_D(QQuickWebEngineProfile); + if (enable != d->profileAdapter()->isUsedForGlobalCertificateVerification()) { + d->profileAdapter()->setUseForGlobalCertificateVerification(enable); + emit useForGlobalCertificateVerificationChanged(); + } +} + +bool QQuickWebEngineProfile::isUsedForGlobalCertificateVerification() const +{ + const Q_D(QQuickWebEngineProfile); + return d->profileAdapter()->isUsedForGlobalCertificateVerification(); +} + +/*! + \qmlproperty string WebEngineProfile::downloadPath + \since QtWebEngine 1.9 + + The path to the location where the downloaded files are stored. + + Overrides the default path used for download location. + + If set to the null string, the default path is restored. + + \note By default, the download path is QStandardPaths::DownloadLocation. +*/ + +/*! + \property QQuickWebEngineProfile::downloadPath + \since QtWebEngine 1.9 + + The path to the location where the downloaded files are stored. + + Overrides the default path used for download location, setting it to \a path. + + If set to the null string, the default path is restored. + + \note By default, the download path is QStandardPaths::DownloadLocation. +*/ + +void QQuickWebEngineProfile::setDownloadPath(const QString &path) +{ + Q_D(QQuickWebEngineProfile); + if (downloadPath() == path) + return; + d->profileAdapter()->setDownloadPath(path); + emit downloadPathChanged(); +} + +QString QQuickWebEngineProfile::downloadPath() const +{ + const Q_D(QQuickWebEngineProfile); + return d->profileAdapter()->downloadPath(); +} + +/*! Returns the cookie store for this profile. */ @@ -840,20 +942,7 @@ void QQuickWebEngineProfile::setRequestInterceptor(QWebEngineUrlRequestIntercept const QWebEngineUrlSchemeHandler *QQuickWebEngineProfile::urlSchemeHandler(const QByteArray &scheme) const { const Q_D(QQuickWebEngineProfile); - if (d->profileAdapter()->customUrlSchemeHandlers().contains(scheme)) - return d->profileAdapter()->customUrlSchemeHandlers().value(scheme); - return 0; -} - -static bool checkInternalScheme(const QByteArray &scheme) -{ - static QSet<QByteArray> internalSchemes; - if (internalSchemes.isEmpty()) { - internalSchemes << QByteArrayLiteral("qrc") << QByteArrayLiteral("data") << QByteArrayLiteral("blob") - << QByteArrayLiteral("http") << QByteArrayLiteral("https") << QByteArrayLiteral("ftp") - << QByteArrayLiteral("javascript"); - } - return internalSchemes.contains(scheme); + return d->profileAdapter()->urlSchemeHandler(scheme); } /*! @@ -865,25 +954,7 @@ static bool checkInternalScheme(const QByteArray &scheme) void QQuickWebEngineProfile::installUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler) { Q_D(QQuickWebEngineProfile); - Q_ASSERT(handler); - QByteArray canonicalScheme = scheme.toLower(); - if (checkInternalScheme(canonicalScheme)) { - qWarning("Cannot install a URL scheme handler overriding internal scheme: %s", scheme.constData()); - return; - } - - if (d->profileAdapter()->customUrlSchemeHandlers().contains(canonicalScheme)) { - if (d->profileAdapter()->customUrlSchemeHandlers().value(canonicalScheme) != handler) - qWarning("URL scheme handler already installed for the scheme: %s", scheme.constData()); - return; - } - - if (QWebEngineUrlScheme::schemeByName(canonicalScheme) == QWebEngineUrlScheme()) - qWarning("Please register the custom scheme '%s' via QWebEngineUrlScheme::registerScheme() " - "before installing the custom scheme handler.", scheme.constData()); - - d->profileAdapter()->addCustomUrlSchemeHandler(canonicalScheme, handler); - connect(handler, SIGNAL(_q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)), this, SLOT(destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*))); + d->profileAdapter()->installUrlSchemeHandler(scheme, handler); } /*! @@ -894,10 +965,7 @@ void QQuickWebEngineProfile::installUrlSchemeHandler(const QByteArray &scheme, Q void QQuickWebEngineProfile::removeUrlSchemeHandler(QWebEngineUrlSchemeHandler *handler) { Q_D(QQuickWebEngineProfile); - Q_ASSERT(handler); - if (!d->profileAdapter()->removeCustomUrlSchemeHandler(handler)) - return; - disconnect(handler, SIGNAL(_q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)), this, SLOT(destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*))); + d->profileAdapter()->removeUrlSchemeHandler(handler); } /*! @@ -908,10 +976,7 @@ void QQuickWebEngineProfile::removeUrlSchemeHandler(QWebEngineUrlSchemeHandler * void QQuickWebEngineProfile::removeUrlScheme(const QByteArray &scheme) { Q_D(QQuickWebEngineProfile); - QWebEngineUrlSchemeHandler *handler = d->profileAdapter()->takeCustomUrlSchemeHandler(scheme); - if (!handler) - return; - disconnect(handler, SIGNAL(_q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)), this, SLOT(destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*))); + d->profileAdapter()->removeUrlScheme(scheme); } /*! @@ -920,12 +985,7 @@ void QQuickWebEngineProfile::removeUrlScheme(const QByteArray &scheme) void QQuickWebEngineProfile::removeAllUrlSchemeHandlers() { Q_D(QQuickWebEngineProfile); - d->profileAdapter()->clearCustomUrlSchemeHandlers(); -} - -void QQuickWebEngineProfile::destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler *obj) -{ - removeUrlSchemeHandler(obj); + d->profileAdapter()->removeAllUrlSchemeHandlers(); } QQuickWebEngineSettings *QQuickWebEngineProfile::settings() const diff --git a/src/webengine/api/qquickwebengineprofile.h b/src/webengine/api/qquickwebengineprofile.h index 9fc4f9eca..1e2e3e030 100644 --- a/src/webengine/api/qquickwebengineprofile.h +++ b/src/webengine/api/qquickwebengineprofile.h @@ -72,6 +72,12 @@ class Q_WEBENGINE_EXPORT QQuickWebEngineProfile : public QObject { Q_PROPERTY(QStringList spellCheckLanguages READ spellCheckLanguages WRITE setSpellCheckLanguages NOTIFY spellCheckLanguagesChanged FINAL REVISION 3) Q_PROPERTY(bool spellCheckEnabled READ isSpellCheckEnabled WRITE setSpellCheckEnabled NOTIFY spellCheckEnabledChanged FINAL REVISION 3) Q_PROPERTY(QQmlListProperty<QQuickWebEngineScript> userScripts READ userScripts FINAL REVISION 4) + Q_PROPERTY(bool useForGlobalCertificateVerification + READ isUsedForGlobalCertificateVerification + WRITE setUseForGlobalCertificateVerification + NOTIFY useForGlobalCertificateVerificationChanged + FINAL REVISION 5) + Q_PROPERTY(QString downloadPath READ downloadPath WRITE setDownloadPath NOTIFY downloadPathChanged FINAL REVISION 5) public: QQuickWebEngineProfile(QObject *parent = Q_NULLPTR); @@ -137,6 +143,12 @@ public: QQmlListProperty<QQuickWebEngineScript> userScripts(); + void setUseForGlobalCertificateVerification(bool b); + bool isUsedForGlobalCertificateVerification() const; + + QString downloadPath() const; + void setDownloadPath(const QString &path); + static QQuickWebEngineProfile *defaultProfile(); Q_SIGNALS: @@ -151,13 +163,12 @@ Q_SIGNALS: Q_REVISION(1) void httpAcceptLanguageChanged(); Q_REVISION(3) void spellCheckLanguagesChanged(); Q_REVISION(3) void spellCheckEnabledChanged(); + Q_REVISION(5) void useForGlobalCertificateVerificationChanged(); + Q_REVISION(5) void downloadPathChanged(); void downloadRequested(QQuickWebEngineDownloadItem *download); void downloadFinished(QQuickWebEngineDownloadItem *download); -private Q_SLOTS: - void destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler *obj); - 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 d31ded0ec..d59470f46 100644 --- a/src/webengine/api/qquickwebengineprofile_p.h +++ b/src/webengine/api/qquickwebengineprofile_p.h @@ -83,6 +83,8 @@ public: void downloadRequested(DownloadItemInfo &info) override; void downloadUpdated(const DownloadItemInfo &info) override; + void useForGlobalCertificateVerificationChanged() override; + // QQmlListPropertyHelpers static void userScripts_append(QQmlListProperty<QQuickWebEngineScript> *p, QQuickWebEngineScript *script); static int userScripts_count(QQmlListProperty<QQuickWebEngineScript> *p); diff --git a/src/webengine/api/qquickwebenginetouchhandleprovider.cpp b/src/webengine/api/qquickwebenginetouchhandleprovider.cpp new file mode 100644 index 000000000..80f4727b6 --- /dev/null +++ b/src/webengine/api/qquickwebenginetouchhandleprovider.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** 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 "qquickwebenginetouchhandleprovider_p_p.h" + +// static +QString QQuickWebEngineTouchHandleProvider::identifier() +{ + return QStringLiteral("touchhandle"); +} + +// static +QUrl QQuickWebEngineTouchHandleProvider::url(int orientation) +{ + return QUrl(QStringLiteral("image://%1/%2").arg(identifier(), QString::number(orientation))); +} + +QQuickWebEngineTouchHandleProvider::QQuickWebEngineTouchHandleProvider() + : QQuickImageProvider(QQuickImageProvider::Image) +{ +} + +QQuickWebEngineTouchHandleProvider::~QQuickWebEngineTouchHandleProvider() +{ +} + +void QQuickWebEngineTouchHandleProvider::init(const QMap<int, QImage> &images) +{ + if (!m_touchHandleMap.empty()) { + Q_ASSERT(images.size() == m_touchHandleMap.size()); + return; + } + + m_touchHandleMap.unite(images); +} + +QImage QQuickWebEngineTouchHandleProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize) +{ + Q_UNUSED(size); + Q_UNUSED(requestedSize); + + Q_ASSERT(m_touchHandleMap.contains(id.toInt())); + return m_touchHandleMap.value(id.toInt()); +} diff --git a/src/webengine/api/qquickwebenginetouchhandleprovider_p_p.h b/src/webengine/api/qquickwebenginetouchhandleprovider_p_p.h new file mode 100644 index 000000000..277436289 --- /dev/null +++ b/src/webengine/api/qquickwebenginetouchhandleprovider_p_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKWEBENGINETOUCHHANDLEPROVIDER_P_P_H +#define QQUICKWEBENGINETOUCHHANDLEPROVIDER_P_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 <QtQuick/QQuickImageProvider> +#include <QtWebEngine/private/qtwebengineglobal_p.h> + +QT_BEGIN_NAMESPACE + +class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineTouchHandleProvider : public QQuickImageProvider { +public: + static QString identifier(); + static QUrl url(int orientation); + + QQuickWebEngineTouchHandleProvider(); + ~QQuickWebEngineTouchHandleProvider(); + + void init(const QMap<int, QImage> &images); + virtual QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize); + +private: + QMap<int, QImage> m_touchHandleMap; +}; + + +QT_END_NAMESPACE + +#endif // QQUICKWEBENGINETOUCHHANDLEPROVIDER_P_P_H diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp index b9ae06aeb..59431cadc 100644 --- a/src/webengine/api/qquickwebengineview.cpp +++ b/src/webengine/api/qquickwebengineview.cpp @@ -39,17 +39,18 @@ #include "qquickwebengineview_p.h" #include "qquickwebengineview_p_p.h" -#include "qtwebenginecoreglobal_p.h" #include "authentication_dialog_controller.h" #include "profile_adapter.h" #include "certificate_error_controller.h" #include "file_picker_controller.h" #include "javascript_dialog_controller.h" +#include "touch_selection_menu_controller.h" #include "qquickwebengineaction_p.h" #include "qquickwebengineaction_p_p.h" #include "qquickwebenginehistory_p.h" #include "qquickwebenginecertificateerror_p.h" +#include "qquickwebengineclientcertificateselection_p.h" #include "qquickwebenginecontextmenurequest_p.h" #include "qquickwebenginedialogrequests_p.h" #include "qquickwebenginefaviconprovider_p_p.h" @@ -59,6 +60,7 @@ #include "qquickwebengineprofile_p.h" #include "qquickwebenginesettings_p.h" #include "qquickwebenginescript_p.h" +#include "qquickwebenginetouchhandleprovider_p_p.h" #include "qwebenginequotarequest.h" #include "qwebengineregisterprotocolhandlerrequest.h" @@ -300,9 +302,17 @@ void QQuickWebEngineViewPrivate::allowCertificateError(const QSharedPointer<Cert m_certificateErrorControllers.append(errorController); } -void QQuickWebEngineViewPrivate::selectClientCert(const QSharedPointer<ClientCertSelectController> &) +void QQuickWebEngineViewPrivate::selectClientCert(const QSharedPointer<ClientCertSelectController> &controller) { - // Doing nothing will free the select-controller and perform default continue. +#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) + Q_Q(QQuickWebEngineView); + QQuickWebEngineClientCertificateSelection *certSelection = new QQuickWebEngineClientCertificateSelection(controller); + // mark the object for gc by creating temporary jsvalue + qmlEngine(q)->newQObject(certSelection); + Q_EMIT q->selectClientCertificate(certSelection); +#else + Q_UNUSED(controller); +#endif } void QQuickWebEngineViewPrivate::runGeolocationPermissionRequest(const QUrl &url) @@ -1130,12 +1140,12 @@ void QQuickWebEngineViewPrivate::didFindText(quint64 requestId, int matchCount) callback.call(args); } -void QQuickWebEngineViewPrivate::didPrintPage(quint64 requestId, const QByteArray &result) +void QQuickWebEngineViewPrivate::didPrintPage(quint64 requestId, QSharedPointer<QByteArray> result) { Q_Q(QQuickWebEngineView); QJSValue callback = m_callbacks.take(requestId); QJSValueList args; - args.append(qmlEngine(q)->toScriptValue(result)); + args.append(qmlEngine(q)->toScriptValue(*(result.data()))); callback.call(args); } @@ -1203,6 +1213,39 @@ void QQuickWebEngineViewPrivate::setToolTip(const QString &toolTipText) ui()->showToolTip(toolTipText); } +QtWebEngineCore::TouchHandleDrawableClient *QQuickWebEngineViewPrivate::createTouchHandle(const QMap<int, QImage> &images) +{ + return new QQuickWebEngineTouchHandle(ui(), images); +} + +void QQuickWebEngineViewPrivate::showTouchSelectionMenu(QtWebEngineCore::TouchSelectionMenuController *menuController, const QRect &selectionBounds, const QSize &handleSize) +{ + Q_UNUSED(handleSize); + + const int kSpacingBetweenButtons = 2; + const int kMenuButtonMinWidth = 63; + const int kMenuButtonMinHeight = 38; + + int buttonCount = menuController->buttonCount(); + if (buttonCount == 1) { + menuController->runContextMenu(); + return; + } + + int width = (kSpacingBetweenButtons * (buttonCount + 1)) + (kMenuButtonMinWidth * buttonCount); + int height = kMenuButtonMinHeight + kSpacingBetweenButtons; + int x = (selectionBounds.x() + selectionBounds.x() + selectionBounds.width() - width) / 2; + int y = selectionBounds.y() - height - 2; + + QRect bounds(x, y, width, height); + ui()->showTouchSelectionMenu(menuController, bounds, kSpacingBetweenButtons); +} + +void QQuickWebEngineViewPrivate::hideTouchSelectionMenu() +{ + ui()->hideTouchSelectionMenu(); +} + bool QQuickWebEngineView::isLoading() const { Q_D(const QQuickWebEngineView); @@ -2277,5 +2320,43 @@ bool QQuickContextMenuBuilder::isMenuItemEnabled(ContextMenuItem menuItem) Q_UNREACHABLE(); } + +QQuickWebEngineTouchHandle::QQuickWebEngineTouchHandle(QtWebEngineCore::UIDelegatesManager *ui, const QMap<int, QImage> &images) +{ + Q_ASSERT(ui); + m_item.reset(ui->createTouchHandle()); + + QQmlEngine *engine = qmlEngine(m_item.data()); + Q_ASSERT(engine); + QQuickWebEngineTouchHandleProvider *touchHandleProvider = + static_cast<QQuickWebEngineTouchHandleProvider *>(engine->imageProvider(QQuickWebEngineTouchHandleProvider::identifier())); + Q_ASSERT(touchHandleProvider); + touchHandleProvider->init(images); +} + +void QQuickWebEngineTouchHandle::setImage(int orientation) +{ + QUrl url = QQuickWebEngineTouchHandleProvider::url(orientation); + m_item->setProperty("source", url); +} + +void QQuickWebEngineTouchHandle::setBounds(const QRect &bounds) +{ + m_item->setProperty("x", bounds.x()); + m_item->setProperty("y", bounds.y()); + m_item->setProperty("width", bounds.width()); + m_item->setProperty("height", bounds.height()); +} + +void QQuickWebEngineTouchHandle::setVisible(bool visible) +{ + m_item->setProperty("visible", visible); +} + +void QQuickWebEngineTouchHandle::setOpacity(float opacity) +{ + m_item->setProperty("opacity", opacity); +} + QT_END_NAMESPACE diff --git a/src/webengine/api/qquickwebengineview_p.h b/src/webengine/api/qquickwebengineview_p.h index ae92b6df0..3a40abf0a 100644 --- a/src/webengine/api/qquickwebengineview_p.h +++ b/src/webengine/api/qquickwebengineview_p.h @@ -51,12 +51,11 @@ // We mean it. // -#include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h> #include <QtWebEngine/private/qtwebengineglobal_p.h> -#include "qquickwebenginescript.h" #include <QQuickItem> #include <QtGui/qcolor.h> +#include "qquickwebenginescript.h" QT_BEGIN_NAMESPACE @@ -65,6 +64,7 @@ class QQuickContextMenuBuilder; class QQuickWebEngineAction; class QQuickWebEngineAuthenticationDialogRequest; class QQuickWebEngineCertificateError; +class QQuickWebEngineClientCertificateSelection; class QQuickWebEngineColorDialogRequest; class QQuickWebEngineContextMenuRequest; class QQuickWebEngineFaviconProvider; @@ -104,7 +104,7 @@ private: const bool m_toggleOn; }; -#define LATEST_WEBENGINEVIEW_REVISION 7 +#define LATEST_WEBENGINEVIEW_REVISION 9 class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineView : public QQuickItem { Q_OBJECT @@ -550,6 +550,7 @@ Q_SIGNALS: Q_REVISION(7) void devToolsViewChanged(); Q_REVISION(7) void registerProtocolHandlerRequested(const QWebEngineRegisterProtocolHandlerRequest &request); Q_REVISION(8) void printRequested(); + Q_REVISION(9) void selectClientCertificate(QQuickWebEngineClientCertificateSelection *clientCertSelection); #if QT_CONFIG(webengine_testsupport) void testSupportChanged(); diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h index cbba9b568..3c985cba1 100644 --- a/src/webengine/api/qquickwebengineview_p_p.h +++ b/src/webengine/api/qquickwebengineview_p_p.h @@ -53,6 +53,7 @@ #include "qquickwebengineview_p.h" #include "render_view_context_menu_qt.h" +#include "touch_handle_drawable_client.h" #include "web_contents_adapter_client.h" #include <QPointer> @@ -64,6 +65,8 @@ namespace QtWebEngineCore { class RenderWidgetHostViewQtDelegateQuick; +class TouchHandleDrawableClient; +class TouchSelectionMenuController; class UIDelegatesManager; class WebContentsAdapter; } @@ -76,6 +79,7 @@ class QQuickWebEngineContextMenuRequest; class QQuickWebEngineSettings; class QQuickWebEngineFaviconProvider; class QQuickWebEngineProfilePrivate; +class QQuickWebEngineTouchHandleProvider; QQuickWebEngineView::WebAction editorActionForKeyEvent(QKeyEvent* event); @@ -128,7 +132,7 @@ public: void didFetchDocumentMarkup(quint64, const QString&) override { } void didFetchDocumentInnerText(quint64, const QString&) override { } void didFindText(quint64, int) override; - void didPrintPage(quint64 requestId, const QByteArray &result) override; + void didPrintPage(quint64 requestId, QSharedPointer<QByteArray>) override; void didPrintPageToPdf(const QString &filePath, bool success) override; void passOnFocus(bool reverse) override; void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString& message, int lineNumber, const QString& sourceID) override; @@ -152,6 +156,9 @@ public: bool supportsDragging() const override; bool isEnabled() const override; void setToolTip(const QString &toolTipText) override; + QtWebEngineCore::TouchHandleDrawableClient *createTouchHandle(const QMap<int, QImage> &images) override; + void showTouchSelectionMenu(QtWebEngineCore::TouchSelectionMenuController *, const QRect &, const QSize &) override; + void hideTouchSelectionMenu() override; const QObject *holdingQObject() const override; ClientType clientType() override { return QtWebEngineCore::WebContentsAdapterClient::QmlClient; } @@ -253,6 +260,19 @@ private: QObject *m_menu; }; +class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineTouchHandle : public QtWebEngineCore::TouchHandleDrawableClient { +public: + QQuickWebEngineTouchHandle(QtWebEngineCore::UIDelegatesManager *ui, const QMap<int, QImage> &images); + + void setImage(int orientation) override; + void setBounds(const QRect &bounds) override; + void setVisible(bool visible) override; + void setOpacity(float opacity) override; + +private: + QScopedPointer<QQuickItem> m_item; +}; + QT_END_NAMESPACE #endif // QQUICKWEBENGINEVIEW_P_P_H diff --git a/src/webengine/api/qtwebengineglobal.h b/src/webengine/api/qtwebengineglobal.h index 2d83be674..c2b33778a 100644 --- a/src/webengine/api/qtwebengineglobal.h +++ b/src/webengine/api/qtwebengineglobal.h @@ -41,6 +41,7 @@ #define QTWEBENGINEGLOBAL_H #include <QtCore/qglobal.h> +#include <QtWebEngine/qtwebengine-config.h> QT_BEGIN_NAMESPACE diff --git a/src/webengine/api/qtwebengineglobal_p.h b/src/webengine/api/qtwebengineglobal_p.h index 7058bef09..2d30f75b0 100644 --- a/src/webengine/api/qtwebengineglobal_p.h +++ b/src/webengine/api/qtwebengineglobal_p.h @@ -51,7 +51,9 @@ // We mean it. // -#include "qtwebengineglobal.h" +#include <QtWebEngine/qtwebengineglobal.h> +#include <QtCore/private/qglobal_p.h> +#include <QtWebEngine/private/qtwebengine-config_p.h> QT_BEGIN_NAMESPACE diff --git a/src/webengine/configure.json b/src/webengine/configure.json new file mode 100644 index 000000000..ec5ad34d9 --- /dev/null +++ b/src/webengine/configure.json @@ -0,0 +1,29 @@ +{ + "module": "webengine", + "depends": [ + "webenginecore-private" + ], + "features": { + "webengine-ui-delegates": { + "label": "UI Delegates", + "section": "WebEngine", + "output": [ "privateFeature" ] + }, + "webengine-testsupport": { + "label": "Test Support", + "autoDetect": "features.private_tests || call.isTestsInBuildParts", + "output": [ "privateFeature" ] + } + }, + "summary": [ + { + "section": "Qt WebEngineQml", + "condition": "features.webengine-qml", + "entries": [ + "webengine-ui-delegates", + "webengine-testsupport" + ] + } + ] +} + diff --git a/src/webengine/doc/src/webengineview_lgpl.qdoc b/src/webengine/doc/src/webengineview_lgpl.qdoc index 0e25e16f3..d96aabc97 100644 --- a/src/webengine/doc/src/webengineview_lgpl.qdoc +++ b/src/webengine/doc/src/webengineview_lgpl.qdoc @@ -1475,3 +1475,19 @@ \sa printToPdf */ + +/*! + \qmlsignal WebEngineView::selectClientCertificate(WebEngineClientCertificateSelection clientCertificateSelection) + \since QtWebEngine 1.9 + + This signal is emitted when a web site requests an SSL client certificate, and one or more were + found in the system's client certificate store. + + Handling the signal is asynchronous, and loading will be waiting until a certificate is selected, + or the last copy of \a clientCertificateSelection is destroyed. + + If the signal is not handled, \a clientCertificateSelection is automatically destroyed, and loading + will continue without a client certificate. + + \sa WebEngineClientCertificateSelection +*/ diff --git a/src/webengine/module.pro b/src/webengine/module.pro new file mode 100644 index 000000000..49a1086b2 --- /dev/null +++ b/src/webengine/module.pro @@ -0,0 +1,94 @@ +include($$QTWEBENGINE_OUT_ROOT/src/webengine/qtwebengine-config.pri) +QT_FOR_CONFIG += webengine-private + +TARGET = QtWebEngine +MODULE = webengine + +# For our export macros +DEFINES += QT_BUILD_WEBENGINE_LIB + +QT += qml quick webenginecore +QT_PRIVATE += quick-private gui-private core-private webenginecore-private + +QMAKE_DOCS = $$PWD/doc/qtwebengine.qdocconf + +INCLUDEPATH += $$PWD api ../core ../core/api + +SOURCES = \ + api/qquickwebengineaction.cpp \ + api/qquickwebenginecertificateerror.cpp \ + api/qquickwebengineclientcertificateselection.cpp \ + api/qquickwebenginecontextmenurequest.cpp \ + api/qquickwebenginedialogrequests.cpp \ + api/qquickwebenginedownloaditem.cpp \ + api/qquickwebenginehistory.cpp \ + api/qquickwebenginefaviconprovider.cpp \ + api/qquickwebengineloadrequest.cpp \ + api/qquickwebenginenavigationrequest.cpp \ + api/qquickwebenginenewviewrequest.cpp \ + api/qquickwebengineprofile.cpp \ + api/qquickwebenginescript.cpp \ + api/qquickwebenginesettings.cpp \ + api/qquickwebenginesingleton.cpp \ + api/qquickwebenginetouchhandleprovider.cpp \ + api/qquickwebengineview.cpp \ + api/qtwebengineglobal.cpp \ + render_widget_host_view_qt_delegate_quick.cpp \ + render_widget_host_view_qt_delegate_quickwindow.cpp \ + ui_delegates_manager.cpp + +HEADERS = \ + api/qtwebengineglobal.h \ + api/qtwebengineglobal_p.h \ + api/qquickwebengineaction_p.h \ + api/qquickwebengineaction_p_p.h \ + api/qquickwebenginecertificateerror_p.h \ + api/qquickwebengineclientcertificateselection_p.h \ + api/qquickwebenginecontextmenurequest_p.h \ + api/qquickwebenginedialogrequests_p.h \ + api/qquickwebenginedownloaditem_p.h \ + api/qquickwebenginedownloaditem_p_p.h \ + api/qquickwebenginehistory_p.h \ + api/qquickwebenginefaviconprovider_p_p.h \ + api/qquickwebengineloadrequest_p.h \ + api/qquickwebenginenavigationrequest_p.h \ + api/qquickwebenginenewviewrequest_p.h \ + api/qquickwebengineprofile.h \ + api/qquickwebengineprofile_p.h \ + api/qquickwebenginescript.h \ + api/qquickwebenginescript_p.h \ + api/qquickwebenginesettings_p.h \ + api/qquickwebenginesingleton_p.h \ + api/qquickwebenginetouchhandleprovider_p_p.h \ + api/qquickwebengineview_p.h \ + api/qquickwebengineview_p_p.h \ + render_widget_host_view_qt_delegate_quick.h \ + render_widget_host_view_qt_delegate_quickwindow.h \ + ui_delegates_manager.h + +qtConfig(webengine-testsupport) { + QT_PRIVATE += testlib + SOURCES += api/qquickwebenginetestsupport.cpp + HEADERS += api/qquickwebenginetestsupport_p.h +} + +!build_pass { + python = $$pythonPathForShell() + chromium_attributions.commands = \ + cd $$shell_quote($$shell_path($$PWD/../3rdparty)) && \ + $$python chromium/tools/licenses.py \ + --file-template ../../tools/about_credits.tmpl \ + --entry-template ../../tools/about_credits_entry.tmpl credits \ + $$shell_quote($$shell_path($$OUT_PWD/chromium_attributions.qdoc)) + chromium_attributions.CONFIG += phony + + QMAKE_EXTRA_TARGETS += chromium_attributions + + prepare_docs { + prepare_docs.depends += chromium_attributions + } else { + html_docs.depends += chromium_attributions + } +} + +load(qt_module) diff --git a/src/webengine/plugin/plugin.cpp b/src/webengine/plugin/plugin.cpp index 84a12c930..0e63989ee 100644 --- a/src/webengine/plugin/plugin.cpp +++ b/src/webengine/plugin/plugin.cpp @@ -40,22 +40,23 @@ #include <QtQml/qqmlextensionplugin.h> #include <QtWebEngine/QQuickWebEngineProfile> -#include "qquickwebenginecertificateerror_p.h" -#include "qquickwebenginecontextmenurequest_p.h" -#include "qquickwebenginedialogrequests_p.h" -#include "qquickwebenginedownloaditem_p.h" -#include "qquickwebenginehistory_p.h" -#include "qquickwebenginefaviconprovider_p_p.h" -#include "qquickwebengineloadrequest_p.h" -#include "qquickwebenginenavigationrequest_p.h" -#include "qquickwebenginenewviewrequest_p.h" -#include "qquickwebenginesettings_p.h" -#include "qquickwebenginesingleton_p.h" -#include "qquickwebengineview_p.h" -#include "qquickwebengineaction_p.h" -#include "qwebenginequotarequest.h" -#include "qwebengineregisterprotocolhandlerrequest.h" -#include "qtwebengineversion.h" +#include <QtWebEngine/private/qquickwebenginecertificateerror_p.h> +#include <QtWebEngine/private/qquickwebengineclientcertificateselection_p.h> +#include <QtWebEngine/private/qquickwebenginecontextmenurequest_p.h> +#include <QtWebEngine/private/qquickwebenginedialogrequests_p.h> +#include <QtWebEngine/private/qquickwebenginedownloaditem_p.h> +#include <QtWebEngine/private/qquickwebenginehistory_p.h> +#include <QtWebEngine/private/qquickwebenginefaviconprovider_p_p.h> +#include <QtWebEngine/private/qquickwebengineloadrequest_p.h> +#include <QtWebEngine/private/qquickwebenginenavigationrequest_p.h> +#include <QtWebEngine/private/qquickwebenginenewviewrequest_p.h> +#include <QtWebEngine/private/qquickwebenginesettings_p.h> +#include <QtWebEngine/private/qquickwebenginesingleton_p.h> +#include <QtWebEngine/private/qquickwebenginetouchhandleprovider_p_p.h> +#include <QtWebEngine/private/qquickwebengineview_p.h> +#include <QtWebEngine/private/qquickwebengineaction_p.h> +#include <QtWebEngineCore/qwebenginequotarequest.h> +#include <QtWebEngineCore/qwebengineregisterprotocolhandlerrequest.h> QT_BEGIN_NAMESPACE @@ -73,6 +74,7 @@ public: { Q_UNUSED(uri); engine->addImageProvider(QQuickWebEngineFaviconProvider::identifier(), new QQuickWebEngineFaviconProvider); + engine->addImageProvider(QQuickWebEngineTouchHandleProvider::identifier(), new QQuickWebEngineTouchHandleProvider); } void registerTypes(const char *uri) override @@ -91,11 +93,13 @@ public: qmlRegisterType<QQuickWebEngineView, 6>(uri, 1, 6, "WebEngineView"); qmlRegisterType<QQuickWebEngineView, 7>(uri, 1, 7, "WebEngineView"); qmlRegisterType<QQuickWebEngineView, 8>(uri, 1, 8, "WebEngineView"); + qmlRegisterType<QQuickWebEngineView, 9>(uri, 1, 9, "WebEngineView"); qmlRegisterType<QQuickWebEngineProfile>(uri, 1, 1, "WebEngineProfile"); qmlRegisterType<QQuickWebEngineProfile, 1>(uri, 1, 2, "WebEngineProfile"); qmlRegisterType<QQuickWebEngineProfile, 2>(uri, 1, 3, "WebEngineProfile"); qmlRegisterType<QQuickWebEngineProfile, 3>(uri, 1, 4, "WebEngineProfile"); qmlRegisterType<QQuickWebEngineProfile, 4>(uri, 1, 5, "WebEngineProfile"); + qmlRegisterType<QQuickWebEngineProfile, 5>(uri, 1, 9, "WebEngineProfile"); qmlRegisterType<QQuickWebEngineScript>(uri, 1, 1, "WebEngineScript"); qmlRegisterUncreatableType<QQuickWebEngineCertificateError>(uri, 1, 1, "WebEngineCertificateError", msgUncreatableType("WebEngineCertificateError")); qmlRegisterUncreatableType<QQuickWebEngineDownloadItem>(uri, 1, 1, "WebEngineDownloadItem", @@ -153,6 +157,11 @@ public: qmlRegisterUncreatableType<QWebEngineRegisterProtocolHandlerRequest>(uri, 1, 7, "RegisterProtocolHandlerRequest", msgUncreatableType("RegisterProtocolHandlerRequest")); qmlRegisterUncreatableType<QQuickWebEngineAction>(uri, 1, 8, "WebEngineAction", msgUncreatableType("WebEngineAction")); + qmlRegisterUncreatableType<QQuickWebEngineClientCertificateSelection>(uri, 1, 9, "WebEngineClientCertificateSelection", + tr("Cannot create a separate instance of WebEngineClientCertificateSelection")); + qmlRegisterUncreatableType<QQuickWebEngineClientCertificateOption>(uri, 1, 9, "WebEngineClientCertificateOption", + tr("Cannot create a separate instance of WebEngineClientCertificateOption")); + } private: diff --git a/src/webengine/plugin/plugin.pro b/src/webengine/plugin/plugin.pro index b6652fa26..102f9a9fe 100644 --- a/src/webengine/plugin/plugin.pro +++ b/src/webengine/plugin/plugin.pro @@ -1,13 +1,11 @@ CXX_MODULE = qml TARGET = qtwebengineplugin TARGETPATH = QtWebEngine -IMPORT_VERSION = 1.8 +IMPORT_VERSION = 1.9 -QT += webengine qml quick +QT += qml quick QT_PRIVATE += core-private webenginecore-private webengine-private -INCLUDEPATH += $$QTWEBENGINE_ROOT/src/core $$QTWEBENGINE_ROOT/src/core/api $$QTWEBENGINE_ROOT/src/webengine $$QTWEBENGINE_ROOT/src/webengine/api $$QTWEBENGINE_ROOT/include/QtWebEngine - SOURCES = plugin.cpp load(qml_plugin) diff --git a/src/webengine/plugin/plugins.qmltypes b/src/webengine/plugin/plugins.qmltypes index 1f295ac57..435124e30 100644 --- a/src/webengine/plugin/plugins.qmltypes +++ b/src/webengine/plugin/plugins.qmltypes @@ -4,7 +4,7 @@ import QtQuick.tooling 1.2 // It is used for QML tooling purposes only. // // This file was auto-generated by: -// 'qmlplugindump -defaultplatform -dependencies dependencies.json -nonrelocatable QtWebEngine 1.8' +// 'qmlplugindump -defaultplatform -dependencies dependencies.json -nonrelocatable QtWebEngine 1.9' Module { dependencies: ["QtQuick 2.8"] @@ -437,9 +437,10 @@ Module { "QtWebEngine/WebEngineProfile 1.2", "QtWebEngine/WebEngineProfile 1.3", "QtWebEngine/WebEngineProfile 1.4", - "QtWebEngine/WebEngineProfile 1.5" + "QtWebEngine/WebEngineProfile 1.5", + "QtWebEngine/WebEngineProfile 1.9" ] - exportMetaObjectRevisions: [0, 1, 2, 3, 4] + exportMetaObjectRevisions: [0, 1, 2, 3, 4, 5] Enum { name: "HttpCacheType" values: { @@ -474,9 +475,13 @@ Module { isList: true isReadonly: true } + Property { name: "useForGlobalCertificateVerification"; revision: 5; type: "bool" } + Property { name: "downloadPath"; revision: 5; type: "string" } Signal { name: "httpAcceptLanguageChanged"; revision: 1 } Signal { name: "spellCheckLanguagesChanged"; revision: 3 } Signal { name: "spellCheckEnabledChanged"; revision: 3 } + Signal { name: "useForGlobalCertificateVerificationChanged"; revision: 5 } + Signal { name: "downloadPathChanged"; revision: 5 } Signal { name: "downloadRequested" Parameter { name: "download"; type: "QQuickWebEngineDownloadItem"; isPointer: true } @@ -664,9 +669,10 @@ Module { "QtWebEngine/WebEngineView 1.5", "QtWebEngine/WebEngineView 1.6", "QtWebEngine/WebEngineView 1.7", - "QtWebEngine/WebEngineView 1.8" + "QtWebEngine/WebEngineView 1.8", + "QtWebEngine/WebEngineView 1.9" ] - exportMetaObjectRevisions: [0, 1, 2, 3, 4, 5, 6, 7, 8] + exportMetaObjectRevisions: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Enum { name: "NavigationRequestAction" values: { diff --git a/src/webengine/plugin/testsupport/plugin.cpp b/src/webengine/testsupport/plugin.cpp index d5c43a859..7a1e73d8b 100644 --- a/src/webengine/plugin/testsupport/plugin.cpp +++ b/src/webengine/testsupport/plugin.cpp @@ -39,7 +39,7 @@ #include <QtQml> -#include "qquickwebenginetestsupport_p.h" +#include <QtWebEngine/private/qquickwebenginetestsupport_p.h> QT_BEGIN_NAMESPACE diff --git a/src/webengine/plugin/testsupport/qmldir b/src/webengine/testsupport/qmldir index 7fff80251..7fff80251 100644 --- a/src/webengine/plugin/testsupport/qmldir +++ b/src/webengine/testsupport/qmldir diff --git a/src/webengine/plugin/testsupport/testsupport.pro b/src/webengine/testsupport/testsupport.pro index 2804635f8..a24796675 100644 --- a/src/webengine/plugin/testsupport/testsupport.pro +++ b/src/webengine/testsupport/testsupport.pro @@ -3,11 +3,9 @@ TARGET = qtwebenginetestsupportplugin TARGETPATH = QtWebEngine/testsupport IMPORT_VERSION = 1.0 -QT += webengine qml quick +QT += qml quick QT_PRIVATE += webengine-private gui-private -INCLUDEPATH += $$QTWEBENGINE_ROOT/src/core $$QTWEBENGINE_ROOT/src/webengine $$QTWEBENGINE_ROOT/src/webengine/api - SOURCES = plugin.cpp load(qml_plugin) diff --git a/src/webengine/ui/TouchHandle.qml b/src/webengine/ui/TouchHandle.qml new file mode 100644 index 000000000..76a93829e --- /dev/null +++ b/src/webengine/ui/TouchHandle.qml @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +import QtQuick 2.5 + +Image { } diff --git a/src/webengine/ui/TouchSelectionMenu.qml b/src/webengine/ui/TouchSelectionMenu.qml new file mode 100644 index 000000000..7cf16b554 --- /dev/null +++ b/src/webengine/ui/TouchSelectionMenu.qml @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Layouts 1.3 + +Rectangle { + id: menu + + signal cutTriggered + signal copyTriggered + signal pasteTriggered + signal contextMenuTriggered + + property bool isCutEnabled: false + property bool isCopyEnabled: false + property bool isPasteEnabled: false + + property color borderColor: "darkGray" + property color bgColor: "white" + + radius: 4 + border.color: borderColor + color: borderColor + antialiasing: true + + RowLayout { + anchors.fill: parent + spacing: parent.border.width + anchors.margins: parent.border.width + + Rectangle { + Layout.fillHeight: true + Layout.fillWidth: true + radius: menu.radius + color: bgColor + visible: isCutEnabled + + Text { + id: cutText + anchors.centerIn: parent + text: "Cut" + } + + MouseArea { + anchors.fill: parent + onPressed: { + parent.color = borderColor; + cutText.color = "white"; + } + onReleased: { + parent.color = bgColor; + cutText.color = "black"; + cutTriggered(); + } + } + } + + Rectangle { + Layout.fillHeight: true + Layout.fillWidth: true + radius: menu.radius + color: bgColor + visible: isCopyEnabled + + Text { + id: copyText + anchors.centerIn: parent + text: "Copy" + } + + MouseArea { + anchors.fill: parent + onPressed: { + parent.color = borderColor; + copyText.color = "white"; + } + onReleased: { + parent.color = bgColor; + copyText.color = "black"; + copyTriggered(); + } + } + } + + Rectangle { + Layout.fillHeight: true + Layout.fillWidth: true + radius: menu.radius + color: bgColor + visible: isPasteEnabled + + Text { + id: pasteText + anchors.centerIn: parent + text: "Paste" + } + + MouseArea { + anchors.fill: parent + onPressed: { + parent.color = borderColor; + pasteText.color = "white"; + } + onReleased: { + parent.color = bgColor; + pasteText.color = "black"; + pasteTriggered(); + } + } + } + + Rectangle { + Layout.fillHeight: true + Layout.fillWidth: true + radius: menu.radius + color: bgColor + + Text { + id: contextMenuText + anchors.centerIn: parent + text: "..." + } + + MouseArea { + anchors.fill: parent + onPressed: { + parent.color = borderColor; + contextMenuText.color = "white"; + } + onReleased: { + parent.color = bgColor; + contextMenuText.color = "black"; + contextMenuTriggered(); + } + } + } + } +} diff --git a/src/webengine/ui/ui.pro b/src/webengine/ui/ui.pro index eb6bf435c..69f754e0c 100644 --- a/src/webengine/ui/ui.pro +++ b/src/webengine/ui/ui.pro @@ -13,6 +13,8 @@ QML_FILES += \ Menu.qml \ MenuItem.qml \ MenuSeparator.qml \ - ToolTip.qml + ToolTip.qml \ + TouchHandle.qml \ + TouchSelectionMenu.qml load(qml_module) diff --git a/src/webengine/ui_delegates_manager.cpp b/src/webengine/ui_delegates_manager.cpp index 7e49bc77d..da120ab69 100644 --- a/src/webengine/ui_delegates_manager.cpp +++ b/src/webengine/ui_delegates_manager.cpp @@ -44,6 +44,7 @@ #include <color_chooser_controller.h> #include <file_picker_controller.h> #include <javascript_dialog_controller.h> +#include <touch_selection_menu_controller.h> #include <web_contents_adapter_client.h> #include <QFileInfo> @@ -54,6 +55,7 @@ #include <QCursor> #include <QList> #include <QScreen> +#include <QTimer> #include <QGuiApplication> // Uncomment for QML debugging @@ -125,6 +127,7 @@ const char *defaultPropertyName(QObject *obj) UIDelegatesManager::UIDelegatesManager(QQuickWebEngineView *view) : m_view(view) , m_toolTip(nullptr) + , m_touchSelectionMenu(nullptr) FOR_EACH_COMPONENT_TYPE(COMPONENT_MEMBER_INIT, NO_SEPARATOR) { } @@ -568,6 +571,82 @@ void UIDelegatesManager::showToolTip(const QString &text) QMetaObject::invokeMethod(m_toolTip.data(), "open"); } +QQuickItem *UIDelegatesManager::createTouchHandle() +{ + if (!ensureComponentLoaded(TouchHandle)) + return nullptr; + + QQmlContext *context = qmlContext(m_view); + QObject *touchHandle = touchHandleComponent->beginCreate(context); + QQuickItem *item = qobject_cast<QQuickItem *>(touchHandle); + Q_ASSERT(item); + item->setParentItem(m_view); + touchHandleComponent->completeCreate(); + + return item; +} + +void UIDelegatesManager::showTouchSelectionMenu(QtWebEngineCore::TouchSelectionMenuController *menuController, const QRect &bounds, const int spacing) +{ + if (!ensureComponentLoaded(TouchSelectionMenu)) + return; + + QQmlContext *context = qmlContext(m_view); + m_touchSelectionMenu.reset(touchSelectionMenuComponent->beginCreate(context)); + if (QQuickItem *item = qobject_cast<QQuickItem *>(m_touchSelectionMenu.data())) + item->setParentItem(m_view); + m_touchSelectionMenu->setParent(m_view); + + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("width")).write(bounds.width()); + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("height")).write(bounds.height()); + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("x")).write(bounds.x()); + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("y")).write(bounds.y()); + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("border.width")).write(spacing); + + // Cut button + bool cutEnabled = menuController->isCommandEnabled(TouchSelectionMenuController::Cut); + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("isCutEnabled")).write(cutEnabled); + if (cutEnabled) { + QQmlProperty cutSignal(m_touchSelectionMenu.data(), QStringLiteral("onCutTriggered")); + CHECK_QML_SIGNAL_PROPERTY(cutSignal, touchSelectionMenuComponent->url()); + int cutIndex = menuController->metaObject()->indexOfSlot("cut()"); + QObject::connect(m_touchSelectionMenu.data(), cutSignal.method(), menuController, menuController->metaObject()->method(cutIndex)); + } + + // Copy button + bool copyEnabled = menuController->isCommandEnabled(TouchSelectionMenuController::Copy); + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("isCopyEnabled")).write(copyEnabled); + if (copyEnabled) { + QQmlProperty copySignal(m_touchSelectionMenu.data(), QStringLiteral("onCopyTriggered")); + CHECK_QML_SIGNAL_PROPERTY(copySignal, touchSelectionMenuComponent->url()); + int copyIndex = menuController->metaObject()->indexOfSlot("copy()"); + QObject::connect(m_touchSelectionMenu.data(), copySignal.method(), menuController, menuController->metaObject()->method(copyIndex)); + } + + // Paste button + bool pasteEnabled = menuController->isCommandEnabled(TouchSelectionMenuController::Paste); + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("isPasteEnabled")).write(pasteEnabled); + if (pasteEnabled) { + QQmlProperty pasteSignal(m_touchSelectionMenu.data(), QStringLiteral("onPasteTriggered")); + CHECK_QML_SIGNAL_PROPERTY(pasteSignal, touchSelectionMenuComponent->url()); + int pasteIndex = menuController->metaObject()->indexOfSlot("paste()"); + QObject::connect(m_touchSelectionMenu.data(), pasteSignal.method(), menuController, menuController->metaObject()->method(pasteIndex)); + } + + // Context menu button + QQmlProperty contextMenuSignal(m_touchSelectionMenu.data(), QStringLiteral("onContextMenuTriggered")); + CHECK_QML_SIGNAL_PROPERTY(contextMenuSignal, touchSelectionMenuComponent->url()); + int contextMenuIndex = menuController->metaObject()->indexOfSlot("runContextMenu()"); + QObject::connect(m_touchSelectionMenu.data(), contextMenuSignal.method(), menuController, menuController->metaObject()->method(contextMenuIndex)); + + touchSelectionMenuComponent->completeCreate(); +} + +void UIDelegatesManager::hideTouchSelectionMenu() +{ + QTimer::singleShot(0, m_view, [this] { m_touchSelectionMenu.reset(); }); +} + UI2DelegatesManager::UI2DelegatesManager(QQuickWebEngineView *view) : UIDelegatesManager(view) { diff --git a/src/webengine/ui_delegates_manager.h b/src/webengine/ui_delegates_manager.h index 18457e4ed..4b6e291b2 100644 --- a/src/webengine/ui_delegates_manager.h +++ b/src/webengine/ui_delegates_manager.h @@ -61,6 +61,8 @@ F(FilePicker, filePicker) SEPARATOR \ F(AuthenticationDialog, authenticationDialog) SEPARATOR \ F(ToolTip, toolTip) SEPARATOR \ + F(TouchHandle, touchHandle) SEPARATOR \ + F(TouchSelectionMenu, touchSelectionMenu) SEPARATOR \ #define COMMA_SEPARATOR , #define SEMICOLON_SEPARATOR ; @@ -81,6 +83,7 @@ namespace QtWebEngineCore { class AuthenticationDialogController; class JavaScriptDialogController; class FilePickerController; +class TouchSelectionMenuController; const char *defaultPropertyName(QObject *obj); @@ -110,6 +113,9 @@ public: void showFilePicker(QSharedPointer<FilePickerController>); virtual void showMenu(QObject *menu); void showToolTip(const QString &text); + QQuickItem *createTouchHandle(); + void showTouchSelectionMenu(TouchSelectionMenuController *, const QRect &, const int spacing); + void hideTouchSelectionMenu(); protected: bool ensureComponentLoaded(ComponentType); @@ -117,6 +123,7 @@ protected: QQuickWebEngineView *m_view; QScopedPointer<QObject> m_toolTip; QStringList m_importDirs; + QScopedPointer<QObject> m_touchSelectionMenu; FOR_EACH_COMPONENT_TYPE(MEMBER_DECLARATION, SEMICOLON_SEPARATOR) diff --git a/src/webengine/webengine.pro b/src/webengine/webengine.pro index 418ade9a8..23668229e 100644 --- a/src/webengine/webengine.pro +++ b/src/webengine/webengine.pro @@ -1,89 +1,19 @@ -include($$QTWEBENGINE_OUT_ROOT/src/core/qtwebenginecore-config.pri) # workaround for QTBUG-68093 -QT_FOR_CONFIG += webenginecore-private +TEMPLATE = subdirs -TARGET = QtWebEngine +qml_module.file = module.pro +qml_plugin.file = plugin/plugin.pro -# For our export macros -DEFINES += QT_BUILD_WEBENGINE_LIB +qml_plugin.depends = qml_module -QT += qml quick webenginecore -QT_PRIVATE += quick-private gui-private core-private webenginecore-private - -QMAKE_DOCS = $$PWD/doc/qtwebengine.qdocconf - -INCLUDEPATH += $$PWD api ../core ../core/api - -SOURCES = \ - api/qquickwebengineaction.cpp \ - api/qquickwebenginecertificateerror.cpp \ - api/qquickwebenginecontextmenurequest.cpp \ - api/qquickwebenginedialogrequests.cpp \ - api/qquickwebenginedownloaditem.cpp \ - api/qquickwebenginehistory.cpp \ - api/qquickwebenginefaviconprovider.cpp \ - api/qquickwebengineloadrequest.cpp \ - api/qquickwebenginenavigationrequest.cpp \ - api/qquickwebenginenewviewrequest.cpp \ - api/qquickwebengineprofile.cpp \ - api/qquickwebenginescript.cpp \ - api/qquickwebenginesettings.cpp \ - api/qquickwebenginesingleton.cpp \ - api/qquickwebengineview.cpp \ - api/qtwebengineglobal.cpp \ - render_widget_host_view_qt_delegate_quick.cpp \ - render_widget_host_view_qt_delegate_quickwindow.cpp \ - ui_delegates_manager.cpp - -HEADERS = \ - api/qtwebengineglobal.h \ - api/qtwebengineglobal_p.h \ - api/qquickwebengineaction_p.h \ - api/qquickwebengineaction_p_p.h \ - api/qquickwebenginecertificateerror_p.h \ - api/qquickwebenginecontextmenurequest_p.h \ - api/qquickwebenginedialogrequests_p.h \ - api/qquickwebenginedownloaditem_p.h \ - api/qquickwebenginedownloaditem_p_p.h \ - api/qquickwebenginehistory_p.h \ - api/qquickwebenginefaviconprovider_p_p.h \ - api/qquickwebengineloadrequest_p.h \ - api/qquickwebenginenavigationrequest_p.h \ - api/qquickwebenginenewviewrequest_p.h \ - api/qquickwebengineprofile.h \ - api/qquickwebengineprofile_p.h \ - api/qquickwebenginescript.h \ - api/qquickwebenginescript_p.h \ - api/qquickwebenginesettings_p.h \ - api/qquickwebenginesingleton_p.h \ - api/qquickwebengineview_p.h \ - api/qquickwebengineview_p_p.h \ - render_widget_host_view_qt_delegate_quick.h \ - render_widget_host_view_qt_delegate_quickwindow.h \ - ui_delegates_manager.h +SUBDIRS += qml_module qml_plugin qtConfig(webengine-testsupport) { - QT_PRIVATE += testlib - SOURCES += api/qquickwebenginetestsupport.cpp - HEADERS += api/qquickwebenginetestsupport_p.h + testsupport_plugin.file = testsupport/testsupport.pro + testsupport_plugin.depends = qml_module + SUBDIRS += testsupport_plugin } -!build_pass { - python = $$pythonPathForShell() - chromium_attributions.commands = \ - cd $$shell_quote($$shell_path($$PWD/../3rdparty)) && \ - $$python chromium/tools/licenses.py \ - --file-template ../../tools/about_credits.tmpl \ - --entry-template ../../tools/about_credits_entry.tmpl credits \ - $$shell_quote($$shell_path($$OUT_PWD/chromium_attributions.qdoc)) - chromium_attributions.CONFIG += phony - - QMAKE_EXTRA_TARGETS += chromium_attributions - - prepare_docs { - prepare_docs.depends += chromium_attributions - } else { - html_docs.depends += chromium_attributions - } +qtConfig(webengine-ui-delegates) { + SUBDIRS += ui \ + ui2 } - -load(qt_module) diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp index 5da39e814..06a708672 100644 --- a/src/webenginewidgets/api/qwebenginepage.cpp +++ b/src/webenginewidgets/api/qwebenginepage.cpp @@ -48,7 +48,7 @@ #include "file_picker_controller.h" #include "javascript_dialog_controller.h" #if QT_CONFIG(webengine_printing_and_pdf) -#include "printing/pdfium_document_wrapper_qt.h" +#include "printer_worker.h" #endif #include "qwebenginecertificateerror.h" #include "qwebenginefullscreenrequest.h" @@ -94,6 +94,7 @@ #include <QMimeData> #if QT_CONFIG(webengine_printing_and_pdf) #include <QPrinter> +#include <QThread> #endif #include <QStandardPaths> #include <QStyle> @@ -106,89 +107,6 @@ using namespace QtWebEngineCore; static const int MaxTooltipLength = 1024; -#if QT_CONFIG(webengine_printing_and_pdf) -static bool printPdfDataOnPrinter(const QByteArray& data, QPrinter& printer) -{ - if (!data.size()) { - qWarning("Failure to print on printer %ls: Print result data is empty.", - qUtf16Printable(printer.printerName())); - return false; - } - - QSize pageSize = printer.pageRect().size(); - PdfiumDocumentWrapperQt pdfiumWrapper(data.constData(), data.size(), pageSize); - - int toPage = printer.toPage(); - int fromPage = printer.fromPage(); - bool ascendingOrder = true; - - if (fromPage == 0 && toPage == 0) { - fromPage = 1; - toPage = pdfiumWrapper.pageCount(); - } - fromPage = qMax(1, fromPage); - toPage = qMin(pdfiumWrapper.pageCount(), toPage); - - if (printer.pageOrder() == QPrinter::LastPageFirst) { - qSwap(fromPage, toPage); - ascendingOrder = false; - } - - int pageCopies = 1; - int documentCopies = 1; - - if (!printer.supportsMultipleCopies()) - documentCopies = printer.copyCount(); - - if (printer.collateCopies()) { - pageCopies = documentCopies; - documentCopies = 1; - } - - QPainter painter; - if (!painter.begin(&printer)) { - qWarning("Failure to print on printer %ls: Could not open printer for painting.", - qUtf16Printable(printer.printerName())); - return false; - } - - for (int printedDocuments = 0; printedDocuments < documentCopies; printedDocuments++) { - int currentPageIndex = fromPage; - while (true) { - for (int printedPages = 0; printedPages < pageCopies; printedPages++) { - if (printer.printerState() == QPrinter::Aborted - || printer.printerState() == QPrinter::Error) - return false; - - QImage currentImage = pdfiumWrapper.pageAsQImage(currentPageIndex - 1); - if (currentImage.isNull()) - return false; - - // Painting operations are automatically clipped to the bounds of the drawable part of the page. - painter.drawImage(QRect(0, 0, pageSize.width(), pageSize.height()), currentImage, currentImage.rect()); - if (printedPages < pageCopies - 1) - printer.newPage(); - } - - if (currentPageIndex == toPage) - break; - - if (ascendingOrder) - currentPageIndex++; - else - currentPageIndex--; - - printer.newPage(); - } - if (printedDocuments < documentCopies - 1) - printer.newPage(); - } - painter.end(); - - return true; -} -#endif // QT_CONFIG(webengine_printing_and_pdf) - static QWebEnginePage::WebWindowType toWindowType(WebContentsAdapterClient::WindowOpenDisposition disposition) { switch (disposition) { @@ -241,6 +159,7 @@ QWebEnginePagePrivate::QWebEnginePagePrivate(QWebEngineProfile *_profile) , webChannelWorldId(QWebEngineScript::MainWorld) , defaultAudioMuted(false) , defaultZoomFactor(1.0) + , requestInterceptor(nullptr) #if QT_CONFIG(webengine_printing_and_pdf) , currentPrinter(nullptr) #endif @@ -260,6 +179,8 @@ QWebEnginePagePrivate::QWebEnginePagePrivate(QWebEngineProfile *_profile) QWebEnginePagePrivate::~QWebEnginePagePrivate() { + if (requestInterceptor) + profile->d_ptr->profileAdapter()->removePageRequestInterceptor(); delete history; delete settings; } @@ -499,19 +420,35 @@ void QWebEnginePagePrivate::didFindText(quint64 requestId, int matchCount) m_callbacks.invoke(requestId, matchCount > 0); } -void QWebEnginePagePrivate::didPrintPage(quint64 requestId, const QByteArray &result) +void QWebEnginePagePrivate::didPrintPage(quint64 requestId, QSharedPointer<QByteArray> result) { #if QT_CONFIG(webengine_printing_and_pdf) + Q_Q(QWebEnginePage); + // If no currentPrinter is set that means that were printing to PDF only. if (!currentPrinter) { - m_callbacks.invoke(requestId, result); + if (!result.data()) + return; + m_callbacks.invoke(requestId, *(result.data())); return; } - bool printerResult = printPdfDataOnPrinter(result, *currentPrinter); + QThread *printerThread = new QThread; + QObject::connect(printerThread, &QThread::finished, printerThread, &QThread::deleteLater); + printerThread->start(); + + PrinterWorker *printerWorker = new PrinterWorker(result, currentPrinter); + QObject::connect(printerWorker, &PrinterWorker::resultReady, q, [=](bool success) { + currentPrinter = nullptr; + m_callbacks.invoke(requestId, success); + }); + + QObject::connect(printerWorker, &PrinterWorker::resultReady, printerThread, &QThread::quit); + QObject::connect(printerThread, &QThread::finished, printerWorker, &PrinterWorker::deleteLater); + + printerWorker->moveToThread(printerThread); + QMetaObject::invokeMethod(printerWorker, "print"); - currentPrinter = nullptr; - m_callbacks.invoke(requestId, printerResult); #else // we should never enter this branch, but just for safe-keeping... Q_UNUSED(result); @@ -1850,6 +1787,40 @@ void QWebEnginePagePrivate::printRequested() }); } +/*! + \since 5.13 + + Registers the request interceptor \a interceptor to intercept URL requests. + + The page does not take ownership of the pointer. This interceptor is called + after any interceptors on the profile, and unlike profile interceptors, is run + on the UI thread, making it thread-safer. Only URL requests from this page are + intercepted. + + To unset the request interceptor, set a \c nullptr. + + \sa QWebEngineUrlRequestInfo, QWebEngineProfile::setRequestInterceptor() +*/ + +void QWebEnginePage::setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) +{ + Q_D(QWebEnginePage); + bool hadInterceptorChanged = bool(d->requestInterceptor) != bool(interceptor); + d->requestInterceptor = interceptor; + if (hadInterceptorChanged) { + if (interceptor) + d->profile->d_ptr->profileAdapter()->addPageRequestInterceptor(); + else + d->profile->d_ptr->profileAdapter()->removePageRequestInterceptor(); + } +} + +void QWebEnginePagePrivate::interceptRequest(QWebEngineUrlRequestInfo &info) +{ + if (requestInterceptor) + requestInterceptor->interceptRequest(info); +} + #if QT_CONFIG(menu) QMenu *QWebEnginePage::createStandardContextMenu() { @@ -2434,10 +2405,7 @@ void QWebEnginePage::printToPdf(const QWebEngineCallback<const QByteArray&> &res It is the users responsibility to ensure the \a printer remains valid until \a resultCallback has been called. - \note The rendering of the current content into a temporary PDF document is asynchronous and does - not block the main thread. However, the subsequent rendering of PDF into \a printer runs on the - main thread and will therefore block the event loop. Moreover, printing runs on the browser - process, which is by default not sandboxed. + \note Printing runs on the browser process, which is by default not sandboxed. 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. diff --git a/src/webenginewidgets/api/qwebenginepage.h b/src/webenginewidgets/api/qwebenginepage.h index 028f1a441..5eda8c72f 100644 --- a/src/webenginewidgets/api/qwebenginepage.h +++ b/src/webenginewidgets/api/qwebenginepage.h @@ -71,6 +71,7 @@ class QWebEngineQuotaRequest; class QWebEngineRegisterProtocolHandlerRequest; class QWebEngineScriptCollection; class QWebEngineSettings; +class QWebEngineUrlRequestInterceptor; class QWEBENGINEWIDGETS_EXPORT QWebEnginePage : public QObject { Q_OBJECT @@ -303,6 +304,8 @@ public: void setDevToolsPage(QWebEnginePage *page); QWebEnginePage *devToolsPage() const; + void setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor); + const QWebEngineContextMenuData &contextMenuData() const; Q_SIGNALS: diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h index eecbf0b65..e60438d97 100644 --- a/src/webenginewidgets/api/qwebenginepage_p.h +++ b/src/webenginewidgets/api/qwebenginepage_p.h @@ -66,6 +66,8 @@ namespace QtWebEngineCore { class RenderWidgetHostViewQtDelegate; class RenderWidgetHostViewQtDelegateWidget; +class TouchHandleDrawableClient; +class TouchSelectionMenuController; class WebContentsAdapter; } @@ -124,7 +126,7 @@ public: void didFetchDocumentMarkup(quint64 requestId, const QString& result) override; void didFetchDocumentInnerText(quint64 requestId, const QString& result) override; void didFindText(quint64 requestId, int matchCount) override; - void didPrintPage(quint64 requestId, const QByteArray &result) override; + void didPrintPage(quint64 requestId, QSharedPointer<QByteArray> result) override; void didPrintPageToPdf(const QString &filePath, bool success) override; void passOnFocus(bool reverse) override; void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString& message, int lineNumber, const QString& sourceID) override; @@ -149,8 +151,12 @@ public: bool isEnabled() const override; void setToolTip(const QString &toolTipText) override; void printRequested() override; + QtWebEngineCore::TouchHandleDrawableClient *createTouchHandle(const QMap<int, QImage> &) override { return nullptr; } + void showTouchSelectionMenu(QtWebEngineCore::TouchSelectionMenuController *, const QRect &, const QSize &) override { } + void hideTouchSelectionMenu() override { } const QObject *holdingQObject() const override; ClientType clientType() override { return QtWebEngineCore::WebContentsAdapterClient::WidgetsClient; } + void interceptRequest(QWebEngineUrlRequestInfo &) override; void widgetChanged(QtWebEngineCore::RenderWidgetHostViewQtDelegate *newWidget) override; QtWebEngineCore::ProfileAdapter *profileAdapter() override; @@ -193,6 +199,7 @@ public: bool defaultAudioMuted; qreal defaultZoomFactor; QTimer wasShownTimer; + QWebEngineUrlRequestInterceptor *requestInterceptor; QtWebEngineCore::RenderWidgetHostViewQtDelegateWidget *widget = nullptr; mutable QtWebEngineCore::CallbackDirectory m_callbacks; diff --git a/src/webenginewidgets/api/qwebengineprofile.cpp b/src/webenginewidgets/api/qwebengineprofile.cpp index 03ce5e0bc..7e80f9720 100644 --- a/src/webenginewidgets/api/qwebengineprofile.cpp +++ b/src/webenginewidgets/api/qwebengineprofile.cpp @@ -359,6 +359,34 @@ void QWebEngineProfile::setPersistentStoragePath(const QString &path) } /*! + \since 5.13 + + The path to the location where the downloaded files are stored. + + \note By default, the download path is QStandardPaths::DownloadLocation. + + \sa setDownloadPath(), QStandardPaths::writableLocation() +*/ +QString QWebEngineProfile::downloadPath() const +{ + const Q_D(QWebEngineProfile); + return d->profileAdapter()->downloadPath(); +} + +/*! + Overrides the default path used for download location, setting it to \a path. + + If set to the null string, the default path is restored. + + \sa downloadPath() +*/ +void QWebEngineProfile::setDownloadPath(const QString &path) +{ + Q_D(QWebEngineProfile); + d->profileAdapter()->setDownloadPath(path); +} + +/*! Returns the path used for caches. By default, this is below StandardPaths::CacheLocation in a QtWebengine/StorageName specific @@ -668,20 +696,7 @@ QWebEngineSettings *QWebEngineProfile::settings() const const QWebEngineUrlSchemeHandler *QWebEngineProfile::urlSchemeHandler(const QByteArray &scheme) const { const Q_D(QWebEngineProfile); - if (d->profileAdapter()->customUrlSchemeHandlers().contains(scheme)) - return d->profileAdapter()->customUrlSchemeHandlers().value(scheme); - return 0; -} - -static bool checkInternalScheme(const QByteArray &scheme) -{ - static QSet<QByteArray> internalSchemes; - if (internalSchemes.isEmpty()) { - internalSchemes << QByteArrayLiteral("qrc") << QByteArrayLiteral("data") << QByteArrayLiteral("blob") - << QByteArrayLiteral("http") << QByteArrayLiteral("https") << QByteArrayLiteral("ftp") - << QByteArrayLiteral("javascript"); - } - return internalSchemes.contains(scheme); + return d->profileAdapter()->urlSchemeHandler(scheme); } /*! @@ -695,25 +710,7 @@ static bool checkInternalScheme(const QByteArray &scheme) void QWebEngineProfile::installUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler) { Q_D(QWebEngineProfile); - Q_ASSERT(handler); - QByteArray canonicalScheme = scheme.toLower(); - if (checkInternalScheme(canonicalScheme)) { - qWarning("Cannot install a URL scheme handler overriding internal scheme: %s", scheme.constData()); - return; - } - - if (d->profileAdapter()->customUrlSchemeHandlers().contains(canonicalScheme)) { - if (d->profileAdapter()->customUrlSchemeHandlers().value(canonicalScheme) != handler) - qWarning("URL scheme handler already installed for the scheme: %s", scheme.constData()); - return; - } - - if (QWebEngineUrlScheme::schemeByName(canonicalScheme) == QWebEngineUrlScheme()) - qWarning("Please register the custom scheme '%s' via QWebEngineUrlScheme::registerScheme() " - "before installing the custom scheme handler.", scheme.constData()); - - d->profileAdapter()->addCustomUrlSchemeHandler(canonicalScheme, handler); - connect(handler, SIGNAL(_q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)), this, SLOT(destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*))); + d->profileAdapter()->installUrlSchemeHandler(scheme, handler); } /*! @@ -726,10 +723,7 @@ void QWebEngineProfile::installUrlSchemeHandler(const QByteArray &scheme, QWebEn void QWebEngineProfile::removeUrlSchemeHandler(QWebEngineUrlSchemeHandler *handler) { Q_D(QWebEngineProfile); - Q_ASSERT(handler); - if (!d->profileAdapter()->removeCustomUrlSchemeHandler(handler)) - return; - disconnect(handler, SIGNAL(_q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)), this, SLOT(destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*))); + d->profileAdapter()->removeUrlSchemeHandler(handler); } /*! @@ -742,10 +736,7 @@ void QWebEngineProfile::removeUrlSchemeHandler(QWebEngineUrlSchemeHandler *handl void QWebEngineProfile::removeUrlScheme(const QByteArray &scheme) { Q_D(QWebEngineProfile); - QWebEngineUrlSchemeHandler *handler = d->profileAdapter()->takeCustomUrlSchemeHandler(scheme); - if (!handler) - return; - disconnect(handler, SIGNAL(_q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)), this, SLOT(destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*))); + d->profileAdapter()->removeUrlScheme(scheme); } /*! @@ -756,12 +747,42 @@ void QWebEngineProfile::removeUrlScheme(const QByteArray &scheme) void QWebEngineProfile::removeAllUrlSchemeHandlers() { Q_D(QWebEngineProfile); - d->profileAdapter()->clearCustomUrlSchemeHandlers(); + d->profileAdapter()->removeAllUrlSchemeHandlers(); } -void QWebEngineProfile::destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler *obj) +/*! + \since 5.13 + + Sets this profile to be used for downloading and caching when needed during + certificate verification, for instance for OCSP, CRLs, and AIA. + + Only one QWebEngineProfile can do this at a time, and it is recommended + that the profile fullfilling this role has a disk HTTP cache to avoid + needlessly re-downloading. + + Currently only affects Linux/NSS installations where it enables OCSP. + + As long as one profile has this option enabled, all other profiles will be + able to use it for their certificate verification. + + \sa isUsedForGlobalCertificateVerification(), httpCacheType() +*/ +void QWebEngineProfile::setUseForGlobalCertificateVerification() { - removeUrlSchemeHandler(obj); + Q_D(QWebEngineProfile); + d->profileAdapter()->setUseForGlobalCertificateVerification(); +} + +/*! + \since 5.13 + + Returns \c true if this profile is currently being used for global + certificate verification. +*/ +bool QWebEngineProfile::isUsedForGlobalCertificateVerification() const +{ + Q_D(const QWebEngineProfile); + return d->profileAdapter()->isUsedForGlobalCertificateVerification(); } /*! diff --git a/src/webenginewidgets/api/qwebengineprofile.h b/src/webenginewidgets/api/qwebengineprofile.h index f9a564cd2..9fc509851 100644 --- a/src/webenginewidgets/api/qwebengineprofile.h +++ b/src/webenginewidgets/api/qwebengineprofile.h @@ -128,19 +128,23 @@ public: void setSpellCheckEnabled(bool enabled); bool isSpellCheckEnabled() const; + void setUseForGlobalCertificateVerification(); + bool isUsedForGlobalCertificateVerification() const; + + QString downloadPath() const; + void setDownloadPath(const QString &path); + static QWebEngineProfile *defaultProfile(); Q_SIGNALS: void downloadRequested(QWebEngineDownloadItem *download); -private Q_SLOTS: - void destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler *obj); - private: Q_DISABLE_COPY(QWebEngineProfile) Q_DECLARE_PRIVATE(QWebEngineProfile) QWebEngineProfile(QWebEngineProfilePrivate *, QObject *parent = Q_NULLPTR); + friend class QWebEnginePage; friend class QWebEnginePagePrivate; friend class QWebEngineUrlSchemeHandler; QScopedPointer<QWebEngineProfilePrivate> d_ptr; diff --git a/src/webenginewidgets/printer_worker.cpp b/src/webenginewidgets/printer_worker.cpp new file mode 100644 index 000000000..3a670ad49 --- /dev/null +++ b/src/webenginewidgets/printer_worker.cpp @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** 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 "printer_worker.h" + +#include "printing/pdfium_document_wrapper_qt.h" + +#include <QPainter> +#include <QPrinter> + +namespace QtWebEngineCore { + +PrinterWorker::PrinterWorker(QSharedPointer<QByteArray> data, QPrinter *printer) + : m_data(data) + , m_printer(printer) +{ +} + +PrinterWorker::~PrinterWorker() +{ +} + +void PrinterWorker::print() +{ + if (!m_data->size()) { + qWarning("Failure to print on printer %ls: Print result data is empty.", + qUtf16Printable(m_printer->printerName())); + Q_EMIT resultReady(false); + return; + } + + QSize pageSize = m_printer->pageRect().size(); + PdfiumDocumentWrapperQt pdfiumWrapper(m_data->constData(), m_data->size(), pageSize); + + int toPage = m_printer->toPage(); + int fromPage = m_printer->fromPage(); + bool ascendingOrder = true; + + if (fromPage == 0 && toPage == 0) { + fromPage = 1; + toPage = pdfiumWrapper.pageCount(); + } + fromPage = qMax(1, fromPage); + toPage = qMin(pdfiumWrapper.pageCount(), toPage); + + if (m_printer->pageOrder() == QPrinter::LastPageFirst) { + qSwap(fromPage, toPage); + ascendingOrder = false; + } + + int pageCopies = 1; + int documentCopies = 1; + + if (!m_printer->supportsMultipleCopies()) + documentCopies = m_printer->copyCount(); + + if (m_printer->collateCopies()) { + pageCopies = documentCopies; + documentCopies = 1; + } + + QPainter painter; + if (!painter.begin(m_printer)) { + qWarning("Failure to print on printer %ls: Could not open printer for painting.", + qUtf16Printable(m_printer->printerName())); + Q_EMIT resultReady(false); + return; + } + + for (int printedDocuments = 0; printedDocuments < documentCopies; printedDocuments++) { + int currentPageIndex = fromPage; + while (true) { + for (int printedPages = 0; printedPages < pageCopies; printedPages++) { + if (m_printer->printerState() == QPrinter::Aborted + || m_printer->printerState() == QPrinter::Error) { + Q_EMIT resultReady(false); + return; + } + + QImage currentImage = pdfiumWrapper.pageAsQImage(currentPageIndex - 1); + if (currentImage.isNull()) { + Q_EMIT resultReady(false); + return; + } + + // Painting operations are automatically clipped to the bounds of the drawable part of the page. + painter.drawImage(QRect(0, 0, pageSize.width(), pageSize.height()), currentImage, currentImage.rect()); + if (printedPages < pageCopies - 1) + m_printer->newPage(); + } + + if (currentPageIndex == toPage) + break; + + if (ascendingOrder) + currentPageIndex++; + else + currentPageIndex--; + + m_printer->newPage(); + } + if (printedDocuments < documentCopies - 1) + m_printer->newPage(); + } + painter.end(); + + Q_EMIT resultReady(true); + return; +} + +} // namespace QtWebEngineCore diff --git a/src/webenginewidgets/printer_worker.h b/src/webenginewidgets/printer_worker.h new file mode 100644 index 000000000..96025c90e --- /dev/null +++ b/src/webenginewidgets/printer_worker.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +// +// 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. +// + +#ifndef PRINTER_WORKER_H +#define PRINTER_WORKER_H + +#include "qtwebenginecoreglobal_p.h" + +#include <QSharedPointer> + +QT_BEGIN_NAMESPACE +class QPrinter; +QT_END_NAMESPACE + +namespace QtWebEngineCore { + +class PrinterWorker : public QObject +{ + Q_OBJECT +public: + PrinterWorker(QSharedPointer<QByteArray> data, QPrinter *printer); + virtual ~PrinterWorker(); + +public Q_SLOTS: + void print(); + +Q_SIGNALS: + void resultReady(bool success); + +private: + Q_DISABLE_COPY(PrinterWorker) + + QSharedPointer<QByteArray> m_data; + QPrinter *m_printer; +}; + +} // namespace QtWebEngineCore + +Q_DECLARE_METATYPE(QtWebEngineCore::PrinterWorker*) + +#endif // PRINTER_WORKER_H 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 900ed3324..5b464a461 100644 --- a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp +++ b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp @@ -132,25 +132,31 @@ RenderWidgetHostViewQtDelegateWidget::RenderWidgetHostViewQtDelegateWidget(Rende "QSurfaceFormat before the QtGui application instance is created."); } #endif - - // Make sure the OpenGL profile of the QQuickWidget matches the shared context profile. - if (sharedFormat.profile() == QSurfaceFormat::CoreProfile) { - int major; - int minor; - QSurfaceFormat::OpenGLContextProfile profile; - + int major; + int minor; + QSurfaceFormat::OpenGLContextProfile profile; #ifdef Q_OS_MACOS - // Due to QTBUG-63180, requesting the sharedFormat.majorVersion() on macOS will lead to - // a failed creation of QQuickWidget shared context. Thus make sure to request the - // major version specified in the defaultFormat instead. - major = defaultFormat.majorVersion(); - minor = defaultFormat.minorVersion(); - profile = defaultFormat.profile(); + // Due to QTBUG-63180, requesting the sharedFormat.majorVersion() on macOS will lead to + // a failed creation of QQuickWidget shared context. Thus make sure to request the + // major version specified in the defaultFormat instead. + major = defaultFormat.majorVersion(); + minor = defaultFormat.minorVersion(); + profile = defaultFormat.profile(); #else - major = sharedFormat.majorVersion(); - minor = sharedFormat.minorVersion(); - profile = sharedFormat.profile(); + major = sharedFormat.majorVersion(); + minor = sharedFormat.minorVersion(); + profile = sharedFormat.profile(); +#endif + + // Make sure the OpenGL profile of the QQuickWidget matches the shared context profile. + // It covers the following cases: + // 1) Desktop OpenGL Core Profile. + // 2) Windows ANGLE OpenGL ES profile. + if (sharedFormat.profile() == QSurfaceFormat::CoreProfile +#ifdef Q_OS_WIN + || globalSharedContext->isOpenGLES() #endif + ) { format.setMajorVersion(major); format.setMinorVersion(minor); format.setProfile(profile); diff --git a/src/webenginewidgets/webenginewidgets.pro b/src/webenginewidgets/webenginewidgets.pro index e61575d3a..4669c2bce 100644 --- a/src/webenginewidgets/webenginewidgets.pro +++ b/src/webenginewidgets/webenginewidgets.pro @@ -49,6 +49,9 @@ HEADERS = \ qtConfig(webengine-printing-and-pdf) { QT += printsupport + + SOURCES += printer_worker.cpp + HEADERS += printer_worker.h } load(qt_module) diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp index 23bf88417..7b7fec6f4 100644 --- a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp +++ b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp @@ -51,7 +51,9 @@ private Q_SLOTS: void cleanupTestCase(); void interceptRequest(); void ipv6HostEncoding(); + void requestedUrl_data(); void requestedUrl(); + void setUrlSameUrl_data(); void setUrlSameUrl(); void firstPartyUrl(); void firstPartyUrlNestedIframes_data(); @@ -123,10 +125,7 @@ public: // Skip import documents and sandboxed documents. // See Document::SiteForCookies() in chromium/third_party/blink/renderer/core/dom/document.cc. - // - // TODO: Change this to empty URL during the next chromium update: - // https://chromium-review.googlesource.com/c/chromium/src/+/1213082/ - return requestInfo.firstPartyUrl == QUrl("data:,"); + return requestInfo.firstPartyUrl == QUrl(""); } QList<RequestInfo> getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceType type) @@ -249,14 +248,27 @@ void tst_QWebEngineUrlRequestInterceptor::ipv6HostEncoding() QCOMPARE(contentProvider.requestedUrls.at(0), QUrl::fromEncoded("http://[::1]/test.xml")); } +void tst_QWebEngineUrlRequestInterceptor::requestedUrl_data() +{ + QTest::addColumn<bool>("interceptInPage"); + + QTest::newRow("Profile intercept") << false; + QTest::newRow("Page intercept") << true; +} + void tst_QWebEngineUrlRequestInterceptor::requestedUrl() { + QFETCH(bool, interceptInPage); + QWebEngineProfile profile; profile.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); TestRequestInterceptor interceptor(/* intercept */ true); - profile.setRequestInterceptor(&interceptor); + if (!interceptInPage) + profile.setRequestInterceptor(&interceptor); QWebEnginePage page(&profile); + if (interceptInPage) + page.setRequestInterceptor(&interceptor); QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); page.setUrl(QUrl("qrc:///resources/__placeholder__")); @@ -273,19 +285,29 @@ void tst_QWebEngineUrlRequestInterceptor::requestedUrl() QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); page.setUrl(QUrl("http://abcdef.abcdef")); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 3, 12000); + QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 3, 15000); QCOMPARE(interceptor.requestInfos.at(3).requestUrl, QUrl("http://abcdef.abcdef/")); QCOMPARE(page.requestedUrl(), QUrl("qrc:///resources/__placeholder__")); QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); } +void tst_QWebEngineUrlRequestInterceptor::setUrlSameUrl_data() +{ + requestedUrl_data(); +} + void tst_QWebEngineUrlRequestInterceptor::setUrlSameUrl() { + QFETCH(bool, interceptInPage); + QWebEngineProfile profile; TestRequestInterceptor interceptor(/* intercept */ true); - profile.setRequestInterceptor(&interceptor); + if (!interceptInPage) + profile.setRequestInterceptor(&interceptor); QWebEnginePage page(&profile); + if (interceptInPage) + page.setRequestInterceptor(&interceptor); QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); page.setUrl(QUrl("qrc:///resources/__placeholder__")); diff --git a/tests/auto/quick/dialogs/dialogs.pro b/tests/auto/quick/dialogs/dialogs.pro index e262c3814..29d509b20 100644 --- a/tests/auto/quick/dialogs/dialogs.pro +++ b/tests/auto/quick/dialogs/dialogs.pro @@ -1,5 +1,5 @@ include(../tests.pri) -QT += webengine webengine-private +QT += core-private webengine webengine-private HEADERS += \ server.h \ diff --git a/tests/auto/quick/dialogs/tst_dialogs.cpp b/tests/auto/quick/dialogs/tst_dialogs.cpp index ecc2764fd..cecea1831 100644 --- a/tests/auto/quick/dialogs/tst_dialogs.cpp +++ b/tests/auto/quick/dialogs/tst_dialogs.cpp @@ -26,7 +26,6 @@ ** ****************************************************************************/ -#include "qtwebengineglobal.h" #include "testhandler.h" #include "server.h" #include <QtWebEngine/private/qquickwebenginedialogrequests_p.h> diff --git a/tests/auto/quick/publicapi/tst_publicapi.cpp b/tests/auto/quick/publicapi/tst_publicapi.cpp index 0e48e280d..4cbadfdaa 100644 --- a/tests/auto/quick/publicapi/tst_publicapi.cpp +++ b/tests/auto/quick/publicapi/tst_publicapi.cpp @@ -40,6 +40,7 @@ #include <private/qquickwebengineview_p.h> #include <private/qquickwebengineaction_p.h> #include <private/qquickwebenginecertificateerror_p.h> +#include <private/qquickwebengineclientcertificateselection_p.h> #include <private/qquickwebenginedialogrequests_p.h> #include <private/qquickwebenginedownloaditem_p.h> #include <private/qquickwebenginehistory_p.h> @@ -60,6 +61,8 @@ static const QList<const QMetaObject *> typesToCheck = QList<const QMetaObject * << &QQuickWebEngineView::staticMetaObject << &QQuickWebEngineAction::staticMetaObject << &QQuickWebEngineCertificateError::staticMetaObject + << &QQuickWebEngineClientCertificateOption::staticMetaObject + << &QQuickWebEngineClientCertificateSelection::staticMetaObject << &QQuickWebEngineDownloadItem::staticMetaObject << &QQuickWebEngineHistory::staticMetaObject << &QQuickWebEngineHistoryListModel::staticMetaObject @@ -86,6 +89,8 @@ static QList<const char *> knownEnumNames = QList<const char *>(); static const QStringList hardcodedTypes = QStringList() << "QJSValue" << "QQmlListProperty<QQuickWebEngineScript>" + << "QQmlListProperty<QQuickWebEngineClientCertificateOption>" + << "const QQuickWebEngineClientCertificateOption*" << "QQmlWebChannel*" // Ignore the testSupport types without making a fuss. << "QQuickWebEngineTestSupport*" diff --git a/tests/auto/quick/qmltests/data/tst_download.qml b/tests/auto/quick/qmltests/data/tst_download.qml index 019ebd9dc..5eb704cce 100644 --- a/tests/auto/quick/qmltests/data/tst_download.qml +++ b/tests/auto/quick/qmltests/data/tst_download.qml @@ -28,7 +28,8 @@ import QtQuick 2.0 import QtTest 1.0 -import QtWebEngine 1.5 +import QtWebEngine 1.9 +import Qt.labs.platform 1.0 TestWebEngineView { id: webEngineView @@ -42,6 +43,12 @@ TestWebEngineView { property var downloadState: [] property var downloadInterruptReason: null + function urlToPath(url) { + var path = url.toString() + path = path.replace(/^(file:\/{2})/,"") + return path + } + SignalSpy { id: downLoadRequestedSpy target: testDownloadProfile @@ -135,5 +142,16 @@ TestWebEngineView { tryCompare(downloadState, "1", WebEngineDownloadItem.DownloadCancelled) tryCompare(webEngineView, "downloadInterruptReason", WebEngineDownloadItem.UserCanceled) } + + function test_downloadLocation() { + var tmpPath = urlToPath(StandardPaths.writableLocation(StandardPaths.TempLocation)); + var downloadPath = urlToPath(StandardPaths.writableLocation(StandardPaths.DownloadLocation)); + + testDownloadProfile.downloadPath = tmpPath; + compare(testDownloadProfile.downloadPath, tmpPath); + + testDownloadProfile.downloadPath = downloadPath; + compare(testDownloadProfile.downloadPath, downloadPath); + } } } diff --git a/tests/auto/quick/qmltests/data/tst_findText.qml b/tests/auto/quick/qmltests/data/tst_findText.qml index dfcfd586f..1ec574fae 100644 --- a/tests/auto/quick/qmltests/data/tst_findText.qml +++ b/tests/auto/quick/qmltests/data/tst_findText.qml @@ -116,7 +116,7 @@ TestWebEngineView { webEngineView.clear() webEngineView.findText("bla", findFlags, webEngineView.findTextCallback) - tryCompare(webEngineView, "matchCount", 100) + tryCompare(webEngineView, "matchCount", 100, 20000) verify(!findFailed) } @@ -172,7 +172,7 @@ TestWebEngineView { webEngineView.clear() webEngineView.findText("hello", findFlags, webEngineView.findTextCallback) - tryCompare(webEngineView, "matchCount", 0) + tryCompare(webEngineView, "matchCount", 0, 20000) verify(findFailed) runJavaScript("document.body.innerHTML = 'blahellobla'"); diff --git a/tests/auto/quick/qmltests/data/tst_profile.qml b/tests/auto/quick/qmltests/data/tst_profile.qml new file mode 100644 index 000000000..ee7fa4e99 --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_profile.qml @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtWebEngine 1.9 + +TestWebEngineView { + id: webEngineView + width: 400 + height: 300 + + + WebEngineProfile { + id: profile1 + } + WebEngineProfile { + id: profile2 + } + property bool profile1UsedForGlobalCertificateVerification: profile1.useForGlobalCertificateVerification + + TestCase { + name: "WebEngineProfile" + + function test_useForGlobalCertificateVerification() { + verify(!profile1.useForGlobalCertificateVerification); + verify(!profile2.useForGlobalCertificateVerification); + verify(!webEngineView.profile1UsedForGlobalCertificateVerification); + + profile1.useForGlobalCertificateVerification = true; + verify(profile1.useForGlobalCertificateVerification); + verify(!profile2.useForGlobalCertificateVerification); + verify(webEngineView.profile1UsedForGlobalCertificateVerification); + + profile2.useForGlobalCertificateVerification = true; + verify(!webEngineView.profile1UsedForGlobalCertificateVerification); + verify(!profile1.useForGlobalCertificateVerification); + verify(profile2.useForGlobalCertificateVerification); + } + } +} diff --git a/tests/auto/quick/qmltests/qmltests.pro b/tests/auto/quick/qmltests/qmltests.pro index a2b05e091..ad479cd31 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_profile.qml \ $$PWD/data/tst_properties.qml \ $$PWD/data/tst_runJavaScript.qml \ $$PWD/data/tst_scrollPosition.qml \ diff --git a/tests/auto/quick/qquickwebengineview/BLACKLIST b/tests/auto/quick/qquickwebengineview/BLACKLIST index 76cb18c1e..5bb38576a 100644 --- a/tests/auto/quick/qquickwebengineview/BLACKLIST +++ b/tests/auto/quick/qquickwebengineview/BLACKLIST @@ -9,8 +9,13 @@ windows [basicRenderingSanity] * + [javascriptClipboard:default] opensuse-leap + [javascriptClipboard:canPaste] opensuse-leap +[changeLocale] +* + diff --git a/tests/auto/quick/qquickwebengineview/qquickwebengineview.pro b/tests/auto/quick/qquickwebengineview/qquickwebengineview.pro index c253bc2a6..38c130aa3 100644 --- a/tests/auto/quick/qquickwebengineview/qquickwebengineview.pro +++ b/tests/auto/quick/qquickwebengineview/qquickwebengineview.pro @@ -1,6 +1,6 @@ include(../tests.pri) exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc -QT_PRIVATE += webengine-private gui-private webenginecore-private +QT_PRIVATE += core_private gui-private webengine-private webenginecore-private HEADERS += ../shared/util.h diff --git a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp index 007acb8b0..8947bba9d 100644 --- a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp +++ b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp @@ -31,6 +31,7 @@ #include <QScopedPointer> #include <QtCore/qelapsedtimer.h> +#include <QtCore/qregularexpression.h> #include <QtGui/qclipboard.h> #include <QtGui/qguiapplication.h> #include <QtGui/qpa/qwindowsysteminterface.h> @@ -40,6 +41,7 @@ #include <QtGui/private/qinputmethod_p.h> #include <QtWebEngine/private/qquickwebengineview_p.h> #include <QtWebEngine/private/qquickwebenginesettings_p.h> +#include <QtWebEngineCore/private/qtwebenginecore-config_p.h> #include <qpa/qplatforminputcontext.h> #include <functional> @@ -1000,8 +1002,8 @@ void tst_QQuickWebEngineView::changeLocale() QVERIFY(waitForLoadFailed(viewDE.data())); QTRY_VERIFY(!evaluateJavaScriptSync(viewDE.data(), "document.body.innerText").isNull()); - errorLines = evaluateJavaScriptSync(viewDE.data(), "document.body.innerText").toString().split(QRegExp("[\r\n]"), QString::SkipEmptyParts); - QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("Die Website ist nicht erreichbar")); + 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.")); QLocale::setDefault(QLocale("en")); QScopedPointer<QQuickWebEngineView> viewEN(newWebEngineView()); @@ -1009,7 +1011,7 @@ void tst_QQuickWebEngineView::changeLocale() QVERIFY(waitForLoadFailed(viewEN.data())); QTRY_VERIFY(!evaluateJavaScriptSync(viewEN.data(), "document.body.innerText").isNull()); - errorLines = evaluateJavaScriptSync(viewEN.data(), "document.body.innerText").toString().split(QRegExp("[\r\n]"), QString::SkipEmptyParts); + 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")); // Reset error page @@ -1021,7 +1023,7 @@ void tst_QQuickWebEngineView::changeLocale() QVERIFY(waitForLoadFailed(viewDE.data())); QTRY_VERIFY(!evaluateJavaScriptSync(viewDE.data(), "document.body.innerText").isNull()); - errorLines = evaluateJavaScriptSync(viewDE.data(), "document.body.innerText").toString().split(QRegExp("[\r\n]"), QString::SkipEmptyParts); + errorLines = evaluateJavaScriptSync(viewDE.data(), "document.body.innerText").toString().split(QRegularExpression("[\r\n]"), QString::SkipEmptyParts); QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("Die Website ist nicht erreichbar")); } diff --git a/tests/auto/quick/quick.pro b/tests/auto/quick/quick.pro index 50a6a8587..46dd57f76 100644 --- a/tests/auto/quick/quick.pro +++ b/tests/auto/quick/quick.pro @@ -1,5 +1,5 @@ -include($$QTWEBENGINE_OUT_ROOT/src/core/qtwebenginecore-config.pri) # workaround for QTBUG-68093 -QT_FOR_CONFIG += webenginecore-private +include($$QTWEBENGINE_OUT_ROOT/src/webengine/qtwebengine-config.pri) # workaround for QTBUG-68093 +QT_FOR_CONFIG += webengine-private TEMPLATE = subdirs diff --git a/tests/auto/quick/tests.pri b/tests/auto/quick/tests.pri index f7104ad9c..1bf69da43 100644 --- a/tests/auto/quick/tests.pri +++ b/tests/auto/quick/tests.pri @@ -1,5 +1,5 @@ -include($$QTWEBENGINE_OUT_ROOT/src/core/qtwebenginecore-config.pri) # workaround for QTBUG-68093 -QT_FOR_CONFIG += webenginecore-private +include($$QTWEBENGINE_OUT_ROOT/src/webengine/qtwebengine-config.pri) # workaround for QTBUG-68093 +QT_FOR_CONFIG += webengine-private TEMPLATE = app diff --git a/tests/auto/widgets/origins/tst_origins.cpp b/tests/auto/widgets/origins/tst_origins.cpp index 4e415af90..61c7232b0 100644 --- a/tests/auto/widgets/origins/tst_origins.cpp +++ b/tests/auto/widgets/origins/tst_origins.cpp @@ -301,7 +301,7 @@ void tst_Origins::jsUrlOrigin() QCOMPARE(eval(QSL("new URL(\"qrc:/crysis.css\").origin")), QVariant(QSL("qrc://"))); QCOMPARE(eval(QSL("new URL(\"qrc://foo.com/crysis.css\").origin")), QVariant(QSL("qrc://"))); - // Same with unregistered schemes. + // Unregistered schemes behaves like opaque origins. QCOMPARE(eval(QSL("new URL(\"tst:/banana\").origin")), QVariant(QSL("tst://"))); QCOMPARE(eval(QSL("new URL(\"tst://foo.com/banana\").origin")), QVariant(QSL("tst://"))); @@ -564,8 +564,6 @@ private: // Try opening a WebSocket from pages loaded over various URL schemes. void tst_Origins::webSocket() { - const int kAbnormalClosure = 1006; - EchoServer echoServer; QWebChannel channel; channel.registerObject(QSL("echoServer"), &echoServer); @@ -578,9 +576,9 @@ void tst_Origins::webSocket() QVERIFY(load(QSL("qrc:/resources/websocket.html"))); QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("ok"))); - // Only registered schemes can open WebSockets. + // Unregistered schemes can also open WebSockets (since Chromium 71) QVERIFY(load(QSL("tst:/resources/websocket.html"))); - QTRY_COMPARE(eval(QSL("result")), QVariant(kAbnormalClosure)); + QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("ok"))); // Even an insecure registered scheme can open WebSockets. QVERIFY(load(QSL("PathSyntax:/resources/websocket.html"))); @@ -599,11 +597,10 @@ void tst_Origins::dedicatedWorker() QTRY_VERIFY(eval(QSL("done")).toBool()); QCOMPARE(eval(QSL("result")), QVariant(42)); - // Unregistered schemes cannot create Workers. + // Unregistered schemes can also create Workers (since Chromium 71) QVERIFY(load(QSL("tst:/resources/dedicatedWorker.html"))); QTRY_VERIFY(eval(QSL("done")).toBool()); - QVERIFY(eval(QSL("error")).toString() - .contains(QSL("Access to dedicated workers is denied to origin 'tst://'"))); + QCOMPARE(eval(QSL("result")), QVariant(42)); // Even an insecure registered scheme can create Workers. QVERIFY(load(QSL("PathSyntax:/resources/dedicatedWorker.html"))); @@ -727,15 +724,9 @@ void tst_Origins::createObjectURL() QVERIFY(load(QSL("qrc:/resources/createObjectURL.html"))); QVERIFY(eval(QSL("result")).toString().startsWith(QSL("blob:qrc:"))); - // Illegal for unregistered schemes (renderer gets terminated). - qRegisterMetaType<QWebEnginePage::RenderProcessTerminationStatus>("RenderProcessTerminationStatus"); - QSignalSpy loadFinishedSpy(m_page, &QWebEnginePage::loadFinished); - QSignalSpy renderProcessTerminatedSpy(m_page, &QWebEnginePage::renderProcessTerminated); - m_page->load(QSL("tst:/resources/createObjectURL.html")); - QVERIFY(!renderProcessTerminatedSpy.empty() || renderProcessTerminatedSpy.wait(20000)); - QVERIFY(renderProcessTerminatedSpy.front().value(0).value<QWebEnginePage::RenderProcessTerminationStatus>() - != QWebEnginePage::NormalTerminationStatus); - QVERIFY(loadFinishedSpy.empty()); + // Also legal for unregistered schemes (since Chromium 71) + QVERIFY(load(QSL("tst:/resources/createObjectURL.html"))); + QVERIFY(eval(QSL("result")).toString().startsWith(QSL("blob:tst:"))); } QTEST_MAIN(tst_Origins) diff --git a/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp b/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp index 2af818928..b50036aa8 100644 --- a/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp +++ b/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp @@ -31,6 +31,7 @@ #include <QStandardPaths> #include <QTemporaryDir> #include <QTest> +#include <QRegularExpression> #include <QWebEngineDownloadItem> #include <QWebEnginePage> #include <QWebEngineProfile> @@ -69,6 +70,12 @@ private Q_SLOTS: void downloadFileNot1(); void downloadFileNot2(); void downloadDeleted(); + void downloadUniqueFilename_data(); + void downloadUniqueFilename(); + void downloadUniqueFileNameWithTimeStamp(); + void downloadToDefaultLocation(); + void downloadToNonExistentDir(); + void downloadToReadOnlyDir(); private: void saveLink(QPoint linkPos); @@ -134,6 +141,8 @@ void tst_QWebEngineDownloadItem::cleanup() QTRY_COMPARE(m_requestedDownloads.count(), 0); QCOMPARE(m_finishedDownloads.count(), 0); QVERIFY(m_server->stop()); + // Set download path to default. + m_profile->setDownloadPath(""); } void tst_QWebEngineDownloadItem::cleanupTestCase() @@ -611,6 +620,7 @@ void tst_QWebEngineDownloadItem::downloadPage_data() void tst_QWebEngineDownloadItem::downloadPage() { + QSKIP("Unstable since Chromium 71"); QFETCH(QWebEngineDownloadItem::SavePageFormat, savePageFormat); // Set up HTTP server @@ -812,5 +822,264 @@ void tst_QWebEngineDownloadItem::downloadDeleted() QTRY_COMPARE(finishedCount, 1); } +void tst_QWebEngineDownloadItem::downloadUniqueFilename_data() +{ + QTest::addColumn<QString>("baseName"); + QTest::addColumn<QString>("extension"); + + QTest::newRow("txt") << QString("test(1.test)") << QString("txt"); + QTest::newRow("tar.gz") << QString("test(1.test)") << QString("tar.gz"); +} + +void tst_QWebEngineDownloadItem::downloadUniqueFilename() +{ + QFETCH(QString, baseName); + QFETCH(QString, extension); + QString fileName = QString("%1.%2").arg(baseName).arg(extension); + QString downloadedFilePath; + bool downloadFinished = false; + + QTemporaryDir tmpDir; + QVERIFY(tmpDir.isValid()); + m_profile->setDownloadPath(tmpDir.path()); + + // Set up HTTP server + ScopedConnection sc1 = connect(m_server, &HttpServer::newRequest, [&](HttpReqRep *rr) { + if (rr->requestMethod() == "GET" && rr->requestPath() == ("/" + fileName)) { + rr->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("application/octet-stream")); + rr->setResponseHeader(QByteArrayLiteral("content-disposition"), QByteArrayLiteral("attachment")); + rr->setResponseBody(QByteArrayLiteral("a")); + rr->sendResponse(); + } else { + rr->setResponseStatus(404); + rr->sendResponse(); + } + }); + + // Set up profile and download handler + ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadItem *item) { + item->accept(); + connect(item, &QWebEngineDownloadItem::finished, [&, item]() { + QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadCompleted); + QCOMPARE(item->isFinished(), true); + QCOMPARE(item->totalBytes(), item->receivedBytes()); + QVERIFY(item->receivedBytes() > 0); + QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason); + QCOMPARE(item->type(), QWebEngineDownloadItem::Attachment); + QCOMPARE(item->isSavePageDownload(), false); + downloadedFilePath = item->path(); + downloadFinished = true; + }); + }); + + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadFinished); + QVERIFY(QFile(downloadedFilePath).exists()); + QCOMPARE(downloadedFilePath, m_profile->downloadPath() + "/" + baseName + "." + extension); + + for (int i = 1; i <= 2; ++i) { + downloadFinished = false; + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadFinished); + QVERIFY(QFile(downloadedFilePath).exists()); + QCOMPARE(downloadedFilePath, m_profile->downloadPath() + "/" + baseName + " (" + QString::number(i) + ")." + extension); + } +} + +void tst_QWebEngineDownloadItem::downloadUniqueFileNameWithTimeStamp() +{ + // Set up HTTP server + QString baseName("test(1.test)"); + QString extension("txt"); + QString fileName = QString("%1.%2").arg(baseName).arg(extension); + QString downloadedFilePath; + bool downloadFinished = false; + + QTemporaryDir tmpDir; + QVERIFY(tmpDir.isValid()); + m_profile->setDownloadPath(tmpDir.path()); + + ScopedConnection sc1 = connect(m_server, &HttpServer::newRequest, [&](HttpReqRep *rr) { + if (rr->requestMethod() == "GET" && rr->requestPath() == ("/" + fileName)) { + rr->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("application/octet-stream")); + rr->setResponseHeader(QByteArrayLiteral("content-disposition"), QByteArrayLiteral("attachment")); + rr->setResponseBody(QByteArrayLiteral("a")); + rr->sendResponse(); + } else { + rr->setResponseStatus(404); + rr->sendResponse(); + } + }); + + // Set up profile and download handler + ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadItem *item) { + item->accept(); + connect(item, &QWebEngineDownloadItem::finished, [&, item]() { + QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadCompleted); + QCOMPARE(item->isFinished(), true); + QCOMPARE(item->totalBytes(), item->receivedBytes()); + QVERIFY(item->receivedBytes() > 0); + QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason); + QCOMPARE(item->page(), m_page); + downloadFinished = true; + downloadedFilePath = item->path(); + }); + }); + + // Create the first empty file without uniquifier. + { + QFile file(m_profile->downloadPath() + "/" + fileName); + file.open(QIODevice::ReadWrite); + } + + // Create 99 empty files with uniquifier. + for (int i = 1; i < 100; i++) { + QFile file(m_profile->downloadPath() + "/" + baseName + " (" + QString::number(i) + ")." + extension); + file.open(QIODevice::ReadWrite); + } + + // Create 100th (kMaxUniqueFiles) empty file with uniquifier. + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadFinished); + QVERIFY(QFile(downloadedFilePath).exists()); + QCOMPARE(downloadedFilePath, m_profile->downloadPath() + "/" + baseName + " (100)." + extension); + + // Check if the downloaded files are suffixed with timestamp after the 100th download. + for (int i = 101; i < 103; i++) { + downloadFinished = false; + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadFinished); + QVERIFY(QFile(downloadedFilePath).exists()); + QRegularExpression fileNameCheck("^.*" + QRegularExpression::escape(baseName) + " - (.*)[.]" + QRegularExpression::escape(extension) + "$"); + QRegularExpressionMatch match = fileNameCheck.match(downloadedFilePath); + QVERIFY(match.hasMatch()); + // ISO 8601 Date and time in UTC + QRegExp timeStamp("^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])([.][0-9]+)?(Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$"); + QVERIFY(timeStamp.exactMatch(match.captured(1))); + } +} + +void tst_QWebEngineDownloadItem::downloadToDefaultLocation() +{ + QTemporaryDir tmpDir; + QVERIFY(tmpDir.isValid()); + + QCOMPARE(m_profile->downloadPath(), QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); + + m_profile->setDownloadPath(""); + QCOMPARE(m_profile->downloadPath(), QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); + + m_profile->setDownloadPath(tmpDir.path()); + QCOMPARE(m_profile->downloadPath(), tmpDir.path()); + + m_profile->setDownloadPath(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); + QCOMPARE(m_profile->downloadPath(), QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); +} + +void tst_QWebEngineDownloadItem::downloadToNonExistentDir() +{ + QString baseName("test(1.test)"); + QString extension("txt"); + QString fileName = QString("%1.%2").arg(baseName).arg(extension); + QString downloadedFilePath; + bool downloadFinished = false; + + QTemporaryDir tmpDir; + QVERIFY(tmpDir.isValid()); + m_profile->setDownloadPath(tmpDir.path()); + + // Set up HTTP server + ScopedConnection sc1 = connect(m_server, &HttpServer::newRequest, [&](HttpReqRep *rr) { + if (rr->requestMethod() == "GET" && rr->requestPath() == ("/" + fileName)) { + rr->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("application/octet-stream")); + rr->setResponseHeader(QByteArrayLiteral("content-disposition"), QByteArrayLiteral("attachment")); + rr->setResponseBody(QByteArrayLiteral("a")); + rr->sendResponse(); + } else { + rr->setResponseStatus(404); + rr->sendResponse(); + } + }); + + // Set up profile and download handler + ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadItem *item) { + item->accept(); + connect(item, &QWebEngineDownloadItem::finished, [&, item]() { + QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadCompleted); + QCOMPARE(item->isFinished(), true); + QCOMPARE(item->totalBytes(), item->receivedBytes()); + QVERIFY(item->receivedBytes() > 0); + QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason); + QCOMPARE(item->page(), m_page); + downloadFinished = true; + downloadedFilePath = item->path(); + }); + }); + + // Set a non-existent directory for the default download location. + QString nonExistentDownloadPath(m_profile->downloadPath() + "/non_existent_dir"); + m_profile->setDownloadPath(nonExistentDownloadPath); + QCOMPARE(m_profile->downloadPath(), nonExistentDownloadPath); + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadFinished); + QVERIFY(QFile(downloadedFilePath).exists()); + QCOMPARE(downloadedFilePath, nonExistentDownloadPath + "/" + fileName); +} + +void tst_QWebEngineDownloadItem::downloadToReadOnlyDir() +{ + QString baseName("test(1.test)"); + QString extension("txt"); + QString fileName = QString("%1.%2").arg(baseName).arg(extension); + QString downloadedFilePath; + bool downloadAccepted = false; + bool downloadFinished = false; + + QTemporaryDir tmpDir; + QVERIFY(tmpDir.isValid()); + m_profile->setDownloadPath(tmpDir.path()); + + // Set up HTTP server + ScopedConnection sc1 = connect(m_server, &HttpServer::newRequest, [&](HttpReqRep *rr) { + if (rr->requestMethod() == "GET" && rr->requestPath() == ("/" + fileName)) { + rr->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("application/octet-stream")); + rr->setResponseHeader(QByteArrayLiteral("content-disposition"), QByteArrayLiteral("attachment")); + rr->setResponseBody(QByteArrayLiteral("a")); + rr->sendResponse(); + } else { + rr->setResponseStatus(404); + rr->sendResponse(); + } + }); + + QPointer<QWebEngineDownloadItem> downloadItem; + ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadItem *item) { + downloadItem = item; + item->accept(); + connect(item, &QWebEngineDownloadItem::finished, [&, item]() { + downloadFinished = true; + }); + downloadAccepted = true; + }); + + // Change permission for directory. + QFile(m_profile->downloadPath()).setPermissions(QFileDevice::ReadOwner); + QVERIFY(QFile(m_profile->downloadPath()).exists()); + + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadAccepted); + + QVERIFY(downloadItem); + QTRY_COMPARE(downloadItem->state(), QWebEngineDownloadItem::DownloadInterrupted); + QCOMPARE(downloadItem->isFinished(), false); + QCOMPARE(downloadItem->interruptReason(), QWebEngineDownloadItem::FileAccessDenied); + QVERIFY(!QFile(downloadedFilePath).exists()); + + // Clear m_requestedDownloads explicitly because download is accepted but never finished. + m_requestedDownloads.clear(); + QVERIFY(!downloadFinished); + QFile(m_profile->downloadPath()).setPermissions(QFileDevice::WriteOwner); +} + QTEST_MAIN(tst_QWebEngineDownloadItem) #include "tst_qwebenginedownloaditem.moc" diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index fdff57254..0b519fe8a 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -34,7 +34,6 @@ #include <QOpenGLWidget> #include <QPaintEngine> #include <QPushButton> -#include <QRegExp> #include <QScreen> #include <QStateMachine> #include <QtGui/QClipboard> @@ -90,11 +89,8 @@ public Q_SLOTS: private Q_SLOTS: void initTestCase(); void cleanupTestCase(); - void thirdPartyCookiePolicy(); void comboBoxPopupPositionAfterMove(); void comboBoxPopupPositionAfterChildMove(); - void contextMenuCopy(); - void contextMenuPopulatedOnce(); void acceptNavigationRequest(); void acceptNavigationRequestNavigationType(); void geolocationRequestJS_data(); @@ -103,26 +99,13 @@ private Q_SLOTS: void actionStates(); void pasteImage(); void popupFormSubmission(); - void userStyleSheet(); - void userStyleSheetFromLocalFileUrl(); - void userStyleSheetFromQrcUrl(); - void modified(); - void contextMenuCrash(); - void updatePositionDependentActionsCrash(); void callbackSpyDeleted(); void multipleProfilesAndLocalStorage(); - void cursorMovements(); void textSelection(); - void textEditing(); void backActionUpdate(); - void testOptionalJSObjects(); - void testLocalStorageVisibility(); - void testEnablePersistentStorage(); + void localStorageVisibility(); void consoleOutput(); - void errorPageExtension(); - void errorPageExtensionLoadFinished(); void userAgentNewlineStripping(); - void undoActionHaveCustomText(); void renderWidgetHostViewNotShowTopLevel(); void getUserMediaRequest_data(); void getUserMediaRequest(); @@ -134,30 +117,19 @@ private Q_SLOTS: void crashTests_LazyInitializationOfMainFrame(); - void screenshot_data(); - void screenshot(); - #if defined(ENABLE_WEBGL) && ENABLE_WEBGL void acceleratedWebGLScreenshotWithoutView(); void unacceleratedWebGLScreenshotWithoutView(); #endif void testJSPrompt(); - void testStopScheduledPageRefresh(); void findText(); void findTextResult(); void findTextSuccessiveShouldCallAllCallbacks(); - void supportedContentType(); - // [Qt] tst_QWebEnginePage::infiniteLoopJS() timeouts with DFG JIT - // https://bugs.webkit.org/show_bug.cgi?id=79040 - // void infiniteLoopJS(); void deleteQWebEngineViewTwice(); - void renderOnRepaintRequestedShouldNotRecurse(); void loadSignalsOrder_data(); void loadSignalsOrder(); void openWindowDefaultSize(); - void cssMediaTypeGlobalSetting(); - void cssMediaTypePageSetting(); #ifdef Q_OS_MAC void macCopyUnicodeToClipboard(); @@ -170,7 +142,6 @@ private Q_SLOTS: // Tests from tst_QWebEngineFrame - void horizontalScrollAfterBack(); void symmetricUrl(); void progressSignal(); void urlChange(); @@ -182,8 +153,6 @@ private Q_SLOTS: void setHtmlWithStylesheetResource(); void setHtmlWithBaseURL(); void setHtmlWithJSAlert(); - void inputFieldFocus(); - void hitTestContent(); void baseUrl_data(); void baseUrl(); void scrollPosition(); @@ -191,7 +160,6 @@ private Q_SLOTS: void evaluateWillCauseRepaint(); void setContent_data(); void setContent(); - void setCacheLoadControlAttribute(); void setUrlWithPendingLoads(); void setUrlToEmpty(); void setUrlToInvalid(); @@ -368,18 +336,6 @@ private: bool m_allowGeolocation; }; -// [Qt] tst_QWebEnginePage::infiniteLoopJS() timeouts with DFG JIT -// https://bugs.webkit.org/show_bug.cgi?id=79040 -/* -void tst_QWebEnginePage::infiniteLoopJS() -{ - JSTestPage newPage(m_view); - m_view->setPage(&newPage); - m_view->setHtml(QString("<html><body>test</body></html>"), QUrl()); - m_view->page()->evaluateJavaScript("var run = true; var a = 1; while (run) { a++; }"); -} -*/ - void tst_QWebEnginePage::geolocationRequestJS_data() { QTest::addColumn<bool>("allowed"); @@ -653,170 +609,6 @@ protected: } }; -void tst_QWebEnginePage::userStyleSheet() -{ -#if !defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) - QSKIP("QWEBENGINEPAGE_SETNETWORKACCESSMANAGER"); -#else - TestNetworkManager* networkManager = new TestNetworkManager(m_page); - m_page->setNetworkAccessManager(networkManager); - - m_page->settings()->setUserStyleSheetUrl(QUrl("data:text/css;charset=utf-8;base64," - + QByteArray("p { background-image: url('http://does.not/exist.png');}").toBase64())); - m_view->setHtml("<p>hello world</p>"); - QSignalSpy spyFinished(m_view, &QWebEngineView::loadFinished); - QVERIFY(spyFinished.wait()); - - QVERIFY(networkManager->requestedUrls.count() >= 1); - QCOMPARE(networkManager->requestedUrls.at(0), QUrl("http://does.not/exist.png")); -#endif -} - -void tst_QWebEnginePage::userStyleSheetFromLocalFileUrl() -{ -#if !defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) - QSKIP("QWEBENGINEPAGE_SETNETWORKACCESSMANAGER"); -#else - TestNetworkManager* networkManager = new TestNetworkManager(m_page); - m_page->setNetworkAccessManager(networkManager); - - QUrl styleSheetUrl = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginepage/resources/user.css")); - m_page->settings()->setUserStyleSheetUrl(styleSheetUrl); - m_view->setHtml("<p>hello world</p>"); - QSignalSpy spyFinished(m_view, &QWebEngineView::loadFinished); - QVERIFY(spyFinished.wait()); - - QVERIFY(networkManager->requestedUrls.count() >= 1); - QCOMPARE(networkManager->requestedUrls.at(0), QUrl("http://does.not/exist.png")); -#endif -} - -void tst_QWebEnginePage::userStyleSheetFromQrcUrl() -{ -#if !defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) - QSKIP("QWEBENGINEPAGE_SETNETWORKACCESSMANAGER"); -#else - TestNetworkManager* networkManager = new TestNetworkManager(m_page); - m_page->setNetworkAccessManager(networkManager); - - m_page->settings()->setUserStyleSheetUrl(QUrl("qrc:///resources/user.css")); - m_view->setHtml("<p>hello world</p>"); - QSignalSpy spyFinished(m_view, &QWebEngineView::loadFinished); - QVERIFY(spyFinished.wait()); - - QVERIFY(networkManager->requestedUrls.count() >= 1); - QCOMPARE(networkManager->requestedUrls.at(0), QUrl("http://does.not/exist.png")); -#endif -} - -void tst_QWebEnginePage::modified() -{ -#if !defined(QWEBENGINEPAGE_ISMODIFIED) - QSKIP("QWEBENGINEPAGE_ISMODIFIED"); -#else - m_page->setUrl(QUrl("data:text/html,<body>blub")); - QSignalSpy spyFinished(m_view, &QWebEngineView::loadFinished); - QVERIFY(spyFinished.wait()); - - m_page->setUrl(QUrl("data:text/html,<body id=foo contenteditable>blah")); - QSignalSpy spyFinished(m_view, &QWebEngineView::loadFinished); - QVERIFY(spyFinished.wait()); - - QVERIFY(!m_page->isModified()); - - m_page->runJavaScript("document.getElementById('foo').focus()"); - evaluateJavaScriptSync(m_page, "document.execCommand('InsertText', true, 'Test');"); - - QVERIFY(m_page->isModified()); - - evaluateJavaScriptSync(m_page, "document.execCommand('Undo', true);"); - - QVERIFY(!m_page->isModified()); - - evaluateJavaScriptSync(m_page, "document.execCommand('Redo', true);"); - - QVERIFY(m_page->isModified()); - - QVERIFY(m_page->history()->canGoBack()); - QVERIFY(!m_page->history()->canGoForward()); - QCOMPARE(m_page->history()->count(), 2); - QVERIFY(m_page->history()->backItem().isValid()); - QVERIFY(!m_page->history()->forwardItem().isValid()); - - m_page->history()->back(); - QSignalSpy spyFinished(m_view, &QWebEngineView::loadFinished); - QVERIFY(spyFinished.wait()); - - QVERIFY(!m_page->history()->canGoBack()); - QVERIFY(m_page->history()->canGoForward()); - - QVERIFY(!m_page->isModified()); - - QCOMPARE(m_page->history()->currentItemIndex(), 0); - - m_page->history()->setMaximumItemCount(3); - QCOMPARE(m_page->history()->maximumItemCount(), 3); - - QVariant variant("string test"); - m_page->history()->currentItem().setUserData(variant); - QVERIFY(m_page->history()->currentItem().userData().toString() == "string test"); - - m_page->setUrl(QUrl("data:text/html,<body>This is second page")); - m_page->setUrl(QUrl("data:text/html,<body>This is third page")); - QCOMPARE(m_page->history()->count(), 2); - m_page->setUrl(QUrl("data:text/html,<body>This is fourth page")); - QCOMPARE(m_page->history()->count(), 2); - m_page->setUrl(QUrl("data:text/html,<body>This is fifth page")); - QSignalSpy spy(m_page, &QWebEnginePage::saveFrameStateRequested); - QVERIFY(spy.wait()); -#endif -} - -// https://bugs.webkit.org/show_bug.cgi?id=51331 -void tst_QWebEnginePage::updatePositionDependentActionsCrash() -{ -#if !defined(QWEBENGINEPAGE_UPDATEPOSITIONDEPENDENTACTIONS) - QSKIP("QWEBENGINEPAGE_UPDATEPOSITIONDEPENDENTACTIONS"); -#else - QWebEngineView view; - view.setHtml("<p>test"); - QPoint pos(0, 0); - view.page()->updatePositionDependentActions(pos); - QMenu* contextMenu = 0; - const QList<QObject *> children = view.children(); - for (QObject *child : children) { - contextMenu = qobject_cast<QMenu*>(child); - if (contextMenu) - break; - } - QVERIFY(!contextMenu); -#endif -} - -// https://bugs.webkit.org/show_bug.cgi?id=20357 -void tst_QWebEnginePage::contextMenuCrash() -{ -#if !defined(QWEBENGINEPAGE_SWALLOWCONTEXTMENUEVENT) - QSKIP("QWEBENGINEPAGE_SWALLOWCONTEXTMENUEVENT"); -#else - QWebEngineView view; - view.setHtml("<p>test"); - QPoint pos(0, 0); - QContextMenuEvent event(QContextMenuEvent::Mouse, pos); - view.page()->swallowContextMenuEvent(&event); - view.page()->updatePositionDependentActions(pos); - QMenu* contextMenu = 0; - const QList<QObject *> children = view.children(); - for (QObject *child : children) { - contextMenu = qobject_cast<QMenu*>(child); - if (contextMenu) - break; - } - QVERIFY(contextMenu); - delete contextMenu; -#endif -} - void tst_QWebEnginePage::multipleProfilesAndLocalStorage() { QDir dir(tmpDirPath()); @@ -889,204 +681,6 @@ public: } }; -void tst_QWebEnginePage::cursorMovements() -{ -#if !defined(QWEBENGINEPAGE_SELECTEDTEXT) - QSKIP("QWEBENGINEPAGE_SELECTEDTEXT"); -#else - QScopedPointer<CursorTrackedPage> page(new CursorTrackedPage); - QString content("<html><body><p id=one>The quick brown fox</p><p id=two>jumps over the lazy dog</p><p>May the source<br/>be with you!</p></body></html>"); - page->setHtml(content); - - // this will select the first paragraph - QString script = "var range = document.createRange(); " \ - "var node = document.getElementById(\"one\"); " \ - "range.selectNode(node); " \ - "getSelection().addRange(range);"; - evaluateJavaScriptSync(page.data(), script); - QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox")); - - QRegExp regExp(" style=\".*\""); - regExp.setMinimal(true); - QCOMPARE(page->selectedHtml().trimmed().replace(regExp, ""), QString::fromLatin1("<p id=\"one\">The quick brown fox</p>")); - - // these actions must exist - QVERIFY(page->action(QWebEnginePage::MoveToNextChar) != 0); - QVERIFY(page->action(QWebEnginePage::MoveToPreviousChar) != 0); - QVERIFY(page->action(QWebEnginePage::MoveToNextWord) != 0); - QVERIFY(page->action(QWebEnginePage::MoveToPreviousWord) != 0); - QVERIFY(page->action(QWebEnginePage::MoveToNextLine) != 0); - QVERIFY(page->action(QWebEnginePage::MoveToPreviousLine) != 0); - QVERIFY(page->action(QWebEnginePage::MoveToStartOfLine) != 0); - QVERIFY(page->action(QWebEnginePage::MoveToEndOfLine) != 0); - QVERIFY(page->action(QWebEnginePage::MoveToStartOfBlock) != 0); - QVERIFY(page->action(QWebEnginePage::MoveToEndOfBlock) != 0); - QVERIFY(page->action(QWebEnginePage::MoveToStartOfDocument) != 0); - QVERIFY(page->action(QWebEnginePage::MoveToEndOfDocument) != 0); - - // right now they are disabled because contentEditable is false - QCOMPARE(page->action(QWebEnginePage::MoveToNextChar)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::MoveToPreviousChar)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::MoveToNextWord)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::MoveToPreviousWord)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::MoveToNextLine)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::MoveToPreviousLine)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::MoveToStartOfLine)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::MoveToEndOfLine)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::MoveToStartOfBlock)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::MoveToEndOfBlock)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::MoveToStartOfDocument)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::MoveToEndOfDocument)->isEnabled(), false); - - // make it editable before navigating the cursor - page->setContentEditable(true); - - // here the actions are enabled after contentEditable is true - QCOMPARE(page->action(QWebEnginePage::MoveToNextChar)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::MoveToPreviousChar)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::MoveToNextWord)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::MoveToPreviousWord)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::MoveToNextLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::MoveToPreviousLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::MoveToStartOfLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::MoveToEndOfLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::MoveToStartOfBlock)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::MoveToEndOfBlock)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::MoveToStartOfDocument)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::MoveToEndOfDocument)->isEnabled(), true); - - // cursor will be before the word "jump" - page->triggerAction(QWebEnginePage::MoveToNextChar); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 0); - - // cursor will be between 'j' and 'u' in the word "jump" - page->triggerAction(QWebEnginePage::MoveToNextChar); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 1); - - // cursor will be between 'u' and 'm' in the word "jump" - page->triggerAction(QWebEnginePage::MoveToNextChar); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 2); - - // cursor will be after the word "jump" - page->triggerAction(QWebEnginePage::MoveToNextWord); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 5); - - // cursor will be after the word "lazy" - page->triggerAction(QWebEnginePage::MoveToNextWord); - page->triggerAction(QWebEnginePage::MoveToNextWord); - page->triggerAction(QWebEnginePage::MoveToNextWord); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 19); - - // cursor will be between 'z' and 'y' in "lazy" - page->triggerAction(QWebEnginePage::MoveToPreviousChar); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 18); - - // cursor will be between 'a' and 'z' in "lazy" - page->triggerAction(QWebEnginePage::MoveToPreviousChar); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 17); - - // cursor will be before the word "lazy" - page->triggerAction(QWebEnginePage::MoveToPreviousWord); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 15); - - // cursor will be before the word "quick" - page->triggerAction(QWebEnginePage::MoveToPreviousWord); - page->triggerAction(QWebEnginePage::MoveToPreviousWord); - page->triggerAction(QWebEnginePage::MoveToPreviousWord); - page->triggerAction(QWebEnginePage::MoveToPreviousWord); - page->triggerAction(QWebEnginePage::MoveToPreviousWord); - page->triggerAction(QWebEnginePage::MoveToPreviousWord); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 4); - - // cursor will be between 'p' and 's' in the word "jumps" - page->triggerAction(QWebEnginePage::MoveToNextWord); - page->triggerAction(QWebEnginePage::MoveToNextWord); - page->triggerAction(QWebEnginePage::MoveToNextWord); - page->triggerAction(QWebEnginePage::MoveToNextChar); - page->triggerAction(QWebEnginePage::MoveToNextChar); - page->triggerAction(QWebEnginePage::MoveToNextChar); - page->triggerAction(QWebEnginePage::MoveToNextChar); - page->triggerAction(QWebEnginePage::MoveToNextChar); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 4); - - // cursor will be before the word "jumps" - page->triggerAction(QWebEnginePage::MoveToStartOfLine); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 0); - - // cursor will be after the word "dog" - page->triggerAction(QWebEnginePage::MoveToEndOfLine); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 23); - - // cursor will be between 'w' and 'n' in "brown" - page->triggerAction(QWebEnginePage::MoveToStartOfLine); - page->triggerAction(QWebEnginePage::MoveToPreviousWord); - page->triggerAction(QWebEnginePage::MoveToPreviousWord); - page->triggerAction(QWebEnginePage::MoveToNextChar); - page->triggerAction(QWebEnginePage::MoveToNextChar); - page->triggerAction(QWebEnginePage::MoveToNextChar); - page->triggerAction(QWebEnginePage::MoveToNextChar); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 14); - - // cursor will be after the word "fox" - page->triggerAction(QWebEnginePage::MoveToEndOfLine); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 19); - - // cursor will be before the word "The" - page->triggerAction(QWebEnginePage::MoveToStartOfDocument); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 0); - - // cursor will be after the word "you!" - page->triggerAction(QWebEnginePage::MoveToEndOfDocument); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 12); - - // cursor will be before the word "be" - page->triggerAction(QWebEnginePage::MoveToStartOfBlock); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 0); - - // cursor will be after the word "you!" - page->triggerAction(QWebEnginePage::MoveToEndOfBlock); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 12); - - // try to move before the document start - page->triggerAction(QWebEnginePage::MoveToStartOfDocument); - page->triggerAction(QWebEnginePage::MoveToPreviousChar); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 0); - page->triggerAction(QWebEnginePage::MoveToStartOfDocument); - page->triggerAction(QWebEnginePage::MoveToPreviousWord); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 0); - - // try to move past the document end - page->triggerAction(QWebEnginePage::MoveToEndOfDocument); - page->triggerAction(QWebEnginePage::MoveToNextChar); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 12); - page->triggerAction(QWebEnginePage::MoveToEndOfDocument); - page->triggerAction(QWebEnginePage::MoveToNextWord); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 12); -#endif -} - void tst_QWebEnginePage::textSelection() { QWebEngineView view; @@ -1101,35 +695,6 @@ void tst_QWebEnginePage::textSelection() // these actions must exist QVERIFY(page->action(QWebEnginePage::SelectAll) != 0); -#if defined(QWEBENGINEPAGE_SELECTACTIONS) - QVERIFY(page->action(QWebEnginePage::SelectNextChar) != 0); - QVERIFY(page->action(QWebEnginePage::SelectPreviousChar) != 0); - QVERIFY(page->action(QWebEnginePage::SelectNextWord) != 0); - QVERIFY(page->action(QWebEnginePage::SelectPreviousWord) != 0); - QVERIFY(page->action(QWebEnginePage::SelectNextLine) != 0); - QVERIFY(page->action(QWebEnginePage::SelectPreviousLine) != 0); - QVERIFY(page->action(QWebEnginePage::SelectStartOfLine) != 0); - QVERIFY(page->action(QWebEnginePage::SelectEndOfLine) != 0); - QVERIFY(page->action(QWebEnginePage::SelectStartOfBlock) != 0); - QVERIFY(page->action(QWebEnginePage::SelectEndOfBlock) != 0); - QVERIFY(page->action(QWebEnginePage::SelectStartOfDocument) != 0); - QVERIFY(page->action(QWebEnginePage::SelectEndOfDocument) != 0); - - // right now they are disabled because contentEditable is false and - // there isn't an existing selection to modify - QCOMPARE(page->action(QWebEnginePage::SelectNextChar)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SelectPreviousChar)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SelectNextWord)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SelectPreviousWord)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SelectNextLine)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SelectPreviousLine)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SelectStartOfLine)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SelectEndOfLine)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SelectStartOfBlock)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SelectEndOfBlock)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SelectStartOfDocument)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SelectEndOfDocument)->isEnabled(), false); -#endif // ..but SelectAll is awalys enabled QCOMPARE(page->action(QWebEnginePage::SelectAll)->isEnabled(), true); @@ -1144,173 +709,10 @@ void tst_QWebEnginePage::textSelection() "getSelection().addRange(range);"; evaluateJavaScriptSync(page, selectScript); QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox")); -#if defined(QWEBENGINEPAGE_SELECTEDHTML) - QRegExp regExp(" style=\".*\""); - regExp.setMinimal(true); - QCOMPARE(page->selectedHtml().trimmed().replace(regExp, ""), QString::fromLatin1("<p id=\"one\">The quick brown fox</p>")); -#endif // Make sure hasSelection returns true, since there is selected text now... QCOMPARE(page->hasSelection(), true); - -#if defined(QWEBENGINEPAGE_SELECTACTIONS) - // here the actions are enabled after a selection has been created - QCOMPARE(page->action(QWebEnginePage::SelectNextChar)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectPreviousChar)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectNextWord)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectPreviousWord)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectNextLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectPreviousLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectStartOfLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectEndOfLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectStartOfBlock)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectEndOfBlock)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectStartOfDocument)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectEndOfDocument)->isEnabled(), true); - - // make it editable before navigating the cursor - page->setContentEditable(true); - - // cursor will be before the word "The", this makes sure there is a charet - page->triggerAction(QWebEnginePage::MoveToStartOfDocument); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 0); - - // here the actions are enabled after contentEditable is true - QCOMPARE(page->action(QWebEnginePage::SelectNextChar)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectPreviousChar)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectNextWord)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectPreviousWord)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectNextLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectPreviousLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectStartOfLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectEndOfLine)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectStartOfBlock)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectEndOfBlock)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectStartOfDocument)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SelectEndOfDocument)->isEnabled(), true); -#endif } -void tst_QWebEnginePage::textEditing() -{ -#if !defined(QWEBENGINEPAGE_EVALUATEJAVASCRIPT) - QSKIP("QWEBENGINEPAGE_EVALUATEJAVASCRIPT"); -#else - QScopedPointer<CursorTrackedPage> page(new CursorTrackedPage); - QString content("<html><body><p id=one>The quick brown fox</p>" \ - "<p id=two>jumps over the lazy dog</p>" \ - "<p>May the source<br/>be with you!</p></body></html>"); - page->setHtml(content); - - // these actions must exist - QVERIFY(page->action(QWebEnginePage::Cut) != 0); - QVERIFY(page->action(QWebEnginePage::Copy) != 0); - QVERIFY(page->action(QWebEnginePage::Paste) != 0); - QVERIFY(page->action(QWebEnginePage::DeleteStartOfWord) != 0); - QVERIFY(page->action(QWebEnginePage::DeleteEndOfWord) != 0); - QVERIFY(page->action(QWebEnginePage::SetTextDirectionDefault) != 0); - QVERIFY(page->action(QWebEnginePage::SetTextDirectionLeftToRight) != 0); - QVERIFY(page->action(QWebEnginePage::SetTextDirectionRightToLeft) != 0); - QVERIFY(page->action(QWebEnginePage::ToggleBold) != 0); - QVERIFY(page->action(QWebEnginePage::ToggleItalic) != 0); - QVERIFY(page->action(QWebEnginePage::ToggleUnderline) != 0); - QVERIFY(page->action(QWebEnginePage::InsertParagraphSeparator) != 0); - QVERIFY(page->action(QWebEnginePage::InsertLineSeparator) != 0); - QVERIFY(page->action(QWebEnginePage::PasteAndMatchStyle) != 0); - QVERIFY(page->action(QWebEnginePage::RemoveFormat) != 0); - QVERIFY(page->action(QWebEnginePage::ToggleStrikethrough) != 0); - QVERIFY(page->action(QWebEnginePage::ToggleSubscript) != 0); - QVERIFY(page->action(QWebEnginePage::ToggleSuperscript) != 0); - QVERIFY(page->action(QWebEnginePage::InsertUnorderedList) != 0); - QVERIFY(page->action(QWebEnginePage::InsertOrderedList) != 0); - QVERIFY(page->action(QWebEnginePage::Indent) != 0); - QVERIFY(page->action(QWebEnginePage::Outdent) != 0); - QVERIFY(page->action(QWebEnginePage::AlignCenter) != 0); - QVERIFY(page->action(QWebEnginePage::AlignJustified) != 0); - QVERIFY(page->action(QWebEnginePage::AlignLeft) != 0); - QVERIFY(page->action(QWebEnginePage::AlignRight) != 0); - - // right now they are disabled because contentEditable is false - QCOMPARE(page->action(QWebEnginePage::Cut)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::Paste)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::DeleteStartOfWord)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::DeleteEndOfWord)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SetTextDirectionDefault)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SetTextDirectionLeftToRight)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::SetTextDirectionRightToLeft)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::ToggleBold)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::ToggleItalic)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::ToggleUnderline)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::InsertParagraphSeparator)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::InsertLineSeparator)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::PasteAndMatchStyle)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::RemoveFormat)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::ToggleStrikethrough)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::ToggleSubscript)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::ToggleSuperscript)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::InsertUnorderedList)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::InsertOrderedList)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::Indent)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::Outdent)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::AlignCenter)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::AlignJustified)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::AlignLeft)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::AlignRight)->isEnabled(), false); - - // Select everything - page->triggerAction(QWebEnginePage::SelectAll); - - // make sure it is enabled since there is a selection - QCOMPARE(page->action(QWebEnginePage::Copy)->isEnabled(), true); - - // make it editable before navigating the cursor - page->setContentEditable(true); - - // clear the selection - page->triggerAction(QWebEnginePage::MoveToStartOfDocument); - QVERIFY(page->isSelectionCollapsed()); - QCOMPARE(page->selectionStartOffset(), 0); - - // make sure it is disabled since there isn't a selection - QCOMPARE(page->action(QWebEnginePage::Copy)->isEnabled(), false); - - // here the actions are enabled after contentEditable is true - QCOMPARE(page->action(QWebEnginePage::Paste)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::DeleteStartOfWord)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::DeleteEndOfWord)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SetTextDirectionDefault)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SetTextDirectionLeftToRight)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::SetTextDirectionRightToLeft)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::ToggleBold)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::ToggleItalic)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::ToggleUnderline)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::InsertParagraphSeparator)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::InsertLineSeparator)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::PasteAndMatchStyle)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::ToggleStrikethrough)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::ToggleSubscript)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::ToggleSuperscript)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::InsertUnorderedList)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::InsertOrderedList)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::Indent)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::Outdent)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::AlignCenter)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::AlignJustified)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::AlignLeft)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::AlignRight)->isEnabled(), true); - - // make sure these are disabled since there isn't a selection - QCOMPARE(page->action(QWebEnginePage::Cut)->isEnabled(), false); - QCOMPARE(page->action(QWebEnginePage::RemoveFormat)->isEnabled(), false); - - // make sure everything is selected - page->triggerAction(QWebEnginePage::SelectAll); - - // this is only true if there is an editable selection - QCOMPARE(page->action(QWebEnginePage::Cut)->isEnabled(), true); - QCOMPARE(page->action(QWebEnginePage::RemoveFormat)->isEnabled(), true); -#endif -} void tst_QWebEnginePage::backActionUpdate() { @@ -1331,243 +733,39 @@ void tst_QWebEnginePage::backActionUpdate() QVERIFY(action->isEnabled()); } -#if defined(QWEBENGINEPAGE_SETTINGS) -static inline bool testFlag(QWebEnginePage& webPage, QWebEngineSettings::WebAttribute settingAttribute, const QString& jsObjectName, bool settingValue) -{ - webPage.settings()->setAttribute(settingAttribute, settingValue); - return evaluateJavaScriptSync(&webPage, QString("(window.%1 != undefined)").arg(jsObjectName)).toBool(); -} -#endif - -void tst_QWebEnginePage::testOptionalJSObjects() -{ -#if !defined(QWEBENGINESETTINGS) - QSKIP("QWEBENGINSETTINGS"); -#else - // Once a feature is enabled and the JS object is accessed turning off the setting will not turn off - // the visibility of the JS object any more. For this reason this test uses two QWebEnginePage instances. - // Part of the test is to make sure that the QWebEnginePage instances do not interfere with each other so turning on - // a feature for one instance will not turn it on for another. - - QWebEnginePage webPage1; - QWebEnginePage webPage2; - - webPage1.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("http://www.example.com/")); - webPage2.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("http://www.example.com/")); - - QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue); - QCOMPARE(testFlag(webPage1, QWebEngineSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false); - QCOMPARE(testFlag(webPage2, QWebEngineSettings::OfflineWebApplicationCacheEnabled, "applicationCache", true), true); - QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue); - QCOMPARE(testFlag(webPage1, QWebEngineSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false); - QCOMPARE(testFlag(webPage2, QWebEngineSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), true); - - QCOMPARE(testFlag(webPage1, QWebEngineSettings::LocalStorageEnabled, "localStorage", false), false); - QCOMPARE(testFlag(webPage2, QWebEngineSettings::LocalStorageEnabled, "localStorage", true), true); - QCOMPARE(testFlag(webPage1, QWebEngineSettings::LocalStorageEnabled, "localStorage", false), false); - QCOMPARE(testFlag(webPage2, QWebEngineSettings::LocalStorageEnabled, "localStorage", false), true); -#endif -} - -#if defined(QWEBENGINEPAGE_SETTINGS) -static inline bool checkLocalStorageVisibility(QWebEnginePage& webPage, bool localStorageEnabled) -{ - webPage.settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, localStorageEnabled); - return evaluateJavaScriptSync(&webPage, QString("(window.localStorage != undefined)")).toBool(); -} -#endif - -void tst_QWebEnginePage::testLocalStorageVisibility() -{ -#if !defined(QWEBENGINEPAGE_SETTINGS) - QSKIP("QWEBENGINEPAGE_SETTINGS"); -#else - // Local storage's visibility depends on its security origin, which depends on base url. - // Initially, it will test it with base urls that get a globally unique origin, which may not - // be able to use local storage even if the feature is enabled. Then later the same test is - // done but with urls that would get a valid origin, so local storage could be used. - // Before every test case it checks if local storage is not already visible. - - QWebEnginePage webPage; - - webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl()); - - QCOMPARE(checkLocalStorageVisibility(webPage, false), false); - QCOMPARE(checkLocalStorageVisibility(webPage, true), false); - - webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("invalid")); - - QCOMPARE(checkLocalStorageVisibility(webPage, false), false); - QCOMPARE(checkLocalStorageVisibility(webPage, true), false); - - webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("://misparsed.com")); - - QCOMPARE(checkLocalStorageVisibility(webPage, false), false); - QCOMPARE(checkLocalStorageVisibility(webPage, true), false); - - webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("http://")); - - QCOMPARE(checkLocalStorageVisibility(webPage, false), false); - QCOMPARE(checkLocalStorageVisibility(webPage, true), false); - - webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("about:blank")); - - QCOMPARE(checkLocalStorageVisibility(webPage, false), false); - QCOMPARE(checkLocalStorageVisibility(webPage, true), false); - - webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("data:text/html,test")); - - QCOMPARE(checkLocalStorageVisibility(webPage, false), false); - QCOMPARE(checkLocalStorageVisibility(webPage, true), false); - - webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("file:///")); - - QCOMPARE(checkLocalStorageVisibility(webPage, false), false); - QCOMPARE(checkLocalStorageVisibility(webPage, true), true); - - webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("http://www.example.com")); - - QCOMPARE(checkLocalStorageVisibility(webPage, false), false); - QCOMPARE(checkLocalStorageVisibility(webPage, true), true); - - webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("https://www.example.com")); - - QCOMPARE(checkLocalStorageVisibility(webPage, false), false); - QCOMPARE(checkLocalStorageVisibility(webPage, true), true); - - webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("ftp://files.example.com")); - - QCOMPARE(checkLocalStorageVisibility(webPage, false), false); - QCOMPARE(checkLocalStorageVisibility(webPage, true), true); - - webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("file:///path/to/index.html")); - - QCOMPARE(checkLocalStorageVisibility(webPage, false), false); - QCOMPARE(checkLocalStorageVisibility(webPage, true), true); -#endif -} - -void tst_QWebEnginePage::testEnablePersistentStorage() -{ -#if !defined(QWEBENGINESETTINGS) - QSKIP("QWEBENGINESETTINGS"); -#else - QWebEnginePage webPage; - - // By default all persistent options should be disabled - QCOMPARE(webPage.settings()->testAttribute(QWebEngineSettings::LocalStorageEnabled), false); - QCOMPARE(webPage.settings()->testAttribute(QWebEngineSettings::OfflineStorageDatabaseEnabled), false); - QCOMPARE(webPage.settings()->testAttribute(QWebEngineSettings::OfflineWebApplicationCacheEnabled), false); - QVERIFY(webPage.settings()->iconDatabasePath().isEmpty()); - - QWebEngineSettings::enablePersistentStorage(); - - - QTRY_COMPARE(webPage.settings()->testAttribute(QWebEngineSettings::LocalStorageEnabled), true); - QTRY_COMPARE(webPage.settings()->testAttribute(QWebEngineSettings::OfflineStorageDatabaseEnabled), true); - QTRY_COMPARE(webPage.settings()->testAttribute(QWebEngineSettings::OfflineWebApplicationCacheEnabled), true); - - QTRY_VERIFY(!webPage.settings()->offlineStoragePath().isEmpty()); - QTRY_VERIFY(!webPage.settings()->offlineWebApplicationCachePath().isEmpty()); - QTRY_VERIFY(!webPage.settings()->iconDatabasePath().isEmpty()); -#endif -} - - -#if defined(QWEBENGINEPAGE_ERRORPAGEEXTENSION) -class ErrorPage : public QWebEnginePage -{ -public: - - ErrorPage(QWidget* parent = 0): QWebEnginePage(parent) - { - } - - virtual bool supportsExtension(Extension extension) const - { - return extension == ErrorPageExtension; - } - - virtual bool extension(Extension, const ExtensionOption* option, ExtensionReturn* output) - { - ErrorPageExtensionReturn* errorPage = static_cast<ErrorPageExtensionReturn*>(output); - - errorPage->contentType = "text/html"; - errorPage->content = "error"; - return true; - } -}; -#endif - -void tst_QWebEnginePage::errorPageExtension() -{ -#if !defined(QWEBENGINEPAGE_ERRORPAGEEXTENSION) - QSKIP("QWEBENGINEPAGE_ERRORPAGEEXTENSION"); -#else - ErrorPage page; - m_view->setPage(&page); - - QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool))); - - m_view->setUrl(QUrl("data:text/html,foo")); - QTRY_COMPARE(spyLoadFinished.count(), 1); - - page.setUrl(QUrl("http://non.existent/url")); - QTRY_COMPARE(spyLoadFinished.count(), 2); - QCOMPARE(toPlainTextSync(&page), QString("error")); - QCOMPARE(page.history()->count(), 2); - QCOMPARE(page.history()->currentItem().url(), QUrl("http://non.existent/url")); - QCOMPARE(page.history()->canGoBack(), true); - QCOMPARE(page.history()->canGoForward(), false); - - page.triggerAction(QWebEnginePage::Back); - QTRY_COMPARE(page.history()->canGoBack(), false); - QTRY_COMPARE(page.history()->canGoForward(), true); - - page.triggerAction(QWebEnginePage::Forward); - QTRY_COMPARE(page.history()->canGoBack(), true); - QTRY_COMPARE(page.history()->canGoForward(), false); - - page.triggerAction(QWebEnginePage::Back); - QTRY_COMPARE(page.history()->canGoBack(), false); - QTRY_COMPARE(page.history()->canGoForward(), true); - QTRY_COMPARE(page.history()->currentItem().url(), QUrl("data:text/html,foo")); - - m_view->setPage(0); -#endif -} - -void tst_QWebEnginePage::errorPageExtensionLoadFinished() +void tst_QWebEnginePage::localStorageVisibility() { -#if !defined(QWEBENGINEPAGE_ERRORPAGEEXTENSION) - QSKIP("QWEBENGINEPAGE_ERRORPAGEEXTENSION"); -#else - ErrorPage page; - m_view->setPage(&page); - - QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool))); - QSignalSpy spyFrameLoadFinished(m_view->page(), SIGNAL(loadFinished(bool))); - - m_view->setUrl(QUrl("data:text/html,foo")); - QTRY_COMPARE(spyLoadFinished.count(), 1); - QTRY_COMPARE(spyFrameLoadFinished.count(), 1); - - const bool loadSucceded = spyLoadFinished.at(0).at(0).toBool(); - QVERIFY(loadSucceded); - const bool frameLoadSucceded = spyFrameLoadFinished.at(0).at(0).toBool(); - QVERIFY(frameLoadSucceded); - - m_view->page()->setUrl(QUrl("http://non.existent/url")); - QTRY_COMPARE(spyLoadFinished.count(), 2); - QTRY_COMPARE(spyFrameLoadFinished.count(), 2); - - const bool nonExistantLoadSucceded = spyLoadFinished.at(1).at(0).toBool(); - QVERIFY(nonExistantLoadSucceded); - const bool nonExistantFrameLoadSucceded = spyFrameLoadFinished.at(1).at(0).toBool(); - QVERIFY(nonExistantFrameLoadSucceded); - - m_view->setPage(0); -#endif + QWebEngineProfile profile; + QWebEnginePage webPage1(&profile); + QWebEnginePage webPage2(&profile); + + webPage1.settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, true); + webPage2.settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, false); + + QSignalSpy loadSpy1(&webPage1, &QWebEnginePage::loadFinished); + QSignalSpy loadSpy2(&webPage2, &QWebEnginePage::loadFinished); + webPage1.setHtml(QString("<html><body>test</body></html>"), QUrl("http://www.example.com/")); + webPage2.setHtml(QString("<html><body>test</body></html>"), QUrl("http://www.example.com/")); + QTRY_COMPARE(loadSpy1.count(), 1); + QTRY_COMPARE(loadSpy2.count(), 1); + + // The attribute determines the visibility of the window.localStorage object. + QVERIFY(evaluateJavaScriptSync(&webPage1, QString("(window.localStorage != undefined)")).toBool()); + QVERIFY(!evaluateJavaScriptSync(&webPage2, QString("(window.localStorage != undefined)")).toBool()); + + // Switching the feature off does not actively remove the object from webPage1. + webPage1.settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, false); + webPage2.settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, true); + QVERIFY(evaluateJavaScriptSync(&webPage1, QString("(window.localStorage != undefined)")).toBool()); + QVERIFY(evaluateJavaScriptSync(&webPage2, QString("(window.localStorage != undefined)")).toBool()); + + // The object disappears only after reloading. + webPage1.triggerAction(QWebEnginePage::Reload); + webPage2.triggerAction(QWebEnginePage::Reload); + QTRY_COMPARE(loadSpy1.count(), 2); + QTRY_COMPARE(loadSpy2.count(), 2); + QVERIFY(!evaluateJavaScriptSync(&webPage1, QString("(window.localStorage != undefined)")).toBool()); + QVERIFY(evaluateJavaScriptSync(&webPage2, QString("(window.localStorage != undefined)")).toBool()); } void tst_QWebEnginePage::userAgentNewlineStripping() @@ -1592,70 +790,9 @@ void tst_QWebEnginePage::crashTests_LazyInitializationOfMainFrame() webPage.selectedText(); } { -#if defined(QWEBENGINEPAGE_SELECTEDHTML) - QWebEnginePage webPage; - webPage.selectedHtml(); -#endif - } - { QWebEnginePage webPage; webPage.triggerAction(QWebEnginePage::Back, true); } - { -#if defined(QWEBENGINEPAGE_UPDATEPOSITIONDEPENDENTACTIONS) - QWebEnginePage webPage; - QPoint pos(10,10); - webPage.updatePositionDependentActions(pos); -#endif - } -} - -#if defined(QWEBENGINEPAGE_RENDER) -static void takeScreenshot(QWebEnginePage* page) -{ - page->setViewportSize(page->contentsSize()); - QImage image(page->viewportSize(), QImage::Format_ARGB32); - QPainter painter(&image); - page->render(&painter); - painter.end(); -} -#endif - -void tst_QWebEnginePage::screenshot_data() -{ - QTest::addColumn<QString>("html"); - QTest::newRow("WithoutPlugin") << "<html><body id='b'>text</body></html>"; - QTest::newRow("WindowedPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf'></embed></body></html>"); - QTest::newRow("WindowlessPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf' wmode='transparent'></embed></body></html>"); -} - -void tst_QWebEnginePage::screenshot() -{ -#if !defined(QWEBENGINESETTINGS) - QSKIP("QWEBENGINESETTINGS"); -#else - if (!QDir(TESTS_SOURCE_DIR).exists()) - W_QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll); - - QDir::setCurrent(TESTS_SOURCE_DIR); - - QFETCH(QString, html); - QWebEnginePage page; - page.settings()->setAttribute(QWebEngineSettings::PluginsEnabled, true); - page.setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR)); - QVERIFY(spyFinished.wait(2000)); - - // take screenshot without a view - takeScreenshot(&page); - - QWebEngineView view; - view.setPage(&page); - - // take screenshot when attached to a view - takeScreenshot(&page); - - QDir::setCurrent(QApplication::applicationDirPath()); -#endif } #if defined(ENABLE_WEBGL) && ENABLE_WEBGL @@ -1754,37 +891,6 @@ void tst_QWebEnginePage::testJSPrompt() QVERIFY(res); } -void tst_QWebEnginePage::testStopScheduledPageRefresh() -{ -#if !defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) - QSKIP("QWEBENGINEPAGE_SETNETWORKACCESSMANAGER"); -#else - // Without QWebEnginePage::StopScheduledPageRefresh - QWebEnginePage page1; - page1.setNetworkAccessManager(new TestNetworkManager(&page1)); - page1.setHtml("<html><head>" - "<meta http-equiv=\"refresh\"content=\"0;URL=qrc:///resources/index.html\">" - "</head><body><h1>Page redirects immediately...</h1>" - "</body></html>"); - QSignalSpy spyFinished(&page1, &QWebEnginePage::loadFinished); - QVERIFY(spyFinished.wait(); - QTest::qWait(500); - QCOMPARE(page1.url(), QUrl(QLatin1String("qrc:///resources/index.html"))); - - // With QWebEnginePage::StopScheduledPageRefresh - QWebEnginePage page2; - page2.setNetworkAccessManager(new TestNetworkManager(&page2)); - page2.setHtml("<html><head>" - "<meta http-equiv=\"refresh\"content=\"1;URL=qrc:///resources/index.html\">" - "</head><body><h1>Page redirect test with 1 sec timeout...</h1>" - "</body></html>"); - page2.triggerAction(QWebEnginePage::StopScheduledPageRefresh); - QTest::qWait(1500); - QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=118673", Continue); - QCOMPARE(page2.url().toString(), QLatin1String("about:blank")); -#endif -} - void tst_QWebEnginePage::findText() { QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool))); @@ -1884,95 +990,6 @@ void tst_QWebEnginePage::findTextSuccessiveShouldCallAllCallbacks() QVERIFY(spy5.wasCalled()); } -#if defined(QWEBENGINEPAGE_SUPPORTEDCONTENTTYPES) -static QString getMimeTypeForExtension(const QString &ext) -{ - QMimeType mimeType = QMimeDatabase().mimeTypeForFile(QStringLiteral("filename.") + ext.toLower(), QMimeDatabase::MatchExtension); - if (mimeType.isValid() && !mimeType.isDefault()) - return mimeType.name(); - - return QString(); -} -#endif - -void tst_QWebEnginePage::supportedContentType() -{ -#if !defined(QWEBENGINEPAGE_SUPPORTEDCONTENTTYPES) - QSKIP("QWEBENGINEPAGE_SUPPORTEDCONTENTTYPES"); -#else - QStringList contentTypes; - - // Add supported non image types... - contentTypes << "text/html" << "text/xml" << "text/xsl" << "text/plain" << "text/" - << "application/xml" << "application/xhtml+xml" << "application/vnd.wap.xhtml+xml" - << "application/rss+xml" << "application/atom+xml" << "application/json"; - -#if ENABLE_MHTML - contentTypes << "application/x-mimearchive"; -#endif - - // Add supported image types... - const QList<QByteArray> supportedImageFormats = QImageWriter::supportedImageFormats(); - for (const QByteArray &imageType : supportedImageFormats) { - const QString mimeType = getMimeTypeForExtension(imageType); - if (!mimeType.isEmpty()) - contentTypes << mimeType; - } - - // Get the mime types supported by webengine... - const QStringList supportedContentTypes = m_page->supportedContentTypes(); - - for (const QString &mimeType : qAsConst(contentTypes)) - QVERIFY2(supportedContentTypes.contains(mimeType), QString("'%1' is not a supported content type!").arg(mimeType).toLatin1()); - - for (const QString &mimeType : qAsConst(contentTypes)) - QVERIFY2(m_page->supportsContentType(mimeType), QString("Cannot handle content types '%1'!").arg(mimeType).toLatin1()); -#endif -} - -void tst_QWebEnginePage::thirdPartyCookiePolicy() -{ -#if !defined(DUMPRENDERTREESUPPORTQT) - QSKIP("DUMPRENDERTREESUPPORTQT"); -#else - QWebEngineSettings::globalSettings()->setThirdPartyCookiePolicy(QWebEngineSettings::AlwaysBlockThirdPartyCookies); - m_page->networkAccessManager()->setCookieJar(new QNetworkCookieJar()); - QVERIFY(m_page->networkAccessManager()->cookieJar()); - - // These are all first-party cookies, so should pass. - QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://www.example.com"), QUrl("http://example.com"))); - QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://www.example.com"), QUrl("http://doc.example.com"))); - QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://aaa.www.example.com"), QUrl("http://doc.example.com"))); - QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://example.com"), QUrl("http://www.example.com"))); - QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://www.example.co.uk"), QUrl("http://example.co.uk"))); - QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://www.example.co.uk"), QUrl("http://doc.example.co.uk"))); - QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://aaa.www.example.co.uk"), QUrl("http://doc.example.co.uk"))); - QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://example.co.uk"), QUrl("http://www.example.co.uk"))); - - // These are all third-party cookies, so should fail. - QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://www.example.com"), QUrl("http://slashdot.org"))); - QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://example.com"), QUrl("http://anotherexample.com"))); - QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://anotherexample.com"), QUrl("http://example.com"))); - QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://www.example.co.uk"), QUrl("http://slashdot.co.uk"))); - QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://example.co.uk"), QUrl("http://anotherexample.co.uk"))); - QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(), - QUrl("http://anotherexample.co.uk"), QUrl("http://example.co.uk"))); -#endif -} - static QWindow *findNewTopLevelWindow(const QWindowList &oldTopLevelWindows) { const auto tlws = QGuiApplication::topLevelWindows(); @@ -2078,66 +1095,6 @@ void tst_QWebEnginePage::macCopyUnicodeToClipboard() } #endif -void tst_QWebEnginePage::contextMenuCopy() -{ -#if !defined(QWEBENGINEELEMENT) - QSKIP("QWEBENGINEELEMENT"); -#else - QWebEngineView view; - - view.setHtml("<a href=\"http://www.google.com\">You cant miss this</a>"); - - view.page()->triggerAction(QWebEnginePage::SelectAll); - QVERIFY(!view.page()->selectedText().isEmpty()); - - QWebEngineElement link = view.page()->mainFrame()->findFirstElement("a"); - QPoint pos(link.geometry().center()); - QContextMenuEvent event(QContextMenuEvent::Mouse, pos); - view.page()->swallowContextMenuEvent(&event); - view.page()->updatePositionDependentActions(pos); - - QList<QMenu*> contextMenus = view.findChildren<QMenu*>(); - QVERIFY(!contextMenus.isEmpty()); - QMenu* contextMenu = contextMenus.first(); - QVERIFY(contextMenu); - - QList<QAction *> list = contextMenu->actions(); - int index = list.indexOf(view.page()->action(QWebEnginePage::Copy)); - QVERIFY(index != -1); -#endif -} - -// https://bugs.webkit.org/show_bug.cgi?id=62139 -void tst_QWebEnginePage::contextMenuPopulatedOnce() -{ -#if !defined(QWEBENGINEELEMENT) - QSKIP("QWEBENGINEELEMENT"); -#else - QWebEngineView view; - - view.setHtml("<input type=\"text\">"); - - QWebEngineElement link = view.page()->mainFrame()->findFirstElement("input"); - QPoint pos(link.geometry().center()); - QContextMenuEvent event(QContextMenuEvent::Mouse, pos); - view.page()->swallowContextMenuEvent(&event); - view.page()->updatePositionDependentActions(pos); - - QList<QMenu*> contextMenus = view.findChildren<QMenu*>(); - QVERIFY(!contextMenus.isEmpty()); - QMenu* contextMenu = contextMenus.first(); - QVERIFY(contextMenu); - - QList<QAction *> list = contextMenu->actions(); - QStringList entries; - while (!list.isEmpty()) { - QString entry = list.takeFirst()->text(); - QVERIFY(!entries.contains(entry)); - entries << entry; - } -#endif -} - void tst_QWebEnginePage::deleteQWebEngineViewTwice() { for (int i = 0; i < 2; ++i) { @@ -2151,62 +1108,6 @@ void tst_QWebEnginePage::deleteQWebEngineViewTwice() } } -#if defined(QWEBENGINEPAGE_RENDER) -class RepaintRequestedRenderer : public QObject { - Q_OBJECT -public: - RepaintRequestedRenderer(QWebEnginePage* page, QPainter* painter) - : m_page(page) - , m_painter(painter) - , m_recursionCount(0) - { - connect(m_page, SIGNAL(repaintRequested(QRect)), this, SLOT(onRepaintRequested(QRect))); - } - -Q_SIGNALS: - void finished(); - -private Q_SLOTS: - void onRepaintRequested(const QRect& rect) - { - QCOMPARE(m_recursionCount, 0); - - m_recursionCount++; - m_page->render(m_painter, rect); - m_recursionCount--; - - QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); - } - -private: - QWebEnginePage* m_page; - QPainter* m_painter; - int m_recursionCount; -}; -#endif - -void tst_QWebEnginePage::renderOnRepaintRequestedShouldNotRecurse() -{ -#if !defined(QWEBENGINEPAGE_RENDER) - QSKIP("QWEBENGINEPAGE_RENDER"); -#else - QSize viewportSize(720, 576); - QWebEnginePage page; - - QImage image(viewportSize, QImage::Format_ARGB32); - QPainter painter(&image); - - page.setPreferredContentsSize(viewportSize); - page.setViewportSize(viewportSize); - RepaintRequestedRenderer r(&page, &painter); - - page.setHtml("zalan loves trunk", QUrl()); - - QSignalSpy spyFinished(&r, &RepaintRequestedRenderer::finished); - QVERIFY(spyFinished.wait()); -#endif -} - class SpyForLoadSignalsOrder : public QStateMachine { Q_OBJECT public: @@ -2265,24 +1166,6 @@ void tst_QWebEnginePage::loadSignalsOrder() QTRY_VERIFY(loadSpy.isFinished()); } -void tst_QWebEnginePage::undoActionHaveCustomText() -{ -#if !defined(QWEBENGINEPAGE_UNDOACTION) - QSKIP("QWEBENGINEPAGE_UNDOACTION"); -#else - m_page->setHtml("<div id=test contenteditable></div>"); - evaluateJavaScriptSync(m_page, "document.getElementById('test').focus()"); - - evaluateJavaScriptSync(m_page, "document.execCommand('insertText', true, 'Test');"); - QString typingActionText = m_page->action(QWebEnginePage::Undo)->text(); - - evaluateJavaScriptSync(m_page, "document.execCommand('indent', true);"); - QString alignActionText = m_page->action(QWebEnginePage::Undo)->text(); - - QVERIFY(typingActionText != alignActionText); -#endif -} - void tst_QWebEnginePage::renderWidgetHostViewNotShowTopLevel() { QWebEnginePage page; @@ -2601,68 +1484,6 @@ void tst_QWebEnginePage::openWindowDefaultSize() QCOMPARE(requestedGeometry.height(), 100); } -void tst_QWebEnginePage::cssMediaTypeGlobalSetting() -{ -#if !defined(QWEBENGINESETTINGS_SETCSSMEDIATYPE) - QSKIP("QWEBENGINESETTINGS_SETCSSMEDIATYPE"); -#else - QString testHtml("<style>@media tv {body{background-color:red;}}@media handheld {body{background-color:green;}}@media screen {body{background-color:blue;}}</style>"); - QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool))); - - QWebEngineSettings::globalSettings()->setCSSMediaType("tv"); - // Clear page specific setting to read from global setting - m_view->page()->settings()->setCSSMediaType(QString()); - m_view->setHtml(testHtml); - QTRY_COMPARE(loadSpy.count(), 1); - QVERIFY(evaluateJavaScriptSync(m_view->page(), "window.matchMedia('tv').matches == true").toBool()); - QVERIFY(QWebEngineSettings::globalSettings()->cssMediaType() == "tv"); - - QWebEngineSettings::globalSettings()->setCSSMediaType("handheld"); - // Clear page specific setting to read from global setting - m_view->page()->settings()->setCSSMediaType(QString()); - m_view->setHtml(testHtml); - QTRY_COMPARE(loadSpy.count(), 2); - QVERIFY(evaluateJavaScriptSync(m_view->page(), "window.matchMedia('handheld').matches == true").toBool()); - QVERIFY(QWebEngineSettings::globalSettings()->cssMediaType() == "handheld"); - - QWebEngineSettings::globalSettings()->setCSSMediaType("screen"); - // Clear page specific setting to read from global setting - m_view->page()->settings()->setCSSMediaType(QString()); - m_view->setHtml(testHtml); - QTRY_COMPARE(loadSpy.count(), 3); - QVERIFY(evaluateJavaScriptSync(m_view->page(), "window.matchMedia('screen').matches == true").toBool()); - QVERIFY(QWebEngineSettings::globalSettings()->cssMediaType() == "screen"); -#endif -} - -void tst_QWebEnginePage::cssMediaTypePageSetting() -{ -#if !defined(QWEBENGINESETTINGS_SETCSSMEDIATYPE) - QSKIP("QWEBENGINESETTINGS_SETCSSMEDIATYPE"); -#else - QString testHtml("<style>@media tv {body{background-color:red;}}@media handheld {body{background-color:green;}}@media screen {body{background-color:blue;}}</style>"); - QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool))); - - m_view->page()->settings()->setCSSMediaType("tv"); - m_view->setHtml(testHtml); - QTRY_COMPARE(loadSpy.count(), 1); - QVERIFY(evaluateJavaScriptSync(m_view->page(), "window.matchMedia('tv').matches == true").toBool()); - QVERIFY(m_view->page()->settings()->cssMediaType() == "tv"); - - m_view->page()->settings()->setCSSMediaType("handheld"); - m_view->setHtml(testHtml); - QTRY_COMPARE(loadSpy.count(), 2); - QVERIFY(evaluateJavaScriptSync(m_view->page(), "window.matchMedia('handheld').matches == true").toBool()); - QVERIFY(m_view->page()->settings()->cssMediaType() == "handheld"); - - m_view->page()->settings()->setCSSMediaType("screen"); - m_view->setHtml(testHtml); - QTRY_COMPARE(loadSpy.count(), 3); - QVERIFY(evaluateJavaScriptSync(m_view->page(), "window.matchMedia('screen').matches == true").toBool()); - QVERIFY(m_view->page()->settings()->cssMediaType() == "screen"); -#endif -} - class JavaScriptCallbackBase { public: @@ -3244,55 +2065,6 @@ void tst_QWebEnginePage::setHtmlWithJSAlert() QCOMPARE(toHtmlSync(&page), html); } -void tst_QWebEnginePage::inputFieldFocus() -{ -#if !defined(QWEBENGINEELEMENT) - QSKIP("QWEBENGINEELEMENT"); -#else - QWebEngineView view; - view.setHtml("<html><body><input type=\"text\"></input></body></html>"); - view.resize(400, 100); - view.show(); - QTest::qWaitForWindowExposed(&view); - view.activateWindow(); - view.setFocus(); - QTRY_VERIFY(view.hasFocus()); - - // double the flashing time, should at least blink once already - int delay = qApp->cursorFlashTime() * 2; - - // focus the lineedit and check if it blinks - bool autoSipEnabled = qApp->autoSipEnabled(); - qApp->setAutoSipEnabled(false); - const QWebEngineElement inputElement = view.page()->documentElement().findFirst(QLatin1String("input[type=text]")); - QTest::mouseClick(&view, Qt::LeftButton, 0, inputElement.geometry().center()); - m_inputFieldsTestView = &view; - view.installEventFilter( this ); - QTest::qWait(delay); - QVERIFY2(m_inputFieldTestPaintCount >= 3, - "The input field should have a blinking caret"); - qApp->setAutoSipEnabled(autoSipEnabled); -#endif -} - -void tst_QWebEnginePage::hitTestContent() -{ -#if !defined(QWEBENGINEELEMENT) - QSKIP("QWEBENGINEELEMENT"); -#else - QString html("<html><body><p>A paragraph</p><br/><br/><br/><a href=\"about:blank\" target=\"_foo\" id=\"link\">link text</a></body></html>"); - - QWebEnginePage page; - page.setHtml(html); - page.setViewportSize(QSize(200, 0)); //no height so link is not visible - const QWebEngineElement linkElement = page.documentElement().findFirst(QLatin1String("a#link")); - QWebEngineHitTestResult result = page.hitTestContent(linkElement.geometry().center()); - QCOMPARE(result.linkText(), QString("link text")); - QWebEngineElement link = result.linkElement(); - QCOMPARE(link.attribute("target"), QString("_foo")); -#endif -} - void tst_QWebEnginePage::baseUrl_data() { QTest::addColumn<QString>("html"); @@ -3373,32 +2145,6 @@ void tst_QWebEnginePage::scrollbarsOff() QVERIFY(evaluateJavaScriptSync(view.page(), "innerWidth == document.documentElement.offsetWidth").toBool()); } -void tst_QWebEnginePage::horizontalScrollAfterBack() -{ -#if !defined(QWEBENGINESETTINGS) - QSKIP("QWEBENGINESETTINGS"); -#else - QWebEngineView view; - QSignalSpy loadSpy(view.page(), SIGNAL(loadFinished(bool))); - - view.page()->settings()->setMaximumPagesInCache(2); - view.page()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAsNeeded); - view.page()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAsNeeded); - - view.load(QUrl("qrc:/resources/testiframe2.html")); - view.resize(200, 200); - QTRY_COMPARE(loadSpy.count(), 1); - QTRY_VERIFY((view.page()->scrollBarGeometry(Qt::Horizontal)).height()); - - view.load(QUrl("qrc:/resources/testiframe.html")); - QTRY_COMPARE(loadSpy.count(), 2); - - view.page()->triggerAction(QWebEnginePage::Back); - QTRY_COMPARE(loadSpy.count(), 3); - QTRY_VERIFY((view.page()->scrollBarGeometry(Qt::Horizontal)).height()); -#endif -} - class WebView : public QWebEngineView { Q_OBJECT @@ -3493,35 +2239,6 @@ private: QNetworkRequest::CacheLoadControl m_lastCacheLoad; }; -void tst_QWebEnginePage::setCacheLoadControlAttribute() -{ -#if !defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) - QSKIP("QWEBENGINEPAGE_SETNETWORKACCESSMANAGER"); -#else - QWebEnginePage page; - CacheNetworkAccessManager* manager = new CacheNetworkAccessManager(&page); - page.setNetworkAccessManager(manager); - - QNetworkRequest request(QUrl("http://abcdef.abcdef/")); - - request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysCache); - page.load(request); - QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::AlwaysCache); - - request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); - page.load(request); - QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::PreferCache); - - request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); - page.load(request); - QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::AlwaysNetwork); - - request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork); - page.load(request); - QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::PreferNetwork); -#endif -} - void tst_QWebEnginePage::setUrlWithPendingLoads() { QWebEnginePage page; diff --git a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp index 1fe0f0304..bf5d320b7 100644 --- a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp +++ b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp @@ -29,6 +29,7 @@ #include "../util.h" #include <QtCore/qbuffer.h> #include <QtTest/QtTest> +#include <QtWebEngineCore/qwebengineurlrequestinterceptor.h> #include <QtWebEngineCore/qwebengineurlrequestjob.h> #include <QtWebEngineCore/qwebengineurlschemehandler.h> #include <QtWebEngineWidgets/qwebengineprofile.h> @@ -52,6 +53,7 @@ private Q_SLOTS: void urlSchemeHandlerFailRequest(); void urlSchemeHandlerFailOnRead(); void urlSchemeHandlerStreaming(); + void urlSchemeHandlerRequestHeaders(); void customUserAgent(); void httpAcceptLanguage(); void downloadItem(); @@ -444,6 +446,65 @@ void tst_QWebEngineProfile::urlSchemeHandlerStreaming() QCOMPARE(toPlainTextSync(view.page()), QString::fromLatin1(result)); } +class ExtraHeaderInterceptor : public QWebEngineUrlRequestInterceptor +{ +public: + ExtraHeaderInterceptor() { } + + void setExtraHeader(const QByteArray &key, const QByteArray &value) + { + m_extraKey = key; + m_extraValue = value; + } + + void interceptRequest(QWebEngineUrlRequestInfo &info) override + { + if (info.requestUrl().scheme() == QLatin1String("myscheme")) + info.setHttpHeader(m_extraKey, m_extraValue); + } + + QByteArray m_extraKey; + QByteArray m_extraValue; +}; + +class RequestHeadersUrlSchemeHandler : public ReplyingUrlSchemeHandler +{ +public: + void setExpectedHeader(const QByteArray &key, const QByteArray &value) + { + m_expectedKey = key; + m_expectedValue = value; + } + void requestStarted(QWebEngineUrlRequestJob *job) override + { + const auto requestHeaders = job->requestHeaders(); + QVERIFY(requestHeaders.contains(m_expectedKey)); + QCOMPARE(requestHeaders.value(m_expectedKey), m_expectedValue); + ReplyingUrlSchemeHandler::requestStarted(job); + } + QByteArray m_expectedKey; + QByteArray m_expectedValue; +}; + +void tst_QWebEngineProfile::urlSchemeHandlerRequestHeaders() +{ + RequestHeadersUrlSchemeHandler handler; + ExtraHeaderInterceptor interceptor; + + handler.setExpectedHeader("Hello", "World"); + interceptor.setExtraHeader("Hello", "World"); + + QWebEngineProfile profile; + profile.installUrlSchemeHandler("myscheme", &handler); + profile.setRequestInterceptor(&interceptor); + + QWebEnginePage page(&profile); + QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); + page.load(QUrl(QStringLiteral("myscheme://whatever"))); + QVERIFY(loadFinishedSpy.wait()); +} + + void tst_QWebEngineProfile::customUserAgent() { QString defaultUserAgent = QWebEngineProfile::defaultProfile()->httpUserAgent(); diff --git a/tests/auto/widgets/qwebengineview/BLACKLIST b/tests/auto/widgets/qwebengineview/BLACKLIST index 7c86a72d6..e8758abcc 100644 --- a/tests/auto/widgets/qwebengineview/BLACKLIST +++ b/tests/auto/widgets/qwebengineview/BLACKLIST @@ -6,3 +6,6 @@ osx [textSelectionOutOfInputField] * + +[changeLocale] +* diff --git a/tests/auto/widgets/qwebengineview/resources/image2.png b/tests/auto/widgets/qwebengineview/resources/image2.png Binary files differnew file mode 100644 index 000000000..8d703640c --- /dev/null +++ b/tests/auto/widgets/qwebengineview/resources/image2.png diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp index 3287ca309..f2810101b 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 <QtCore/qregularexpression.h> #define VERIFY_INPUTMETHOD_HINTS(actual, expect) \ QVERIFY(actual == (expect | Qt::ImhNoPredictiveText | Qt::ImhNoTextHandles | Qt::ImhNoEditMenu)); @@ -267,7 +268,7 @@ void tst_QWebEngineView::getWebKitVersion() void tst_QWebEngineView::changePage_data() { QString html = "<html><head><title>%1</title>" - "<link rel='icon' href='file://" TESTS_SOURCE_DIR "/resources/image2.png'></head></html>"; + "<link rel='icon' href='qrc:///resources/image2.png'></head></html>"; QUrl urlFrom("data:text/html," + html.arg("TitleFrom")); QUrl urlTo("data:text/html," + html.arg("TitleTo")); QUrl nullPage("data:text/html,<html/>"); @@ -1180,7 +1181,7 @@ void tst_QWebEngineView::changeLocale() QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpyDE.count(), 1, 20000); QTRY_VERIFY(!toPlainTextSync(viewDE.page()).isEmpty()); - errorLines = toPlainTextSync(viewDE.page()).split(QRegExp("[\r\n]"), QString::SkipEmptyParts); + errorLines = toPlainTextSync(viewDE.page()).split(QRegularExpression("[\r\n]"), QString::SkipEmptyParts); QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("Die Website ist nicht erreichbar")); QLocale::setDefault(QLocale("en")); @@ -1190,7 +1191,7 @@ void tst_QWebEngineView::changeLocale() QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpyEN.count(), 1, 20000); QTRY_VERIFY(!toPlainTextSync(viewEN.page()).isEmpty()); - errorLines = toPlainTextSync(viewEN.page()).split(QRegExp("[\r\n]"), QString::SkipEmptyParts); + errorLines = toPlainTextSync(viewEN.page()).split(QRegularExpression("[\r\n]"), QString::SkipEmptyParts); QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("This site can\xE2\x80\x99t be reached")); // Reset error page @@ -1203,7 +1204,7 @@ void tst_QWebEngineView::changeLocale() QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpyDE.count(), 1, 20000); QTRY_VERIFY(!toPlainTextSync(viewDE.page()).isEmpty()); - errorLines = toPlainTextSync(viewDE.page()).split(QRegExp("[\r\n]"), QString::SkipEmptyParts); + errorLines = toPlainTextSync(viewDE.page()).split(QRegularExpression("[\r\n]"), QString::SkipEmptyParts); QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("Die Website ist nicht erreichbar")); } @@ -2634,7 +2635,7 @@ void tst_QWebEngineView::imeJSInputEvents() view.show(); auto logLines = [&view]() -> QStringList { - return evaluateJavaScriptSync(view.page(), "log.textContent").toString().split("\n").filter(QRegExp(".+")); + return evaluateJavaScriptSync(view.page(), "log.textContent").toString().split("\n").filter(QRegularExpression(".+")); }; QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.qrc b/tests/auto/widgets/qwebengineview/tst_qwebengineview.qrc index 53b11bca8..a09be0399 100644 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.qrc +++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.qrc @@ -5,5 +5,6 @@ <file>resources/input_types.html</file> <file>resources/scrolltest_page.html</file> <file>resources/keyboardEvents.html</file> + <file>resources/image2.png</file> </qresource> </RCC> diff --git a/tests/manual/widgets/inputmethods/testview.cpp b/tests/manual/widgets/inputmethods/testview.cpp index d41734c2d..14e355caf 100644 --- a/tests/manual/widgets/inputmethods/testview.cpp +++ b/tests/manual/widgets/inputmethods/testview.cpp @@ -31,7 +31,7 @@ #include <QDebug> #include <QFile> #include <QPushButton> -#include <QRegExp> +#include <QRegularExpression> #include <QStandardItemModel> #include <QTableView> #include <QTest> @@ -77,15 +77,16 @@ void TestView::loadTestData(const QString &testDataPath) QTextStream testDataStream(&testDataFile); while (!testDataStream.atEnd()) { QString line = testDataStream.readLine(); - QRegExp data("^\"(.*)\"\\s*,\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*,\\s*(\\d+)\\s*,\\s*\"(.*)\"\\s*,\\s*\"(.*)\"\\s*$"); - if (!data.exactMatch(line)) + QRegularExpression data(QRegularExpression::anchoredPattern("^\"(.*)\"\\s*,\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*,\\s*(\\d+)\\s*,\\s*\"(.*)\"\\s*,\\s*\"(.*)\"\\s*$")); + QRegularExpressionMatch match = data.match(line); + if (!match.hasMatch()) continue; QStandardItemModel *model = qobject_cast<QStandardItemModel *>(m_tableView->model()); QList<QStandardItem *> row; - for (int i = 1; i <= data.captureCount(); ++i) - row.append(new QStandardItem(data.cap(i))); + for (int i = 1; i <= match.lastCapturedIndex(); ++i) + row.append(new QStandardItem(match.captured(i))); model->appendRow(row); } diff --git a/tests/manual/widgets/webgl/main.cpp b/tests/manual/widgets/webgl/main.cpp new file mode 100644 index 000000000..364eda8b9 --- /dev/null +++ b/tests/manual/widgets/webgl/main.cpp @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/QDebug> +#include <QtCore/QLoggingCategory> +#include <QtCore/QOperatingSystemVersion> +#include <QtGui/QOpenGLContext> +#include <QtGui/QSurfaceFormat> +#include <QtWidgets/QApplication> +#include <QtWidgets/QDesktopWidget> +#include <QtWidgets/QGroupBox> +#include <QtWidgets/QHBoxLayout> +#include <QtWidgets/QMainWindow> +#include <QtWidgets/QPushButton> +#include <QtWidgets/QVBoxLayout> +#include <QtWebEngineWidgets/QWebEngineView> + +// Manual test for checking if WebGL (1 or 2) works. +// Set environment variable QTWEBENGINE_GL_TYPE to one of the following to try and switch +// the underlying GL implementation (mostly Windows): "desktop", "gles", "gles3", "software". + +class MainWindow : public QMainWindow +{ + Q_OBJECT +public: + explicit MainWindow(QWidget *parent = nullptr); + QSize sizeHint() const; + +private: + QWebEngineView *view = nullptr; +}; + +MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) +{ + QWidget *centralWidget = new QWidget; + + QGroupBox *horizontalGroupBox = new QGroupBox; + QHBoxLayout *buttonLayout = new QHBoxLayout; + QPushButton *exButton1 = new QPushButton(QStringLiteral("Aquarium (WebGL 1)")); + QPushButton *exButton2 = new QPushButton(QStringLiteral("Lots of objects (WebGL 1)")); + QPushButton *exButton3 = new QPushButton(QStringLiteral("Instanced triangles (WebGL 2)")); + + buttonLayout->addWidget(exButton1); + buttonLayout->addWidget(exButton2); + buttonLayout->addWidget(exButton3); + horizontalGroupBox->setLayout(buttonLayout); + + const QUrl exUrl1 = + QUrl(QStringLiteral( + "http://webglsamples.org/aquarium/aquarium.html")); + const QUrl exUrl2 = + QUrl(QStringLiteral( + "http://webglsamples.org/lots-o-objects/lots-o-objects-draw-elements.html")); + const QUrl exUrl3 = + QUrl(QStringLiteral( + "http://webglsamples.org/WebGL2Samples/#transform_feedback_instanced")); + + view = new QWebEngineView; + connect(exButton1, &QPushButton::clicked, view, [=](){ + view->setUrl(exUrl1); + }); + connect(exButton2, &QPushButton::clicked, view, [=](){ + view->setUrl(exUrl2); + }); + connect(exButton3, &QPushButton::clicked, view, [=](){ + view->setUrl(exUrl3); + }); + + QVBoxLayout *centralLayout = new QVBoxLayout; + centralLayout->addWidget(horizontalGroupBox); + centralLayout->addWidget(view, 1); + + centralWidget->setLayout(centralLayout); + setCentralWidget(centralWidget); + + view->setUrl(QUrl(QStringLiteral("http://webglsamples.org/aquarium/aquarium.html"))); + setWindowTitle(tr("WebGL 1 and 2 examples")); +} + +QSize MainWindow::sizeHint() const +{ + const QRect desktopRect = QApplication::desktop()->screenGeometry(); + const QSize size = desktopRect.size() * qreal(0.9); + return size; +} + +bool isWindows() +{ + return QOperatingSystemVersion::currentType() == QOperatingSystemVersion::Windows; +} + +// Easy snippets to copy to command line for testing for UNIX only. +// QTWEBENGINE_GL_TYPE=desktop ./webgl +// QTWEBENGINE_GL_TYPE=gles ./webgl +// QTWEBENGINE_GL_TYPE=gles3 ./webgl +// QTWEBENGINE_GL_TYPE=software ./webgl +int main(int argc, char *argv[]) +{ + // Not all options are relevant for all platforms. + const QString desktopGL = QStringLiteral("desktop"); + const QString angle = QStringLiteral("angle"); // Same as gles really, just an alias. + const QString gles = QStringLiteral("gles"); // ANGLE on Windows. + const QString gles3 = QStringLiteral("gles3"); // ANGLE on Windows. + const QString softwareGL = QStringLiteral("software"); + + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + + QString glType = qEnvironmentVariable("QTWEBENGINE_GL_TYPE"); + if (glType.isEmpty()) { + if (isWindows()) + glType = gles3; + else + glType = desktopGL; + } + + if (glType == desktopGL) { + QApplication::setAttribute(Qt::AA_UseDesktopOpenGL); + qInfo() << QStringLiteral("Trying to use Desktop OpenGL.\n"); + } else if (glType == gles || glType == gles3 || glType == angle) { + QApplication::setAttribute(Qt::AA_UseOpenGLES); + if (glType == gles || glType == angle) + qInfo() << QStringLiteral("Trying to use OpenGL ES 2.\n"); + if (glType == gles3) + qInfo() << QStringLiteral("Trying to use OpenGL ES 3.\n"); + } else if (glType == softwareGL) { + QApplication::setAttribute(Qt::AA_UseSoftwareOpenGL); + qInfo() << QStringLiteral("Trying to use software OpenGL.\n"); + } + + if (glType == gles3) { + // Set OpenGL ES version 3. + QSurfaceFormat format; + format.setSamples(4); + format.setDepthBufferSize(24); + format.setStencilBufferSize(8); + format.setVersion(3, 0); + QSurfaceFormat::setDefaultFormat(format); + } + + QApplication a(argc, argv); + + QLoggingCategory::setFilterRules(QStringLiteral("qt.scenegraph.general=true")); + QOpenGLContext *globalSharedContext = QOpenGLContext::globalShareContext(); + qInfo() << "Global OpenGL context format: " << globalSharedContext->format() << "\n"; + + MainWindow w; + + // Move middle-ish. + const QRect desktopRect = QApplication::desktop()->screenGeometry(); + const QSize pos = desktopRect.size() * qreal(0.1); + w.move(pos.width(), pos.height()); + + w.show(); + + return a.exec(); +} + +#include "main.moc" diff --git a/tests/manual/widgets/webgl/webgl.pro b/tests/manual/widgets/webgl/webgl.pro new file mode 100644 index 000000000..9141d0221 --- /dev/null +++ b/tests/manual/widgets/webgl/webgl.pro @@ -0,0 +1,6 @@ +TEMPLATE = app +TARGET = webgl +QT += core gui widgets webenginewidgets +CONFIG += c++11 +SOURCES += main.cpp + diff --git a/tests/manual/widgets/widgets.pro b/tests/manual/widgets/widgets.pro index f06d3e1c3..34e88f0e3 100644 --- a/tests/manual/widgets/widgets.pro +++ b/tests/manual/widgets/widgets.pro @@ -1,4 +1,5 @@ TEMPLATE= subdirs SUBDIRS += \ - inputmethods + inputmethods \ + webgl diff --git a/tools/scripts/gn_find_mocables.py b/tools/scripts/gn_find_mocables.py index d97dcb534..d1f682456 100644 --- a/tools/scripts/gn_find_mocables.py +++ b/tools/scripts/gn_find_mocables.py @@ -32,10 +32,9 @@ import os mocables = set() includedMocs = set() -dir = sys.argv[1] files = sys.argv[2:] -for f in filter(os.path.isfile, [os.path.join(dir, f) for f in files]): +for f in filter(os.path.isfile, files): inBlockComment = False for line in open(f).readlines(): # Block comments handling diff --git a/tools/scripts/take_snapshot.py b/tools/scripts/take_snapshot.py index 4bf4381cb..4f71185e5 100755 --- a/tools/scripts/take_snapshot.py +++ b/tools/scripts/take_snapshot.py @@ -43,7 +43,7 @@ qtwebengine_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', os.chdir(qtwebengine_root) def isInGitBlacklist(file_path): - # We do need all the gyp files. + # We need all the git files. if ( '.gitignore' in file_path or '.gitmodules' in file_path or '.gitattributes' in file_path @@ -54,33 +54,14 @@ def isInChromiumBlacklist(file_path): # Filter out empty submodule directories. if (os.path.isdir(file_path)): return True - # We do need all the gyp files. - if file_path.endswith('.gyp') or file_path.endswith('.gypi') or file_path.endswith('.isolate'): - return False - # We do need all the gn file. + # We do need all the gn files. if file_path.endswith('.gn') or file_path.endswith('.gni') or file_path.endswith('.typemap') or \ file_path.endswith('.mojom'): return False - if ( '_jni' in file_path - or 'jni_' in file_path - or 'testdata/' in file_path - or '/tests/' in file_path - or ('/test/' in file_path and - not '/webrtc/' in file_path and - not file_path.startswith('net/test/') and - not file_path.endswith('mock_chrome_application_mac.h') and - not file_path.endswith('perftimer.h') and - not file_path.endswith('test-torque.tq') and - not 'ozone' in file_path and - not 'fontconfig_util_linux' in file_path and - not 'core/mojo/test/' in file_path and - not file_path.startswith('extensions/browser/')) - or file_path.endswith('.java') - or file_path.startswith('android_webview') + if (file_path.startswith('android_webview') or file_path.startswith('apps/') or file_path.startswith('ash/') or file_path.startswith('base/android') - or file_path.startswith('breakpad') or file_path.startswith('buildtools/clang_format/script') or file_path.startswith('buildtools/third_party/libc++') or file_path.startswith('buildtools/third_party/libc++abi') @@ -98,6 +79,7 @@ def isInChromiumBlacklist(file_path): not 'media/webrtc/desktop_media_list.h' in file_path and not 'media/webrtc/desktop_streams_registry.' in file_path and not 'browser/net/chrome_mojo_proxy_resolver_factory.' in file_path and + not '/browser/accessibility/' in file_path and not '/browser/custom_handlers/' in file_path and not '/browser/devtools/' in file_path and not '/browser/ui/webui/' in file_path and @@ -122,25 +104,27 @@ def isInChromiumBlacklist(file_path): not file_path.endswith('.grdp') and not file_path.endswith('.json') and not file_path.endswith('chrome_version.rc.version')) - or file_path.startswith('chrome_frame') + or file_path.startswith('chrome_elf') or file_path.startswith('chromecast') or file_path.startswith('chromeos') or file_path.startswith('cloud_print') - or file_path.startswith('components/chrome_apps/') - or file_path.startswith('components/cronet/') - or file_path.startswith('components/drive/') - or file_path.startswith('components/invalidation/') - or file_path.startswith('components/gcm_driver/') - or file_path.startswith('components/nacl/') - or file_path.startswith('components/omnibox/') - or file_path.startswith('components/policy/') - or file_path.startswith('components/proximity_auth/') - or (file_path.startswith('components/resources/terms/') and not file_path.endswith('terms_chromium.html')) - or file_path.startswith('components/rlz/') - or file_path.startswith('components/sync/') and not file_path.endswith('ordinal.h') - or file_path.startswith('components/test/') - or file_path.startswith('components/test_runner/') - or file_path.startswith('components/translate/') + or (file_path.startswith('components/') and ( + file_path.startswith('components/chrome_apps/') + or file_path.startswith('components/cronet/') + or file_path.startswith('components/drive/') + or file_path.startswith('components/invalidation/') + or file_path.startswith('components/gcm_driver/') + or file_path.startswith('components/nacl/') + or file_path.startswith('components/omnibox/') + or file_path.startswith('components/policy/') + or file_path.startswith('components/proximity_auth/') + or (file_path.startswith('components/resources/terms/') and not file_path.endswith('terms_chromium.html')) + or file_path.startswith('components/rlz/') + or file_path.startswith('components/sync/') and not file_path.endswith('ordinal.h') + or file_path.startswith('components/test/') + or file_path.startswith('components/test_runner/') + or file_path.startswith('components/translate/') + )) or file_path.startswith('content/public/android/java') or (file_path.startswith('content/shell') and not file_path.startswith('content/shell/common') and @@ -149,7 +133,7 @@ def isInChromiumBlacklist(file_path): or file_path.startswith('google_update') or file_path.startswith('ios') or file_path.startswith('media/base/android/java') - or file_path.startswith('native_client') + or file_path.startswith('native_client_sdk') or file_path.startswith('net/android/java') or (file_path.startswith('net/data/') and '_unittest/' in file_path) or file_path.startswith('net/data/fuzzer_data/') @@ -157,86 +141,85 @@ def isInChromiumBlacklist(file_path): or file_path.startswith('rlz') or file_path.startswith('testing/android') or file_path.startswith('testing/buildbot') - or file_path.startswith('third_party/WebKit/LayoutTests') - or file_path.startswith('third_party/WebKit/ManualTests') - or file_path.startswith('third_party/WebKit/Source/core/testing/data/') - or file_path.startswith('third_party/WebKit/Source/devtools/devtools-node-modules') - or file_path.startswith('third_party/WebKit/PerformanceTests') - or file_path.startswith('third_party/accessibility-audit') - or file_path.startswith('third_party/afl') - or file_path.startswith('third_party/android_') - or file_path.startswith('third_party/apache-win32') - or file_path.startswith('third_party/apple_sample_code') - or file_path.startswith('third_party/ashmem') - or file_path.startswith('third_party/binutils') - or file_path.startswith('third_party/bison') - or file_path.startswith('third_party/blink/perf_tests/') - or file_path.startswith('third_party/breakpad/src/processor/testdata/') - or file_path.startswith('third_party/boringssl/crypto_test_data.cc') - or file_path.startswith('third_party/boringssl/src/fuzz') - or (file_path.startswith('third_party/cacheinvalidation') and - not file_path.endswith('isolate')) - or file_path.startswith('third_party/catapult') - or file_path.startswith('third_party/chromite') - or file_path.startswith('third_party/cld_2') - or file_path.startswith('third_party/closure_compiler') - or file_path.startswith('third_party/codesighs') - or file_path.startswith('third_party/colorama') - or file_path.startswith('third_party/cygwin') - or file_path.startswith('third_party/cython') - or file_path.startswith('third_party/deqp') - or file_path.startswith('third_party/depot_tools') - or file_path.startswith('third_party/elfutils') - or file_path.startswith('third_party/freetype-android') - or file_path.startswith('third_party/google_input_tools') - or file_path.startswith('third_party/gperf') - or file_path.startswith('third_party/gnu_binutils') - or file_path.startswith('third_party/grpc') - or file_path.startswith('third_party/gtk+') - or file_path.startswith('third_party/google_appengine_cloudstorage') - or file_path.startswith('third_party/google_toolbox_for_mac') - or file_path.startswith('third_party/hunspell_dictionaries') - or (file_path.startswith('third_party/icu') and file_path.endswith('icudtl_dat.S')) - or file_path.startswith('third_party/icu/android') - or file_path.startswith('third_party/icu/ios') - or file_path.startswith('third_party/instrumented_libraries') - or file_path.startswith('third_party/jsr-305') - or file_path.startswith('third_party/junit') - or file_path.startswith('third_party/lcov') - or file_path.startswith('third_party/libphonenumber') - or file_path.startswith('third_party/libaddressinput/src/testdata') - or file_path.startswith('third_party/libaddressinput/src/common/src/test') - or file_path.startswith('third_party/libc++') - or file_path.startswith('third_party/liblouis') - or file_path.startswith('third_party/lighttpd') - or file_path.startswith('third_party/libwebm/source/webm_parser/fuzzing') - or file_path.startswith('third_party/logilab') - or file_path.startswith('third_party/markdown') - or file_path.startswith('third_party/mingw-w64') - or file_path.startswith('third_party/nacl_sdk_binaries') - or (file_path.startswith('third_party/polymer') and - not file_path.startswith('third_party/polymer/v1_0/components-chromium/')) - or file_path.startswith('third_party/openh264/src/res') - or file_path.startswith('third_party/pdfium/testing/resources') - or file_path.startswith('third_party/pdfium/tools') - or file_path.startswith('third_party/pdfsqueeze') - or file_path.startswith('third_party/pefile') - or file_path.startswith('third_party/perl') - or file_path.startswith('third_party/psyco_win32') - or file_path.startswith('third_party/pylint') - or file_path.startswith('third_party/scons-2.0.1') - or file_path.startswith('third_party/sfntly/src/cpp/data/fonts') - or file_path.startswith('third_party/sfntly/src/java') - or file_path.startswith('third_party/skia/infra') - or file_path.startswith('third_party/speech-dispatcher') - or file_path.startswith('third_party/talloc') - or file_path.startswith('third_party/trace-viewer') - or file_path.startswith('third_party/undoview') - or file_path.startswith('third_party/wayland') - or file_path.startswith('third_party/webgl') - or file_path.startswith('third_party/webrtc/resources/') - or file_path.startswith('third_party/webrtc/third_party/boringssl/crypto_test_data.cc') - or file_path.startswith('third_party/webrtc/third_party/boringssl/src/fuzz') + or (file_path.startswith('third_party/') and ( + file_path.startswith('third_party/WebKit/LayoutTests') + or file_path.startswith('third_party/accessibility-audit') + or file_path.startswith('third_party/afl') + or file_path.startswith('third_party/android_') + or file_path.startswith('third_party/apache-win32') + or file_path.startswith('third_party/apple_sample_code') + or file_path.startswith('third_party/ashmem') + or file_path.startswith('third_party/binutils') + or file_path.startswith('third_party/bison') + or file_path.startswith('third_party/blink/perf_tests/') + or file_path.startswith('third_party/breakpad/src/processor/testdata/') + or file_path.startswith('third_party/boringssl/crypto_test_data.cc') + or file_path.startswith('third_party/boringssl/src/fuzz') + or (file_path.startswith('third_party/cacheinvalidation') and + not file_path.endswith('isolate')) + or file_path.startswith('third_party/catapult') + or file_path.startswith('third_party/chromite') + or file_path.startswith('third_party/cld_2') + or file_path.startswith('third_party/closure_compiler') + or file_path.startswith('third_party/codesighs') + or file_path.startswith('third_party/colorama') + or file_path.startswith('third_party/cygwin') + or file_path.startswith('third_party/cython') + or file_path.startswith('third_party/deqp') + or file_path.startswith('third_party/depot_tools') + or file_path.startswith('third_party/elfutils') + or file_path.startswith('third_party/freetype-android') + or file_path.startswith('third_party/google_input_tools') + or file_path.startswith('third_party/gperf') + or file_path.startswith('third_party/gnu_binutils') + or file_path.startswith('third_party/grpc') + or file_path.startswith('third_party/gtk+') + or file_path.startswith('third_party/google_appengine_cloudstorage') + or file_path.startswith('third_party/google_toolbox_for_mac') + or file_path.startswith('third_party/hunspell_dictionaries') + or (file_path.startswith('third_party/icu') and file_path.endswith('icudtl_dat.S')) + or file_path.startswith('third_party/icu/android') + or file_path.startswith('third_party/icu/ios') + or file_path.startswith('third_party/instrumented_libraries') + or file_path.startswith('third_party/jsr-305') + or file_path.startswith('third_party/junit') + or file_path.startswith('third_party/lcov') + or file_path.startswith('third_party/libphonenumber') + or file_path.startswith('third_party/libaddressinput/src/testdata') + or file_path.startswith('third_party/libaddressinput/src/common/src/test') + or file_path.startswith('third_party/libc++') + or file_path.startswith('third_party/liblouis') + or file_path.startswith('third_party/lighttpd') + or file_path.startswith('third_party/libwebm/source/webm_parser/fuzzing') + or file_path.startswith('third_party/logilab') + or file_path.startswith('third_party/markdown') + or file_path.startswith('third_party/mingw-w64') + or file_path.startswith('third_party/nacl_sdk_binaries') + or (file_path.startswith('third_party/polymer') and + not file_path.startswith('third_party/polymer/v1_0/components-chromium/')) + or file_path.startswith('third_party/openh264/src/res') + or file_path.startswith('third_party/pdfium/testing/resources') + or file_path.startswith('third_party/pdfium/tools') + or file_path.startswith('third_party/pdfsqueeze') + or file_path.startswith('third_party/pefile') + or file_path.startswith('third_party/perl') + or file_path.startswith('third_party/psyco_win32') + or file_path.startswith('third_party/pylint') + or file_path.startswith('third_party/scons-2.0.1') + or file_path.startswith('third_party/sfntly/src/cpp/data/fonts') + or file_path.startswith('third_party/sfntly/src/java') + or file_path.startswith('third_party/skia/infra') + or file_path.startswith('third_party/speech-dispatcher') + or file_path.startswith('third_party/swiftshader/third_party/llvm') + or file_path.startswith('third_party/talloc') + or file_path.startswith('third_party/trace-viewer') + or file_path.startswith('third_party/undoview') + or file_path.startswith('third_party/wayland') + or file_path.startswith('third_party/webgl') + or file_path.startswith('third_party/webrtc/resources/') + or file_path.startswith('third_party/webrtc/third_party/boringssl/crypto_test_data.cc') + or file_path.startswith('third_party/webrtc/third_party/boringssl/src/fuzz') + )) or file_path.startswith('tools/android') or file_path.startswith('tools/luci_go') or file_path.startswith('tools/memory_inspector') @@ -250,7 +233,19 @@ def isInChromiumBlacklist(file_path): or file_path.startswith('ui/events/ozone/chromeos') or file_path.startswith('ui/file_manager') or file_path.startswith('ui/gfx/chromeos') - + or 'testdata/' in file_path + or '/tests/' in file_path + or ('/test/' in file_path and + not '/webrtc/' in file_path and + not file_path.startswith('net/test/') and + not file_path.endswith('mock_chrome_application_mac.h') and + not file_path.endswith('perftimer.h') and + not file_path.endswith('test-torque.tq') and + not 'ozone' in file_path and + not 'clang_coverage' in file_path and + not 'fontconfig_util_linux' in file_path and + not 'core/mojo/test/' in file_path and + not file_path.startswith('extensions/browser/')) ): return True return False @@ -342,6 +337,7 @@ def exportChromium(): files = listFilesInCurrentRepository() # Add LASTCHANGE files which are not tracked by git. files.append('build/util/LASTCHANGE') + files.append('build/util/LASTCHANGE.committime') files.append('skia/ext/skia_commit_hash.h') files.append('gpu/config/gpu_lists_version.h') print 'copying files to ' + third_party_chromium diff --git a/tools/scripts/version_resolver.py b/tools/scripts/version_resolver.py index 27062fbcf..32c3f9d12 100644 --- a/tools/scripts/version_resolver.py +++ b/tools/scripts/version_resolver.py @@ -38,8 +38,8 @@ import json import urllib2 import git_submodule as GitSubmodule -chromium_version = '69.0.3497.113' -chromium_branch = '3497' +chromium_version = '71.0.3578.93' +chromium_branch = '3578' ninja_version = 'v1.8.2' json_url = 'http://omahaproxy.appspot.com/all.json' @@ -51,7 +51,6 @@ upstream_src_dir = os.path.abspath(snapshot_src_dir + '_upstream') submodule_blacklist = [ 'third_party/WebKit/LayoutTests/w3c/csswg-test' , 'third_party/WebKit/LayoutTests/w3c/web-platform-tests' - , 'third_party/jsoncpp/source' , 'chrome/tools/test/reference_build/chrome_mac' , 'chrome/tools/test/reference_build/chrome_linux' , 'chrome/tools/test/reference_build/chrome_win' |