diff options
author | Kari Oikarinen <kari.oikarinen@qt.io> | 2019-09-04 12:12:51 +0300 |
---|---|---|
committer | Kari Oikarinen <kari.oikarinen@qt.io> | 2019-09-04 13:15:42 +0300 |
commit | 705210a03a8fa1ab4366e269bff2a1d6691a912b (patch) | |
tree | 8b278bdb97f667ce54e3b36b71e4b6b8c7f49df3 | |
parent | 48c85451adf1d530bccf9bfb542abb3211f0bf36 (diff) | |
parent | ff1272de6bb44dda30b90c62806d037ca4ef8b77 (diff) | |
download | qtwebengine-705210a03a8fa1ab4366e269bff2a1d6691a912b.tar.gz |
Merge "Merge dev into 5.14 (delayed final downmerge)"
51 files changed, 2641 insertions, 182 deletions
diff --git a/examples/webengine/minimal/main.cpp b/examples/webengine/minimal/main.cpp index 9db6ea6aa..8bcd0e0e9 100644 --- a/examples/webengine/minimal/main.cpp +++ b/examples/webengine/minimal/main.cpp @@ -56,6 +56,7 @@ int main(int argc, char *argv[]) { QCoreApplication::setOrganizationName("QtExamples"); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); QGuiApplication app(argc, argv); QtWebEngine::initialize(); diff --git a/examples/webengine/quicknanobrowser/BrowserWindow.qml b/examples/webengine/quicknanobrowser/BrowserWindow.qml index 088b23e59..39a13df59 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.10 +import QtWebEngine 1.11 ApplicationWindow { id: browserWindow @@ -73,6 +73,10 @@ ApplicationWindow { // Make sure the Qt.WindowFullscreenButtonHint is set on OS X. Component.onCompleted: flags = flags | Qt.WindowFullscreenButtonHint + onCurrentWebViewChanged: { + findBar.reset(); + } + // Create a styleItem to determine the platform. // When using style "mac", ToolButtons are not supposed to accept focus. QQCPrivate.StyleItem { id: styleItem } @@ -136,6 +140,9 @@ ApplicationWindow { fullScreenNotification.hide(); currentWebView.triggerWebAction(WebEngineView.ExitFullScreen); } + + if (findBar.visible) + findBar.visible = false; } } Action { @@ -187,6 +194,21 @@ ApplicationWindow { shortcut: StandardKey.Forward onTriggered: currentWebView.triggerWebAction(WebEngineView.Forward) } + Action { + shortcut: StandardKey.Find + onTriggered: { + if (!findBar.visible) + findBar.visible = true; + } + } + Action { + shortcut: StandardKey.FindNext + onTriggered: findBar.findNext() + } + Action { + shortcut: StandardKey.FindPrevious + onTriggered: findBar.findPrevious() + } toolBar: ToolBar { id: navigationBar @@ -553,6 +575,19 @@ ApplicationWindow { selection.certificates[0].select(); } + onFindTextFinished: function(result) { + if (!findBar.visible) + findBar.visible = true; + + findBar.numberOfMatches = result.numberOfMatches; + findBar.activeMatchOrdinal = result.activeMatchOrdinal; + } + + onLoadingChanged: function(loadRequest) { + if (loadRequest.status == WebEngineView.LoadStartedStatus) + findBar.reset(); + } + Timer { id: reloadTimer interval: 0 @@ -625,6 +660,28 @@ ApplicationWindow { download.accept(); } + FindBar { + id: findBar + visible: false + anchors.right: parent.right + anchors.rightMargin: 10 + anchors.top: parent.top + + onFindNext: { + if (text) + currentWebView && currentWebView.findText(text); + else if (!visible) + visible = true; + } + onFindPrevious: { + if (text) + currentWebView && currentWebView.findText(text, WebEngineView.FindBackward); + else if (!visible) + visible = true; + } + } + + Rectangle { id: statusBubble color: "oldlace" diff --git a/examples/webengine/quicknanobrowser/FindBar.qml b/examples/webengine/quicknanobrowser/FindBar.qml new file mode 100644 index 000000000..2d673592a --- /dev/null +++ b/examples/webengine/quicknanobrowser/FindBar.qml @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE: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.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.0 + +Rectangle { + id: root + + property int numberOfMatches: 0 + property int activeMatchOrdinal: 0 + property alias text: findTextField.text + + function reset() { + numberOfMatches = 0; + activeMatchOrdinal = 0; + visible = false; + } + + signal findNext() + signal findPrevious() + + width: 250 + height: 35 + radius: 2 + + border.width: 1 + border.color: "black" + color: "white" + + onVisibleChanged: { + if (visible) + findTextField.forceActiveFocus(); + } + + + RowLayout { + anchors.fill: parent + anchors.topMargin: 5 + anchors.bottomMargin: 5 + anchors.leftMargin: 10 + anchors.rightMargin: 10 + + spacing: 5 + + Rectangle { + Layout.fillWidth: true + Layout.fillHeight: true + + TextField { + id: findTextField + anchors.fill: parent + + style: TextFieldStyle { + background: Rectangle { + color: "transparent" + } + } + + onAccepted: root.findNext() + onTextChanged: root.findNext() + onActiveFocusChanged: activeFocus ? selectAll() : deselect() + } + } + + Label { + text: activeMatchOrdinal + "/" + numberOfMatches + visible: findTextField.text != "" + } + + Rectangle { + border.width: 1 + border.color: "#ddd" + width: 2 + height: parent.height + anchors.topMargin: 5 + anchors.bottomMargin: 5 + } + + ToolButton { + text: "<" + enabled: numberOfMatches > 0 + onClicked: root.findPrevious() + } + + ToolButton { + text: ">" + enabled: numberOfMatches > 0 + onClicked: root.findNext() + } + + ToolButton { + text: "x" + onClicked: root.visible = false + } + } +} diff --git a/examples/webengine/quicknanobrowser/quicknanobrowser.pro b/examples/webengine/quicknanobrowser/quicknanobrowser.pro index 5a27f5fd4..922cf79e2 100644 --- a/examples/webengine/quicknanobrowser/quicknanobrowser.pro +++ b/examples/webengine/quicknanobrowser/quicknanobrowser.pro @@ -10,6 +10,7 @@ OTHER_FILES += ApplicationRoot.qml \ BrowserDialog.qml \ BrowserWindow.qml \ DownloadView.qml \ + FindBar.qml \ FullScreenNotification.qml RESOURCES += resources.qrc diff --git a/examples/webengine/quicknanobrowser/resources.qrc b/examples/webengine/quicknanobrowser/resources.qrc index c6270897d..50ea05f5e 100644 --- a/examples/webengine/quicknanobrowser/resources.qrc +++ b/examples/webengine/quicknanobrowser/resources.qrc @@ -4,6 +4,7 @@ <file>BrowserDialog.qml</file> <file>BrowserWindow.qml</file> <file>DownloadView.qml</file> + <file>FindBar.qml</file> <file>FullScreenNotification.qml</file> </qresource> <qresource prefix="icons"> diff --git a/examples/webenginewidgets/simplebrowser/browserwindow.cpp b/examples/webenginewidgets/simplebrowser/browserwindow.cpp index 5d00cd19a..2bb9045b0 100644 --- a/examples/webenginewidgets/simplebrowser/browserwindow.cpp +++ b/examples/webenginewidgets/simplebrowser/browserwindow.cpp @@ -66,6 +66,7 @@ #include <QStatusBar> #include <QToolBar> #include <QVBoxLayout> +#include <QWebEngineFindTextResult> #include <QWebEngineProfile> BrowserWindow::BrowserWindow(Browser *browser, QWebEngineProfile *profile, bool forDevTools) @@ -129,6 +130,7 @@ BrowserWindow::BrowserWindow(Browser *browser, QWebEngineProfile *profile, bool connect(m_urlLineEdit, &QLineEdit::returnPressed, [this]() { m_tabWidget->setUrl(QUrl::fromUserInput(m_urlLineEdit->text())); }); + connect(m_tabWidget, &TabWidget::findTextFinished, this, &BrowserWindow::handleFindTextFinished); QAction *focusUrlLineEditAction = new QAction(this); addAction(focusUrlLineEditAction); @@ -460,10 +462,7 @@ void BrowserWindow::handleFindActionTriggered() m_lastSearch, &ok); if (ok && !search.isEmpty()) { m_lastSearch = search; - currentTab()->findText(m_lastSearch, 0, [this](bool found) { - if (!found) - statusBar()->showMessage(tr("\"%1\" not found.").arg(m_lastSearch)); - }); + currentTab()->findText(m_lastSearch); } } @@ -526,3 +525,14 @@ void BrowserWindow::handleDevToolsRequested(QWebEnginePage *source) source->setDevToolsPage(m_browser->createDevToolsWindow()->currentTab()->page()); source->triggerAction(QWebEnginePage::InspectElement); } + +void BrowserWindow::handleFindTextFinished(const QWebEngineFindTextResult &result) +{ + if (result.numberOfMatches() == 0) { + statusBar()->showMessage(tr("\"%1\" not found.").arg(m_lastSearch)); + } else { + statusBar()->showMessage(tr("\"%1\" found: %2/%3").arg(m_lastSearch, + QString::number(result.activeMatchOrdinal()), + QString::number(result.numberOfMatches()))); + } +} diff --git a/examples/webenginewidgets/simplebrowser/browserwindow.h b/examples/webenginewidgets/simplebrowser/browserwindow.h index 8f328b751..11a655469 100644 --- a/examples/webenginewidgets/simplebrowser/browserwindow.h +++ b/examples/webenginewidgets/simplebrowser/browserwindow.h @@ -88,6 +88,7 @@ private slots: void handleWebViewTitleChanged(const QString &title); void handleWebActionEnabledChanged(QWebEnginePage::WebAction action, bool enabled); void handleDevToolsRequested(QWebEnginePage *source); + void handleFindTextFinished(const QWebEngineFindTextResult &result); private: QMenu *createFileMenu(TabWidget *tabWidget); diff --git a/examples/webenginewidgets/simplebrowser/tabwidget.cpp b/examples/webenginewidgets/simplebrowser/tabwidget.cpp index 369bebfd9..3b6d84ebe 100644 --- a/examples/webenginewidgets/simplebrowser/tabwidget.cpp +++ b/examples/webenginewidgets/simplebrowser/tabwidget.cpp @@ -200,6 +200,10 @@ void TabWidget::setupView(WebView *webView) closeTab(index); }); connect(webView, &WebView::devToolsRequested, this, &TabWidget::devToolsRequested); + connect(webPage, &QWebEnginePage::findTextFinished, [this, webView](const QWebEngineFindTextResult &result) { + if (currentIndex() == indexOf(webView)) + emit findTextFinished(result); + }); } WebView *TabWidget::createTab() diff --git a/examples/webenginewidgets/simplebrowser/tabwidget.h b/examples/webenginewidgets/simplebrowser/tabwidget.h index bf83781df..fba61d44f 100644 --- a/examples/webenginewidgets/simplebrowser/tabwidget.h +++ b/examples/webenginewidgets/simplebrowser/tabwidget.h @@ -78,6 +78,7 @@ signals: void favIconChanged(const QIcon &icon); void webActionEnabledChanged(QWebEnginePage::WebAction action, bool enabled); void devToolsRequested(QWebEnginePage *source); + void findTextFinished(const QWebEngineFindTextResult &result); public slots: // current tab/page slots diff --git a/src/3rdparty b/src/3rdparty -Subproject f5613a4bc321972b8f72654d4c4bc9ba0c36ffb +Subproject feccbb4ea7fa685dcd013f5a3f6c14ea768636c diff --git a/src/core/api/core_api.pro b/src/core/api/core_api.pro index 326d4481f..5e8b8387e 100644 --- a/src/core/api/core_api.pro +++ b/src/core/api/core_api.pro @@ -37,6 +37,7 @@ HEADERS = \ qtwebenginecoreglobal_p.h \ qwebenginecookiestore.h \ qwebenginecookiestore_p.h \ + qwebenginefindtextresult.h \ qwebenginehttprequest.h \ qwebenginemessagepumpscheduler_p.h \ qwebenginenotification.h \ @@ -53,6 +54,7 @@ SOURCES = \ qtwebenginecoreglobal.cpp \ qwebengineclientcertificatestore.cpp \ qwebenginecookiestore.cpp \ + qwebenginefindtextresult.cpp \ qwebenginehttprequest.cpp \ qwebenginemessagepumpscheduler.cpp \ qwebenginenotification.cpp \ diff --git a/src/core/api/qwebenginefindtextresult.cpp b/src/core/api/qwebenginefindtextresult.cpp new file mode 100644 index 000000000..ce1be359e --- /dev/null +++ b/src/core/api/qwebenginefindtextresult.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE: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 "qwebenginefindtextresult.h" + +QT_BEGIN_NAMESPACE + +class QWebEngineFindTextResultPrivate : public QSharedData { +public: + int numberOfMatches = 0; + int activeMatchOrdinal = 0; +}; + +/*! + \class QWebEngineFindTextResult + \brief The QWebEngineFindTextResult class encapsulates the result of a string search on a page. + \since 5.14 + + \inmodule QtWebEngineCore + + Results are passed to the user in the + \l QWebEnginePage::findTextFinished() and + \l{WebEngineView::findTextFinished()}{WebEngineView.findTextFinished()} signals. +*/ + +/*! \internal +*/ +QWebEngineFindTextResult::QWebEngineFindTextResult() + : d(new QWebEngineFindTextResultPrivate) +{} + +/*! \internal +*/ +QWebEngineFindTextResult::QWebEngineFindTextResult(int numberOfMatches, int activeMatchOrdinal) + : d(new QWebEngineFindTextResultPrivate) +{ + d->numberOfMatches = numberOfMatches; + d->activeMatchOrdinal = activeMatchOrdinal; +} + +/*! \internal +*/ +QWebEngineFindTextResult::QWebEngineFindTextResult(const QWebEngineFindTextResult &other) + : d(other.d) +{} + +/*! \internal +*/ +QWebEngineFindTextResult &QWebEngineFindTextResult::operator=(const QWebEngineFindTextResult &other) +{ + d = other.d; + return *this; +} + +/*! \internal +*/ +QWebEngineFindTextResult::~QWebEngineFindTextResult() +{} + +/*! + \property QWebEngineFindTextResult::numberOfMatches + \brief The number of matches found. +*/ +int QWebEngineFindTextResult::numberOfMatches() const +{ + return d->numberOfMatches; +} + +/*! + \property QWebEngineFindTextResult::activeMatchOrdinal + \brief The index of the currently highlighted match. +*/ +int QWebEngineFindTextResult::activeMatchOrdinal() const +{ + return d->activeMatchOrdinal; +} + +QT_END_NAMESPACE + +#include "moc_qwebenginefindtextresult.cpp" diff --git a/src/core/api/qwebenginefindtextresult.h b/src/core/api/qwebenginefindtextresult.h new file mode 100644 index 000000000..073a8135f --- /dev/null +++ b/src/core/api/qwebenginefindtextresult.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWEBENGINEFINDTEXTRESULT_H +#define QWEBENGINEFINDTEXTRESULT_H + +#include <QtWebEngineCore/qtwebenginecoreglobal.h> +#include <QtCore/QObject> +#include <QtCore/QSharedData> + +namespace QtWebEngineCore { +class FindTextHelper; +} + +QT_BEGIN_NAMESPACE + +class QWebEngineFindTextResultPrivate; + +class Q_WEBENGINECORE_EXPORT QWebEngineFindTextResult { + Q_GADGET + Q_PROPERTY(int numberOfMatches READ numberOfMatches CONSTANT FINAL) + Q_PROPERTY(int activeMatchOrdinal READ activeMatchOrdinal CONSTANT FINAL) + +public: + int numberOfMatches() const; + int activeMatchOrdinal() const; + + QWebEngineFindTextResult(); + QWebEngineFindTextResult(const QWebEngineFindTextResult &other); + QWebEngineFindTextResult &operator=(const QWebEngineFindTextResult &other); + ~QWebEngineFindTextResult(); + +private: + QWebEngineFindTextResult(int numberOfMatches, int activeMatchOrdinal); + + QSharedDataPointer<QWebEngineFindTextResultPrivate> d; + + friend class QtWebEngineCore::FindTextHelper; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QWebEngineFindTextResult) + +#endif // QWEBENGINEFINDTEXTRESULT_H diff --git a/src/core/compositor/compositor_resource_fence.cpp b/src/core/compositor/compositor_resource_fence.cpp index 7fc5fbfb2..4179395d6 100644 --- a/src/core/compositor/compositor_resource_fence.cpp +++ b/src/core/compositor/compositor_resource_fence.cpp @@ -146,12 +146,12 @@ void CompositorResourceFence::release() } // static -scoped_refptr<CompositorResourceFence> CompositorResourceFence::create() +scoped_refptr<CompositorResourceFence> CompositorResourceFence::create(std::unique_ptr<gl::GLFence> glFence) { - if (gl::GLContext::GetCurrent() && gl::GLFence::IsSupported()) { - std::unique_ptr<gl::GLFence> glFence{gl::GLFence::Create()}; + if (!glFence && gl::GLContext::GetCurrent() && gl::GLFence::IsSupported()) + glFence = gl::GLFence::Create(); + if (glFence) return base::MakeRefCounted<CompositorResourceFence>(glFence->Transfer()); - } return nullptr; } diff --git a/src/core/compositor/compositor_resource_fence.h b/src/core/compositor/compositor_resource_fence.h index 1c2ea3695..196297f78 100644 --- a/src/core/compositor/compositor_resource_fence.h +++ b/src/core/compositor/compositor_resource_fence.h @@ -60,7 +60,7 @@ public: void release(); // May be used only by GPU thread. - static scoped_refptr<CompositorResourceFence> create(); + static scoped_refptr<CompositorResourceFence> create(std::unique_ptr<gl::GLFence> glFence = nullptr); private: gl::TransferableFence m_sync; diff --git a/src/core/compositor/display_consumer.h b/src/core/compositor/display_consumer.h new file mode 100644 index 000000000..d220088ad --- /dev/null +++ b/src/core/compositor/display_consumer.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DISPLAY_CONSUMER_H +#define DISPLAY_CONSUMER_H + +#include "qtwebenginecoreglobal_p.h" + +namespace QtWebEngineCore { + +// Receives composited frames for display. +class DisplayConsumer +{ +public: + // Schedule a call to updatePaintNode soon. + // + // Called on the consumer thread. + virtual void scheduleUpdate() = 0; + +protected: + ~DisplayConsumer() {} +}; + +} // namespace QtWebEngineCore + +#endif // !DISPLAY_CONSUMER_H diff --git a/src/core/compositor/display_frame_sink.cpp b/src/core/compositor/display_frame_sink.cpp new file mode 100644 index 000000000..945600299 --- /dev/null +++ b/src/core/compositor/display_frame_sink.cpp @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "display_frame_sink.h" + +#include <QMap> + +namespace QtWebEngineCore { + +namespace { + +class DisplayFrameSinkMap +{ +public: + static DisplayFrameSinkMap *instance() + { + static DisplayFrameSinkMap map; + return ↦ + } + + scoped_refptr<DisplayFrameSink> findOrCreate(viz::FrameSinkId frameSinkId) + { + QMutexLocker locker(&m_mutex); + auto it = m_map.find(frameSinkId); + if (it == m_map.end()) + it = m_map.insert(frameSinkId, new DisplayFrameSink(frameSinkId)); + return *it; + } + + void remove(viz::FrameSinkId frameSinkId) + { + QMutexLocker locker(&m_mutex); + m_map.remove(frameSinkId); + } + +private: + mutable QMutex m_mutex; + QMap<viz::FrameSinkId, DisplayFrameSink *> m_map; +}; + +} // namespace + +// static +scoped_refptr<DisplayFrameSink> DisplayFrameSink::findOrCreate(viz::FrameSinkId frameSinkId) +{ + return DisplayFrameSinkMap::instance()->findOrCreate(frameSinkId); +} + +DisplayFrameSink::DisplayFrameSink(viz::FrameSinkId frameSinkId) + : m_frameSinkId(frameSinkId) +{ + DCHECK(m_frameSinkId.is_valid()); +} + +DisplayFrameSink::~DisplayFrameSink() +{ + DisplayFrameSinkMap::instance()->remove(m_frameSinkId); +} + +void DisplayFrameSink::connect(DisplayConsumer *consumer) +{ + QMutexLocker locker(&m_mutex); + DCHECK(m_consumer == nullptr); + m_consumer = consumer; +} + +void DisplayFrameSink::connect(DisplayProducer *producer) +{ + QMutexLocker locker(&m_mutex); + DCHECK(m_producer == nullptr); + m_producer = producer; +} + +void DisplayFrameSink::disconnect(DisplayConsumer *consumer) +{ + QMutexLocker locker(&m_mutex); + DCHECK(m_consumer == consumer); + m_consumer = nullptr; +} + +void DisplayFrameSink::disconnect(DisplayProducer *producer) +{ + QMutexLocker locker(&m_mutex); + DCHECK(m_producer == producer); + m_producer = nullptr; +} + +void DisplayFrameSink::scheduleUpdate() +{ + QMutexLocker locker(&m_mutex); + if (m_consumer) + m_consumer->scheduleUpdate(); +} + +QSGNode *DisplayFrameSink::updatePaintNode(QSGNode *oldNode, RenderWidgetHostViewQtDelegate *delegate) +{ + QMutexLocker locker(&m_mutex); + QSGNode *newNode = oldNode; + if (m_producer) + newNode = m_producer->updatePaintNode(oldNode, delegate); + return newNode; +} + +} // namespace QtWebEngineCore diff --git a/src/core/compositor/display_frame_sink.h b/src/core/compositor/display_frame_sink.h new file mode 100644 index 000000000..f620dc4f2 --- /dev/null +++ b/src/core/compositor/display_frame_sink.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DISPLAY_FRAME_SINK_H +#define DISPLAY_FRAME_SINK_H + +#include "display_consumer.h" +#include "display_producer.h" + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_refptr.h" +#include "components/viz/common/surfaces/frame_sink_id.h" + +#include <QMutex> + +namespace QtWebEngineCore { + +// Connects a DisplayConsumer with a DisplayProducer. +class DisplayFrameSink final : public base::RefCountedThreadSafe<DisplayFrameSink> +{ +public: + static scoped_refptr<DisplayFrameSink> findOrCreate(viz::FrameSinkId frameSinkId); + DisplayFrameSink(viz::FrameSinkId frameSinkId); + ~DisplayFrameSink(); + void connect(DisplayConsumer *consumer); + void connect(DisplayProducer *producer); + void disconnect(DisplayConsumer *consumer); + void disconnect(DisplayProducer *producer); + void scheduleUpdate(); + QSGNode *updatePaintNode(QSGNode *oldNode, RenderWidgetHostViewQtDelegate *delegate); + +private: + const viz::FrameSinkId m_frameSinkId; + mutable QMutex m_mutex; + DisplayProducer *m_producer = nullptr; + DisplayConsumer *m_consumer = nullptr; +}; + +} // namespace QtWebEngineCore + +#endif // !DISPLAY_FRAME_SINK_H diff --git a/src/core/compositor/display_gl_output_surface.cpp b/src/core/compositor/display_gl_output_surface.cpp new file mode 100644 index 000000000..5a78b8322 --- /dev/null +++ b/src/core/compositor/display_gl_output_surface.cpp @@ -0,0 +1,292 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "display_gl_output_surface.h" + +#include "chromium_gpu_helper.h" + +#include "base/threading/thread_task_runner_handle.h" +#include "components/viz/service/display/display.h" +#include "components/viz/service/display/output_surface_frame.h" +#include "gpu/command_buffer/client/gles2_implementation.h" +#include "gpu/command_buffer/client/gles2_interface.h" +#include "gpu/command_buffer/service/mailbox_manager.h" +#include "gpu/command_buffer/service/texture_base.h" +#include "gpu/ipc/in_process_command_buffer.h" +#include "ui/gl/color_space_utils.h" + +namespace QtWebEngineCore { + +DisplayGLOutputSurface::DisplayGLOutputSurface( + scoped_refptr<viz::VizProcessContextProvider> contextProvider, + viz::UpdateVSyncParametersCallback callback) + : OutputSurface(contextProvider) + , m_commandBuffer(contextProvider->command_buffer()) + , m_gl(contextProvider->ContextGL()) +{ + capabilities_.uses_default_gl_framebuffer = false; + m_gl->GenFramebuffers(1, &m_fboId); + contextProvider->SetUpdateVSyncParametersCallback(std::move(callback)); +} + +DisplayGLOutputSurface::~DisplayGLOutputSurface() +{ + m_gl->DeleteFramebuffers(1, &m_fboId); + if (m_sink) + m_sink->disconnect(this); +} + +// Called from viz::Display::Initialize. +void DisplayGLOutputSurface::BindToClient(viz::OutputSurfaceClient *client) +{ + m_display = static_cast<viz::Display *>(client); + m_sink = DisplayFrameSink::findOrCreate(m_display->frame_sink_id()); + m_sink->connect(this); +} + +// Triggered by ui::Compositor::SetVisible(true). +void DisplayGLOutputSurface::EnsureBackbuffer() +{ +} + +// Triggered by ui::Compositor::SetVisible(false). Framebuffer must be cleared. +void DisplayGLOutputSurface::DiscardBackbuffer() +{ + NOTIMPLEMENTED(); + // m_gl->DiscardBackbufferCHROMIUM(); +} + +// Called from viz::DirectRenderer::DrawFrame before rendering starts, but only +// if the parameters differ from the previous Reshape call. +// +// Parameters: +// +// - sizeInPixels comes from ui::Compositor::SetScaleAndSize via +// viz::HostContextFactoryPrivate::ResizeDisplay. +// +// - devicePixelRatio comes from viz::CompositorFrame::device_scale_factor() +// via viz::RootCompositorFrameSinkImpl::SubmitCompositorFrame and +// viz::Display::SetLocalSurfaceId. +// +// - colorSpace and hasAlpha correspond to the color_space and +// has_transparent_background properties of the root viz::RenderPass. +// +// - useStencil should create a stencil buffer, but this is only needed for +// overdraw feedback (--show-overdraw-feedback), so it's safe to ignore. +// Accordingly, capabilities_.supports_stencil should be set to false. +// +void DisplayGLOutputSurface::Reshape(const gfx::Size &sizeInPixels, + float devicePixelRatio, + const gfx::ColorSpace &colorSpace, + bool hasAlpha, + bool /*useStencil*/) +{ + m_currentShape = Shape{sizeInPixels, devicePixelRatio, colorSpace, hasAlpha}; + m_gl->ResizeCHROMIUM(sizeInPixels.width(), sizeInPixels.height(), devicePixelRatio, + gl::ColorSpaceUtils::GetGLColorSpace(colorSpace), hasAlpha); +} + +std::unique_ptr<DisplayGLOutputSurface::Buffer> DisplayGLOutputSurface::makeBuffer(const Shape &shape) +{ + std::unique_ptr<Buffer> buffer = std::make_unique<Buffer>(this); + buffer->shape = shape; + m_gl->GenTextures(1, &buffer->clientId); + m_gl->BindTexture(GL_TEXTURE_2D, buffer->clientId); + m_gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + m_gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + m_gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + m_gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + uint32_t width = shape.sizeInPixels.width(); + uint32_t height = shape.sizeInPixels.height(); + uint32_t format = shape.hasAlpha ? GL_RGBA : GL_RGB; + m_gl->TexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, nullptr); + return buffer; +} + +void DisplayGLOutputSurface::deleteBufferResources(Buffer *buffer) +{ + m_gl->DeleteTextures(1, &buffer->clientId); +} + +// Called by viz::GLRenderer during rendering whenever it switches to the root +// render pass. +void DisplayGLOutputSurface::BindFramebuffer() +{ + if (!m_backBuffer || m_backBuffer->shape != m_currentShape) + m_backBuffer = makeBuffer(m_currentShape); + + m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_fboId); + m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_backBuffer->clientId, 0); +} + +// Called from viz::Display::DrawAndSwap after rendering. +// +// Parameters: +// +// - frame.size is the same as the size given to Reshape. +// +// - frame.sub_buffer_rect and frame.content_bounds are never used since these +// are only enabled if gl::GLSurface::SupportsPostSubBuffer() or +// gl::GLSurface::SupportsSwapBuffersWithBounds() are true, respectively, +// but this not the case for any offscreen gl::GLSurface. +// +// - frame.latency_info is viz::CompositorFrame::metadata.latency_info. +void DisplayGLOutputSurface::SwapBuffers(viz::OutputSurfaceFrame frame) +{ + DCHECK(frame.size == m_currentShape.sizeInPixels); + DCHECK(!frame.sub_buffer_rect.has_value()); + DCHECK(frame.content_bounds.empty()); + DCHECK(m_backBuffer); + + m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_fboId); + m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + gpu::SyncToken syncToken; + m_gl->GenSyncTokenCHROMIUM(syncToken.GetData()); + + unsigned int clientId = m_backBuffer->clientId; + + // Now some thread-hopping: + // + // - We start here on the viz thread (client side of command buffer). + // + // - Then we'll jump to the gpu thread (service side of command buffer) to + // get the real OpenGL texture id. + // + // - Then we'll get a call from the Qt Quick Scene Graph thread (could be + // a separate thread or the main thread). + // + // - Finally we'll return to the viz thread to acknowledge the swap. + + { + QMutexLocker locker(&m_mutex); + m_taskRunner = base::ThreadTaskRunnerHandle::Get(); + m_middleBuffer = std::move(m_backBuffer); + m_middleBuffer->serviceId = 0; + } + + m_commandBuffer->GetTextureQt( + clientId, + base::BindOnce(&DisplayGLOutputSurface::swapBuffersOnGpuThread, base::Unretained(this)), + std::vector<gpu::SyncToken>{syncToken}); +} + +void DisplayGLOutputSurface::swapBuffersOnGpuThread(unsigned int id, std::unique_ptr<gl::GLFence> fence) +{ + { + QMutexLocker locker(&m_mutex); + m_middleBuffer->serviceId = id; + m_middleBuffer->fence = CompositorResourceFence::create(std::move(fence)); + } + + m_sink->scheduleUpdate(); +} + +void DisplayGLOutputSurface::swapBuffersOnVizThread() +{ + { + QMutexLocker locker(&m_mutex); + m_backBuffer = std::move(m_middleBuffer); + } + + m_display->DidReceiveSwapBuffersAck(); + m_display->DidReceivePresentationFeedback( + gfx::PresentationFeedback(base::TimeTicks::Now(), base::TimeDelta(), + gfx::PresentationFeedback::Flags::kVSync)); +} + +void DisplayGLOutputSurface::SetDrawRectangle(const gfx::Rect &) +{ +} + +// Returning nullptr here effectively disables viz::OverlayProcessor. +viz::OverlayCandidateValidator *DisplayGLOutputSurface::GetOverlayCandidateValidator() const +{ + return nullptr; +} + +// Returning true here will cause viz::GLRenderer to try to render the output +// surface as an overlay plane (see viz::DirectRenderer::DrawFrame and +// viz::GLRenderer::ScheduleOverlays). +bool DisplayGLOutputSurface::IsDisplayedAsOverlayPlane() const +{ + return false; +} + +// Only used if IsDisplayedAsOverlayPlane was true (called from +// viz::GLRenderer::ScheduleOverlays). +unsigned DisplayGLOutputSurface::GetOverlayTextureId() const +{ + return 0; +} + +// Only used if IsDisplayedAsOverlayPlane was true (called from +// viz::DirectRender::DrawFrame). +gfx::BufferFormat DisplayGLOutputSurface::GetOverlayBufferFormat() const +{ + return gfx::BufferFormat(); +} + +// Called by viz::GLRenderer but always false in all implementations except for +// android_webview::ParentOutputSurface. +bool DisplayGLOutputSurface::HasExternalStencilTest() const +{ + return false; +} + +// Only called if HasExternalStencilTest was true. Dead code? +void DisplayGLOutputSurface::ApplyExternalStencil() +{ + NOTREACHED(); +} + +// Called from GLRenderer::GetFramebufferCopyTextureFormat when using +// glCopyTexSubImage2D on our framebuffer. +uint32_t DisplayGLOutputSurface::GetFramebufferCopyTextureFormat() +{ + return GL_RGBA; +} + +// Called from viz::DirectRenderer::DrawFrame, only used for overlays. +unsigned DisplayGLOutputSurface::UpdateGpuFence() +{ + NOTREACHED(); + return 0; +} + +} // namespace QtWebEngineCore diff --git a/src/core/compositor/display_gl_output_surface.h b/src/core/compositor/display_gl_output_surface.h new file mode 100644 index 000000000..c42a6630a --- /dev/null +++ b/src/core/compositor/display_gl_output_surface.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DISPLAY_GL_OUTPUT_SURFACE_H +#define DISPLAY_GL_OUTPUT_SURFACE_H + +#include "compositor_resource_fence.h" +#include "display_frame_sink.h" + +#include "components/viz/common/display/update_vsync_parameters_callback.h" +#include "components/viz/service/display/output_surface.h" +#include "components/viz/service/display_embedder/viz_process_context_provider.h" +#include "gpu/command_buffer/common/mailbox.h" +#include "gpu/command_buffer/common/sync_token.h" + +namespace viz { +class Display; +class SyntheticBeginFrameSource; +} // namespace viz + +namespace QtWebEngineCore { + +// NOTE: Some methods are defined in display_gl_output_surface_qsg.cpp due +// to conflicts between Qt & Chromium OpenGL APIs. +class DisplayGLOutputSurface final : public viz::OutputSurface, public DisplayProducer +{ +public: + DisplayGLOutputSurface( + scoped_refptr<viz::VizProcessContextProvider> contextProvider, + viz::UpdateVSyncParametersCallback callback); + ~DisplayGLOutputSurface() override; + + // Overridden from viz::OutputSurface. + void BindToClient(viz::OutputSurfaceClient *client) override; + void EnsureBackbuffer() override; + void DiscardBackbuffer() override; + void BindFramebuffer() override; + void SetDrawRectangle(const gfx::Rect &drawRect) override; + viz::OverlayCandidateValidator *GetOverlayCandidateValidator() const override; + bool IsDisplayedAsOverlayPlane() const override; + unsigned GetOverlayTextureId() const override; + gfx::BufferFormat GetOverlayBufferFormat() const override; + void Reshape(const gfx::Size &size, + float devicePixelRatio, + const gfx::ColorSpace &colorSpace, + bool hasAlpha, + bool useStencil) override; + bool HasExternalStencilTest() const override; + void ApplyExternalStencil() override; + uint32_t GetFramebufferCopyTextureFormat() override; + void SwapBuffers(viz::OutputSurfaceFrame frame) override; + unsigned UpdateGpuFence() override; + + // Overridden from DisplayProducer. + QSGNode *updatePaintNode(QSGNode *oldNode, RenderWidgetHostViewQtDelegate *delegate) override; + +private: + struct Shape + { + gfx::Size sizeInPixels; + float devicePixelRatio; + gfx::ColorSpace colorSpace; + bool hasAlpha; + + bool operator==(const Shape &that) const + { + return (sizeInPixels == that.sizeInPixels && + devicePixelRatio == that.devicePixelRatio && + colorSpace == that.colorSpace && + hasAlpha == that.hasAlpha); + } + bool operator!=(const Shape &that) const { return !(*this == that); } + }; + + struct Buffer + { + DisplayGLOutputSurface *parent; + Shape shape; + uint32_t clientId = 0; + uint32_t serviceId = 0; + scoped_refptr<CompositorResourceFence> fence; + + Buffer(DisplayGLOutputSurface *parent) : parent(parent) {} + ~Buffer() { parent->deleteBufferResources(this); } + }; + + class Texture; + + void swapBuffersOnGpuThread(unsigned int id, std::unique_ptr<gl::GLFence> fence); + void swapBuffersOnVizThread(); + + std::unique_ptr<Buffer> makeBuffer(const Shape &shape); + void deleteBufferResources(Buffer *buffer); + void attachBuffer(); + void detachBuffer(); + + gpu::InProcessCommandBuffer *const m_commandBuffer; + gpu::gles2::GLES2Interface *const m_gl; + mutable QMutex m_mutex; + uint32_t m_fboId = 0; + viz::Display *m_display = nullptr; + scoped_refptr<DisplayFrameSink> m_sink; + Shape m_currentShape; + std::unique_ptr<Buffer> m_backBuffer; + std::unique_ptr<Buffer> m_middleBuffer; + std::unique_ptr<Buffer> m_frontBuffer; + scoped_refptr<base::SingleThreadTaskRunner> m_taskRunner; +}; + +} // namespace QtWebEngineCore + +#endif // !DISPLAY_GL_OUTPUT_SURFACE_H diff --git a/src/core/compositor/display_gl_output_surface_qsg.cpp b/src/core/compositor/display_gl_output_surface_qsg.cpp new file mode 100644 index 000000000..2f7b3de84 --- /dev/null +++ b/src/core/compositor/display_gl_output_surface_qsg.cpp @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "display_gl_output_surface.h" + +#include "compositor_resource_fence.h" +#include "render_widget_host_view_qt_delegate.h" +#include "type_conversion.h" + +#include <QOpenGLFunctions> +#include <QSGImageNode> +#include <QSGTexture> + +namespace QtWebEngineCore { + +class DisplayGLOutputSurface::Texture final : public QSGTexture +{ +public: + Texture(uint32_t id, QSize sizeInPixels, bool hasAlphaChannel, scoped_refptr<CompositorResourceFence> fence) + : m_id(id) + , m_sizeInPixels(sizeInPixels) + , m_hasAlphaChannel(hasAlphaChannel) + , m_fence(std::move(fence)) + { + } + + // QSGTexture: + int textureId() const override { return m_id; } + QSize textureSize() const override { return m_sizeInPixels; } + bool hasAlphaChannel() const override { return m_hasAlphaChannel; } + bool hasMipmaps() const override { return false; } + void bind() override + { + if (m_fence) { + m_fence->wait(); + m_fence.reset(); + } + + QOpenGLContext *context = QOpenGLContext::currentContext(); + QOpenGLFunctions *funcs = context->functions(); + funcs->glBindTexture(GL_TEXTURE_2D, m_id); + } + +private: + uint32_t m_id; + QSize m_sizeInPixels; + bool m_hasAlphaChannel; + scoped_refptr<CompositorResourceFence> m_fence; +}; + +QSGNode *DisplayGLOutputSurface::updatePaintNode(QSGNode *oldNode, RenderWidgetHostViewQtDelegate *delegate) +{ + { + QMutexLocker locker(&m_mutex); + if (m_middleBuffer && m_middleBuffer->serviceId) { + std::swap(m_middleBuffer, m_frontBuffer); + m_taskRunner->PostTask( + FROM_HERE, + base::BindOnce(&DisplayGLOutputSurface::swapBuffersOnVizThread, base::Unretained(this))); + m_taskRunner.reset(); + } + } + + if (!m_frontBuffer) + return oldNode; + + auto node = static_cast<QSGImageNode *>(oldNode); + if (!node) + node = delegate->createImageNode(); + + QSize sizeInPixels = toQt(m_frontBuffer->shape.sizeInPixels); + QSizeF sizeInDips = QSizeF(sizeInPixels) / m_frontBuffer->shape.devicePixelRatio; + QRectF rectInDips(QPointF(0, 0), sizeInDips); + node->setRect(rectInDips); + node->setOwnsTexture(true); + node->setTexture(new Texture(m_frontBuffer->serviceId, + sizeInPixels, + m_frontBuffer->shape.hasAlpha, + m_frontBuffer->fence)); + node->setTextureCoordinatesTransform(QSGImageNode::MirrorVertically); + + return node; +} + +} // namespace QtWebEngineCore diff --git a/src/core/compositor/display_overrides.cpp b/src/core/compositor/display_overrides.cpp new file mode 100644 index 000000000..4547bb04b --- /dev/null +++ b/src/core/compositor/display_overrides.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "display_gl_output_surface.h" +#include "display_software_output_surface.h" + +#include "components/viz/service/display_embedder/gpu_display_provider.h" +#include "gpu/ipc/in_process_command_buffer.h" + +std::unique_ptr<viz::OutputSurface> +viz::GpuDisplayProvider::CreateGLOutputSurface( + scoped_refptr<VizProcessContextProvider> context_provider, + UpdateVSyncParametersCallback update_vsync_callback) +{ + return std::make_unique<QtWebEngineCore::DisplayGLOutputSurface>( + std::move(context_provider), std::move(update_vsync_callback)); +} + +std::unique_ptr<viz::OutputSurface> +viz::GpuDisplayProvider::CreateSoftwareOutputSurface( + UpdateVSyncParametersCallback update_vsync_callback) +{ + return std::make_unique<QtWebEngineCore::DisplaySoftwareOutputSurface>(std::move(update_vsync_callback)); +} + +void gpu::InProcessCommandBuffer::GetTextureQt( + unsigned int client_id, + GetTextureCallback callback, + const std::vector<SyncToken>& sync_token_fences) +{ + ScheduleGpuTask(base::BindOnce(&InProcessCommandBuffer::GetTextureQtOnGpuThread, + gpu_thread_weak_ptr_factory_.GetWeakPtr(), + client_id, + std::move(callback)), + sync_token_fences); +} + +void gpu::InProcessCommandBuffer::GetTextureQtOnGpuThread( + unsigned int client_id, GetTextureCallback callback) +{ + MakeCurrent(); + gpu::TextureBase *texture = decoder_->GetTextureBase(client_id); + std::move(callback).Run(texture ? texture->service_id() : 0, gl::GLFence::Create()); +} diff --git a/src/core/compositor/display_producer.h b/src/core/compositor/display_producer.h new file mode 100644 index 000000000..5de09d2d2 --- /dev/null +++ b/src/core/compositor/display_producer.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DISPLAY_PRODUCER_H +#define DISPLAY_PRODUCER_H + +#include "qtwebenginecoreglobal_p.h" + +QT_BEGIN_NAMESPACE +class QSGNode; +QT_END_NAMESPACE + +namespace QtWebEngineCore { +class RenderWidgetHostViewQtDelegate; + +// Produces composited frames for display. +class DisplayProducer +{ +public: + // Generate scene graph nodes for the current frame. + // + // If this is a scheduled update (that is, scheduleUpdate was called + // earlier), then updatePaintNode will generate nodes for a new frame. + // Otherwise, it will just regenerate nodes for the old frame. + virtual QSGNode *updatePaintNode(QSGNode *oldNode, RenderWidgetHostViewQtDelegate *delegate) = 0; + +protected: + ~DisplayProducer() {} +}; + +} // namespace QtWebEngineCore + +#endif // !DISPLAY_PRODUCER_H diff --git a/src/core/compositor/display_software_output_surface.cpp b/src/core/compositor/display_software_output_surface.cpp new file mode 100644 index 000000000..13f8e8f38 --- /dev/null +++ b/src/core/compositor/display_software_output_surface.cpp @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "display_software_output_surface.h" + +#include "display_frame_sink.h" +#include "render_widget_host_view_qt_delegate.h" +#include "type_conversion.h" + +#include "base/threading/thread_task_runner_handle.h" +#include "components/viz/service/display/display.h" +#include "components/viz/service/display/output_surface_frame.h" + +#include <QMutex> +#include <QPainter> +#include <QSGImageNode> + +namespace QtWebEngineCore { + +class DisplaySoftwareOutputSurface::Device final : public viz::SoftwareOutputDevice, public DisplayProducer +{ +public: + ~Device(); + + // Called from DisplaySoftwareOutputSurface. + void bind(viz::FrameSinkId frameSinkId); + + // Overridden from viz::SoftwareOutputDevice. + void Resize(const gfx::Size &sizeInPixels, float devicePixelRatio) override; + void OnSwapBuffers(base::OnceClosure swapCompletionCallback) override; + + // Overridden from DisplayProducer. + QSGNode *updatePaintNode(QSGNode *oldNode, RenderWidgetHostViewQtDelegate *delegate) override; + +private: + mutable QMutex m_mutex; + scoped_refptr<DisplayFrameSink> m_sink; + float m_devicePixelRatio = 1.0; + scoped_refptr<base::SingleThreadTaskRunner> m_taskRunner; + base::OnceClosure m_swapCompletionCallback; + QImage m_image; + float m_imageDevicePixelRatio = 1.0; +}; + +DisplaySoftwareOutputSurface::Device::~Device() +{ + if (m_sink) + m_sink->disconnect(this); +} + +void DisplaySoftwareOutputSurface::Device::bind(viz::FrameSinkId frameSinkId) +{ + m_sink = DisplayFrameSink::findOrCreate(frameSinkId); + m_sink->connect(this); +} + +void DisplaySoftwareOutputSurface::Device::Resize(const gfx::Size &sizeInPixels, float devicePixelRatio) +{ + if (viewport_pixel_size_ == sizeInPixels && m_devicePixelRatio == devicePixelRatio) + return; + m_devicePixelRatio = devicePixelRatio; + viewport_pixel_size_ = sizeInPixels; + surface_ = SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(sizeInPixels.width(), sizeInPixels.height())); +} + +void DisplaySoftwareOutputSurface::Device::OnSwapBuffers(base::OnceClosure swapCompletionCallback) +{ + QMutexLocker locker(&m_mutex); + m_taskRunner = base::ThreadTaskRunnerHandle::Get(); + m_swapCompletionCallback = std::move(swapCompletionCallback); + m_sink->scheduleUpdate(); +} + +inline QImage::Format imageFormat(SkColorType colorType) +{ + switch (colorType) { + case kBGRA_8888_SkColorType: + return QImage::Format_ARGB32_Premultiplied; + case kRGBA_8888_SkColorType: + return QImage::Format_RGBA8888_Premultiplied; + default: + Q_UNREACHABLE(); + return QImage::Format_ARGB32_Premultiplied; + } +} + +QSGNode *DisplaySoftwareOutputSurface::Device::updatePaintNode( + QSGNode *oldNode, RenderWidgetHostViewQtDelegate *delegate) +{ + QMutexLocker locker(&m_mutex); + + // Delete old node to make sure refcount of m_image is at most 1. + delete oldNode; + QSGImageNode *node = delegate->createImageNode(); + + if (m_swapCompletionCallback) { + SkPixmap skPixmap; + surface_->peekPixels(&skPixmap); + QImage image(reinterpret_cast<const uchar *>(skPixmap.addr()), + viewport_pixel_size_.width(), viewport_pixel_size_.height(), + skPixmap.rowBytes(), imageFormat(skPixmap.colorType())); + if (m_image.size() == image.size()) { + QRect damageRect = toQt(damage_rect_); + QPainter(&m_image).drawImage(damageRect, image, damageRect); + } else { + m_image = image; + m_image.detach(); + } + m_imageDevicePixelRatio = m_devicePixelRatio; + m_taskRunner->PostTask(FROM_HERE, std::move(m_swapCompletionCallback)); + m_taskRunner.reset(); + } + + QSizeF sizeInDips = QSizeF(m_image.size()) / m_imageDevicePixelRatio; + node->setRect(QRectF(QPointF(0, 0), sizeInDips)); + node->setOwnsTexture(true); + node->setTexture(delegate->createTextureFromImage(m_image)); + + return node; +} + +DisplaySoftwareOutputSurface::DisplaySoftwareOutputSurface(viz::UpdateVSyncParametersCallback callback) + : SoftwareOutputSurface(std::make_unique<Device>(), std::move(callback)) +{} + +DisplaySoftwareOutputSurface::~DisplaySoftwareOutputSurface() {} + +// Called from viz::Display::Initialize. +void DisplaySoftwareOutputSurface::BindToClient(viz::OutputSurfaceClient *client) +{ + auto display = static_cast<viz::Display *>(client); + auto device = static_cast<Device *>(software_device()); + device->bind(display->frame_sink_id()); + SoftwareOutputSurface::BindToClient(client); +} + +} // namespace QtWebEngineCore diff --git a/src/core/compositor/display_software_output_surface.h b/src/core/compositor/display_software_output_surface.h new file mode 100644 index 000000000..6707c74dc --- /dev/null +++ b/src/core/compositor/display_software_output_surface.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DISPLAY_SOFTWARE_OUTPUT_SURFACE_H +#define DISPLAY_SOFTWARE_OUTPUT_SURFACE_H + +#include "components/viz/service/display_embedder/software_output_surface.h" + +namespace QtWebEngineCore { + +class DisplaySoftwareOutputSurface final : public viz::SoftwareOutputSurface +{ +public: + DisplaySoftwareOutputSurface(viz::UpdateVSyncParametersCallback callback); + ~DisplaySoftwareOutputSurface() override; + + // Overridden from viz::SoftwareOutputSurface. + void BindToClient(viz::OutputSurfaceClient *client) override; + +private: + class Device; +}; + +} // namespace QtWebEngineCore + +#endif // !DISPLAY_SOFTWARE_OUTPUT_SURFACE_H diff --git a/src/core/core_chromium.pri b/src/core/core_chromium.pri index 908387788..810ec9a0f 100644 --- a/src/core/core_chromium.pri +++ b/src/core/core_chromium.pri @@ -59,16 +59,21 @@ SOURCES = \ compositor/compositor_resource_tracker.cpp \ compositor/content_gpu_client_qt.cpp \ compositor/delegated_frame_node.cpp \ + compositor/display_frame_sink.cpp \ + compositor/display_overrides.cpp \ + compositor/display_software_output_surface.cpp \ content_client_qt.cpp \ content_browser_client_qt.cpp \ content_main_delegate_qt.cpp \ content_utility_client_qt.cpp \ + delegated_frame_host_client_qt.cpp \ desktop_screen_qt.cpp \ devtools_frontend_qt.cpp \ devtools_manager_delegate_qt.cpp \ download_manager_delegate_qt.cpp \ favicon_manager.cpp \ file_picker_controller.cpp \ + find_text_helper.cpp \ javascript_dialog_controller.cpp \ javascript_dialog_manager_qt.cpp \ login_delegate_qt.cpp \ @@ -163,16 +168,20 @@ HEADERS = \ compositor/compositor_resource_tracker.h \ compositor/content_gpu_client_qt.h \ compositor/delegated_frame_node.h \ + compositor/display_frame_sink.h \ + compositor/display_software_output_surface.h \ content_client_qt.h \ content_browser_client_qt.h \ content_main_delegate_qt.h \ content_utility_client_qt.h \ + delegated_frame_host_client_qt.h \ desktop_screen_qt.h \ devtools_frontend_qt.h \ devtools_manager_delegate_qt.h \ download_manager_delegate_qt.h \ favicon_manager.h \ file_picker_controller.h \ + find_text_helper.h \ global_descriptors_qt.h \ javascript_dialog_controller_p.h \ javascript_dialog_controller.h \ @@ -292,11 +301,13 @@ qtConfig(webengine-printing-and-pdf) { contains(QT_CONFIG, opengl) { SOURCES += \ compositor/compositor_resource_fence.cpp \ + compositor/display_gl_output_surface.cpp \ + compositor/display_gl_output_surface_qsg.cpp \ compositor/stream_video_node.cpp \ compositor/yuv_video_node.cpp - HEADERS += \ compositor/compositor_resource_fence.h \ + compositor/display_gl_output_surface.h \ compositor/stream_video_node.h \ compositor/yuv_video_node.h } diff --git a/src/core/delegated_frame_host_client_qt.cpp b/src/core/delegated_frame_host_client_qt.cpp new file mode 100644 index 000000000..d3f5a4ade --- /dev/null +++ b/src/core/delegated_frame_host_client_qt.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "delegated_frame_host_client_qt.h" + +#include "render_widget_host_view_qt.h" + +namespace QtWebEngineCore { + +ui::Layer *DelegatedFrameHostClientQt::DelegatedFrameHostGetLayer() const +{ + return p->m_rootLayer.get(); +} + +bool DelegatedFrameHostClientQt::DelegatedFrameHostIsVisible() const +{ + return !p->host()->is_hidden(); +} + +SkColor DelegatedFrameHostClientQt::DelegatedFrameHostGetGutterColor() const +{ + return p->GetBackgroundColor().value_or(SK_ColorWHITE); +} + +void DelegatedFrameHostClientQt::OnBeginFrame(base::TimeTicks frame_time) +{ + p->host()->ProgressFlingIfNeeded(frame_time); +} + +void DelegatedFrameHostClientQt::OnFrameTokenChanged(uint32_t frame_token) +{ + p->OnFrameTokenChangedForView(frame_token); +} + +float DelegatedFrameHostClientQt::GetDeviceScaleFactor() const +{ + return p->m_screenInfo.device_scale_factor; +} + +void DelegatedFrameHostClientQt::InvalidateLocalSurfaceIdOnEviction() +{ + p->m_dfhLocalSurfaceIdAllocator.Invalidate(); +} + +std::vector<viz::SurfaceId> DelegatedFrameHostClientQt::CollectSurfaceIdsForEviction() +{ + return p->host()->CollectSurfaceIdsForEviction(); +} + +bool DelegatedFrameHostClientQt::ShouldShowStaleContentOnEviction() +{ + return p->host()->ShouldShowStaleContentOnEviction(); +} + +} // namespace QtWebEngineCore diff --git a/src/core/delegated_frame_host_client_qt.h b/src/core/delegated_frame_host_client_qt.h new file mode 100644 index 000000000..b5dc6eb59 --- /dev/null +++ b/src/core/delegated_frame_host_client_qt.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DELEGATED_FRAME_HOST_CLIENT_QT_H +#define DELEGATED_FRAME_HOST_CLIENT_QT_H + +#include "qtwebenginecoreglobal_p.h" + +#include "content/browser/renderer_host/delegated_frame_host.h" +#include "content/browser/renderer_host/render_widget_host_impl.h" + +namespace QtWebEngineCore { + +class RenderWidgetHostViewQt; +class DelegatedFrameHostClientQt : public content::DelegatedFrameHostClient +{ +public: + explicit DelegatedFrameHostClientQt(RenderWidgetHostViewQt *p) : p(p) {} + + // Overridden from content::DelegatedFrameHostClient + ui::Layer *DelegatedFrameHostGetLayer() const override; + bool DelegatedFrameHostIsVisible() const override; + SkColor DelegatedFrameHostGetGutterColor() const override; + void OnBeginFrame(base::TimeTicks frame_time) override; + void OnFrameTokenChanged(uint32_t frame_token) override; + float GetDeviceScaleFactor() const override; + void InvalidateLocalSurfaceIdOnEviction() override; + std::vector<viz::SurfaceId> CollectSurfaceIdsForEviction() override; + bool ShouldShowStaleContentOnEviction() override; + +private: + RenderWidgetHostViewQt *p; +}; + +} // namespace QtWebEngineCore + +#endif // !DELEGATED_FRAME_HOST_CLIENT_QT_H diff --git a/src/core/favicon_manager.h b/src/core/favicon_manager.h index df74f6303..a8247d15f 100644 --- a/src/core/favicon_manager.h +++ b/src/core/favicon_manager.h @@ -90,7 +90,7 @@ public: TouchIcon = 1 << 1, TouchPrecomposedIcon = 1 << 2 }; - Q_DECLARE_FLAGS(FaviconTypeFlags, FaviconTypeFlag); + Q_DECLARE_FLAGS(FaviconTypeFlags, FaviconTypeFlag) FaviconInfo(); FaviconInfo(const FaviconInfo &); diff --git a/src/core/find_text_helper.cpp b/src/core/find_text_helper.cpp new file mode 100644 index 000000000..effda529f --- /dev/null +++ b/src/core/find_text_helper.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "find_text_helper.h" +#include "qwebenginefindtextresult.h" +#include "type_conversion.h" +#include "web_contents_adapter_client.h" + +#include "content/public/browser/web_contents.h" +#include "third_party/blink/public/mojom/frame/find_in_page.mojom.h" + +namespace QtWebEngineCore { + +// static +int FindTextHelper::m_findRequestIdCounter = -1; + +FindTextHelper::FindTextHelper(content::WebContents *webContents, WebContentsAdapterClient *viewClient) + : m_webContents(webContents) + , m_viewClient(viewClient) + , m_currentFindRequestId(m_findRequestIdCounter++) + , m_lastCompletedFindRequestId(m_currentFindRequestId) +{ +} + +FindTextHelper::~FindTextHelper() +{ + if (isFindTextInProgress()) + stopFinding(); +} + +void FindTextHelper::startFinding(const QString &findText, bool caseSensitively, bool findBackward, const QWebEngineCallback<bool> resultCallback) +{ + if (findText.isEmpty()) { + stopFinding(); + m_viewClient->findTextFinished(QWebEngineFindTextResult()); + m_widgetCallbacks.invokeEmpty(resultCallback); + return; + } + + startFinding(findText, caseSensitively, findBackward); + m_widgetCallbacks.registerCallback(m_currentFindRequestId, resultCallback); +} + +void FindTextHelper::startFinding(const QString &findText, bool caseSensitively, bool findBackward, const QJSValue &resultCallback) +{ + if (findText.isEmpty()) { + stopFinding(); + m_viewClient->findTextFinished(QWebEngineFindTextResult()); + if (!resultCallback.isUndefined()) { + QJSValueList args; + args.append(QJSValue(0)); + const_cast<QJSValue&>(resultCallback).call(args); + } + return; + } + + startFinding(findText, caseSensitively, findBackward); + if (!resultCallback.isUndefined()) + m_quickCallbacks.insert(m_currentFindRequestId, resultCallback); +} + +void FindTextHelper::startFinding(const QString &findText, bool caseSensitively, bool findBackward) +{ + if (findText.isEmpty()) { + stopFinding(); + return; + } + + if (m_currentFindRequestId > m_lastCompletedFindRequestId) { + // There are cases where the render process will overwrite a previous request + // with the new search and we'll have a dangling callback, leaving the application + // waiting for it forever. + // Assume that any unfinished find has been unsuccessful when a new one is started + // to cover that case. + m_viewClient->findTextFinished(QWebEngineFindTextResult()); + invokeResultCallback(m_currentFindRequestId, 0); + } + + blink::mojom::FindOptionsPtr options = blink::mojom::FindOptions::New(); + options->forward = !findBackward; + options->match_case = caseSensitively; + options->find_next = findText == m_previousFindText; + m_previousFindText = findText; + + m_currentFindRequestId = m_findRequestIdCounter++; + m_webContents->Find(m_currentFindRequestId, toString16(findText), std::move(options)); +} + +void FindTextHelper::stopFinding() +{ + m_lastCompletedFindRequestId = m_currentFindRequestId; + m_previousFindText = QString(); + m_webContents->StopFinding(content::STOP_FIND_ACTION_KEEP_SELECTION); +} + +bool FindTextHelper::isFindTextInProgress() const +{ + return m_currentFindRequestId != m_lastCompletedFindRequestId; +} + +void FindTextHelper::handleFindReply(content::WebContents *source, int requestId, int numberOfMatches, + const gfx::Rect &selectionRect, int activeMatchOrdinal, bool finalUpdate) +{ + Q_UNUSED(selectionRect); + + Q_ASSERT(source == m_webContents); + + if (!finalUpdate || requestId <= m_lastCompletedFindRequestId) + return; + + Q_ASSERT(m_currentFindRequestId == requestId); + m_lastCompletedFindRequestId = requestId; + m_viewClient->findTextFinished(QWebEngineFindTextResult(numberOfMatches, activeMatchOrdinal)); + invokeResultCallback(requestId, numberOfMatches); +} + +void FindTextHelper::handleLoadCommitted() +{ + // Make sure that we don't set the findNext WebFindOptions on a new frame. + m_previousFindText = QString(); +} + +void FindTextHelper::invokeResultCallback(int requestId, int numberOfMatches) +{ + if (m_quickCallbacks.contains(requestId)) { + QJSValue resultCallback = m_quickCallbacks.take(requestId); + QJSValueList args; + args.append(QJSValue(numberOfMatches)); + resultCallback.call(args); + } else { + m_widgetCallbacks.invoke(requestId, numberOfMatches > 0); + } +} + +} // namespace QtWebEngineCore diff --git a/src/core/find_text_helper.h b/src/core/find_text_helper.h new file mode 100644 index 000000000..e8f186272 --- /dev/null +++ b/src/core/find_text_helper.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// 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 FIND_TEXT_HELPER_H +#define FIND_TEXT_HELPER_H + +#include "qtwebenginecoreglobal_p.h" + +#include "qwebenginecallback_p.h" +#include <QJSValue> + +namespace content { +class WebContents; +} + +namespace gfx { +class Rect; +} + +namespace QtWebEngineCore { + +class WebContentsAdapterClient; + +class Q_WEBENGINECORE_PRIVATE_EXPORT FindTextHelper { +public: + FindTextHelper(content::WebContents *webContents, WebContentsAdapterClient *viewClient); + ~FindTextHelper(); + + void startFinding(const QString &findText, bool caseSensitively, bool findBackward, const QWebEngineCallback<bool> resultCallback); + void startFinding(const QString &findText, bool caseSensitively, bool findBackward, const QJSValue &resultCallback); + void startFinding(const QString &findText, bool caseSensitively, bool findBackward); + void stopFinding(); + bool isFindTextInProgress() const; + void handleFindReply(content::WebContents *source, int requestId, int numberOfMatches, const gfx::Rect &selectionRect, int activeMatchOrdinal, bool finalUpdate); + void handleLoadCommitted(); + +private: + void invokeResultCallback(int requestId, int numberOfMatches); + + content::WebContents *m_webContents; + WebContentsAdapterClient *m_viewClient; + + static int m_findRequestIdCounter; + int m_currentFindRequestId; + int m_lastCompletedFindRequestId; + + QString m_previousFindText; + + QMap<int, QJSValue> m_quickCallbacks; + CallbackDirectory m_widgetCallbacks; +}; + +} // namespace QtWebEngineCore + +#endif // FIND_TEXT_HELPER_H diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp index 0242506ee..2918fd8bc 100644 --- a/src/core/render_widget_host_view_qt.cpp +++ b/src/core/render_widget_host_view_qt.cpp @@ -51,6 +51,9 @@ #include "web_contents_adapter_client.h" #include "web_event_factory.h" +#include "base/threading/thread_task_runner_handle.h" +#include "components/viz/common/features.h" +#include "components/viz/common/frame_sinks/begin_frame_source.h" #include "components/viz/common/surfaces/frame_sink_id_allocator.h" #include "components/viz/host/host_frame_sink_manager.h" #include "content/browser/compositor/surface_utils.h" @@ -271,10 +274,12 @@ static content::ScreenInfo screenInfoFromQScreen(QScreen *screen) RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost *widget) : content::RenderWidgetHostViewBase::RenderWidgetHostViewBase(widget) + , m_taskRunner(base::ThreadTaskRunnerHandle::Get()) , m_gestureProvider(QtGestureProviderConfig(), this) , m_sendMotionActionDown(false) , m_touchMotionStarted(false) - , m_compositor(new Compositor(widget)) + , m_enableViz(features::IsVizDisplayCompositorEnabled()) + , m_visible(false) , m_loadVisuallyCommittedState(NotCommitted) , m_adapterClient(0) , m_imeInProgress(false) @@ -288,14 +293,39 @@ RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost *widget , m_mouseWheelPhaseHandler(this) , m_frameSinkId(host()->GetFrameSinkId()) { - host()->SetView(this); - if (GetTextInputManager()) GetTextInputManager()->AddObserver(this); const QPlatformInputContext *context = QGuiApplicationPrivate::platformIntegration()->inputContext(); m_imeHasHiddenTextCapability = context && context->hasCapability(QPlatformInputContext::HiddenTextCapability); + if (m_enableViz) { + m_rootLayer.reset(new ui::Layer(ui::LAYER_SOLID_COLOR)); + m_rootLayer->SetColor(SK_ColorTRANSPARENT); + + m_delegatedFrameHost.reset(new content::DelegatedFrameHost( + host()->GetFrameSinkId(), + &m_delegatedFrameHostClient, + true /* should_register_frame_sink_id */)); + + content::ImageTransportFactory *imageTransportFactory = content::ImageTransportFactory::GetInstance(); + ui::ContextFactory *contextFactory = imageTransportFactory->GetContextFactory(); + ui::ContextFactoryPrivate *contextFactoryPrivate = imageTransportFactory->GetContextFactoryPrivate(); + m_uiCompositor.reset(new ui::Compositor( + contextFactoryPrivate->AllocateFrameSinkId(), + contextFactory, + contextFactoryPrivate, + m_taskRunner, + false /* enable_pixel_canvas */)); + m_uiCompositor->SetAcceleratedWidget(gfx::kNullAcceleratedWidget); // null means offscreen + m_uiCompositor->SetRootLayer(m_rootLayer.get()); + + m_displayFrameSink = DisplayFrameSink::findOrCreate(m_uiCompositor->frame_sink_id()); + m_displayFrameSink->connect(this); + } else { + m_compositor.reset(new Compositor(widget)); + } + if (host()->delegate() && host()->delegate()->GetInputEventRouter()) host()->delegate()->GetInputEventRouter()->AddFrameSinkIdOwner(GetFrameSinkId(), this); @@ -305,12 +335,22 @@ RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost *widget 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)); + + host()->render_frame_metadata_provider()->ReportAllFrameSubmissionsForTesting(true); + + // May call SetNeedsBeginFrames + host()->SetView(this); } RenderWidgetHostViewQt::~RenderWidgetHostViewQt() { + m_delegate.reset(); + QObject::disconnect(m_adapterClientDestroyedConnection); + if (m_enableViz) + m_displayFrameSink->disconnect(this); + if (text_input_manager_) text_input_manager_->RemoveObserver(this); @@ -404,6 +444,8 @@ bool RenderWidgetHostViewQt::HasFocus() bool RenderWidgetHostViewQt::IsSurfaceAvailableForCopy() { + if (m_enableViz) + return m_delegatedFrameHost->CanCopyFromCompositingSurface(); return true; } @@ -411,6 +453,11 @@ void RenderWidgetHostViewQt::CopyFromSurface(const gfx::Rect &src_rect, const gfx::Size &output_size, base::OnceCallback<void(const SkBitmap &)> callback) { + if (m_enableViz) { + m_delegatedFrameHost->CopyFromCompositingSurface(src_rect, output_size, std::move(callback)); + return; + } + QImage image; if (m_delegate->copySurface(toQt(src_rect), toQt(output_size), image)) std::move(callback).Run(toSkBitmap(image)); @@ -441,6 +488,18 @@ gfx::Rect RenderWidgetHostViewQt::GetViewBounds() void RenderWidgetHostViewQt::UpdateBackgroundColor() { + if (m_enableViz) { + DCHECK(GetBackgroundColor()); + SkColor color = *GetBackgroundColor(); + bool opaque = SkColorGetA(color) == SK_AlphaOPAQUE; + m_rootLayer->SetFillsBoundsOpaquely(opaque); + m_rootLayer->SetColor(color); + m_uiCompositor->SetBackgroundColor(color); + m_delegate->setClearColor(toQt(color)); + host()->Send(new RenderViewObserverQt_SetBackgroundColor(host()->GetRoutingID(), color)); + return; + } + auto color = GetBackgroundColor(); if (color) { m_delegate->setClearColor(toQt(*color)); @@ -662,17 +721,15 @@ void RenderWidgetHostViewQt::DisplayTooltipText(const base::string16 &tooltip_te m_adapterClient->setToolTip(toQt(tooltip_text)); } -void RenderWidgetHostViewQt::DidCreateNewRendererCompositorFrameSink(viz::mojom::CompositorFrameSinkClient *frameSink) +void RenderWidgetHostViewQt::DidCreateNewRendererCompositorFrameSink(viz::mojom::CompositorFrameSinkClient *frameSinkClient) { - m_compositor->setFrameSinkClient(frameSink); + DCHECK(!m_enableViz); + m_compositor->setFrameSinkClient(frameSinkClient); } -void RenderWidgetHostViewQt::SubmitCompositorFrame(const viz::LocalSurfaceId &local_surface_id, viz::CompositorFrame frame, base::Optional<viz::HitTestRegionList>) +void RenderWidgetHostViewQt::SubmitCompositorFrame(const viz::LocalSurfaceId &local_surface_id, viz::CompositorFrame frame, base::Optional<viz::HitTestRegionList> hit_test_region_list) { - bool scrollOffsetChanged = (m_lastScrollOffset != frame.metadata.root_scroll_offset); - bool contentsSizeChanged = (m_lastContentsSize != frame.metadata.root_layer_size); - m_lastScrollOffset = frame.metadata.root_scroll_offset; - m_lastContentsSize = frame.metadata.root_layer_size; + DCHECK(!m_enableViz); // Force to process swap messages uint32_t frame_token = frame.metadata.frame_token; @@ -681,19 +738,7 @@ void RenderWidgetHostViewQt::SubmitCompositorFrame(const viz::LocalSurfaceId &lo m_compositor->submitFrame( std::move(frame), - base::BindOnce(&RenderWidgetHostViewQtDelegate::update, base::Unretained(m_delegate.get()))); - - if (m_loadVisuallyCommittedState == NotCommitted) { - m_loadVisuallyCommittedState = DidFirstCompositorFrameSwap; - } else if (m_loadVisuallyCommittedState == DidFirstVisuallyNonEmptyPaint) { - m_adapterClient->loadVisuallyCommitted(); - m_loadVisuallyCommittedState = NotCommitted; - } - - if (scrollOffsetChanged) - m_adapterClient->updateScrollPosition(toQt(m_lastScrollOffset)); - if (contentsSizeChanged) - m_adapterClient->updateContentsSize(toQt(m_lastContentsSize)); + base::BindOnce(&RenderWidgetHostViewQt::callUpdate, base::Unretained(this))); } void RenderWidgetHostViewQt::GetScreenInfo(content::ScreenInfo *results) @@ -906,7 +951,7 @@ viz::ScopedSurfaceIdAllocator RenderWidgetHostViewQt::DidUpdateVisualProperties( base::OnceCallback<void()> allocation_task = base::BindOnce(&RenderWidgetHostViewQt::OnDidUpdateVisualPropertiesComplete, base::Unretained(this), metadata); - return viz::ScopedSurfaceIdAllocator(std::move(allocation_task)); + return viz::ScopedSurfaceIdAllocator(&m_dfhLocalSurfaceIdAllocator, std::move(allocation_task)); } void RenderWidgetHostViewQt::OnDidUpdateVisualPropertiesComplete(const cc::RenderFrameMetadata &metadata) @@ -914,19 +959,71 @@ void RenderWidgetHostViewQt::OnDidUpdateVisualPropertiesComplete(const cc::Rende synchronizeVisualProperties(metadata.local_surface_id_allocation); } +void RenderWidgetHostViewQt::OnDidFirstVisuallyNonEmptyPaint() +{ + if (m_loadVisuallyCommittedState == NotCommitted) { + m_loadVisuallyCommittedState = DidFirstVisuallyNonEmptyPaint; + } else if (m_loadVisuallyCommittedState == DidFirstCompositorFrameSwap) { + m_adapterClient->loadVisuallyCommitted(); + m_loadVisuallyCommittedState = NotCommitted; + } +} + +void RenderWidgetHostViewQt::scheduleUpdate() +{ + DCHECK(m_enableViz); + m_taskRunner->PostTask( + FROM_HERE, + base::BindOnce(&RenderWidgetHostViewQt::callUpdate, m_weakPtrFactory.GetWeakPtr())); +} + +void RenderWidgetHostViewQt::callUpdate() +{ + m_delegate->update(); + + if (m_loadVisuallyCommittedState == NotCommitted) { + m_loadVisuallyCommittedState = DidFirstCompositorFrameSwap; + } else if (m_loadVisuallyCommittedState == DidFirstVisuallyNonEmptyPaint) { + m_adapterClient->loadVisuallyCommitted(); + m_loadVisuallyCommittedState = NotCommitted; + } +} + QSGNode *RenderWidgetHostViewQt::updatePaintNode(QSGNode *oldNode) { + if (m_enableViz) + return m_displayFrameSink->updatePaintNode(oldNode, m_delegate.get()); return m_compositor->updatePaintNode(oldNode, m_delegate.get()); } void RenderWidgetHostViewQt::notifyShown() { - host()->WasShown(false); + if (m_enableViz) { + if (m_visible) + return; + m_visible = true; + m_delegatedFrameHost->AttachToCompositor(m_uiCompositor.get()); + m_delegatedFrameHost->WasShown(GetLocalSurfaceIdAllocation().local_surface_id(), + m_viewRectInDips.size(), + false /* record_presentation_time */); + host()->WasShown(false); + } else { + host()->WasShown(false); + } } void RenderWidgetHostViewQt::notifyHidden() { - host()->WasHidden(); + if (m_enableViz) { + if (!m_visible) + return; + m_visible = false; + host()->WasHidden(); + m_delegatedFrameHost->WasHidden(); + m_delegatedFrameHost->DetachFromCompositor(); + } else { + host()->WasHidden(); + } } void RenderWidgetHostViewQt::visualPropertiesChanged() @@ -1644,6 +1741,7 @@ void RenderWidgetHostViewQt::handleFocusEvent(QFocusEvent *ev) void RenderWidgetHostViewQt::SetNeedsBeginFrames(bool needs_begin_frames) { + DCHECK(!m_enableViz); m_compositor->setNeedsBeginFrames(needs_begin_frames); } @@ -1683,26 +1781,35 @@ void RenderWidgetHostViewQt::SetWantsAnimateOnlyBeginFrames() viz::SurfaceId RenderWidgetHostViewQt::GetCurrentSurfaceId() const { + if (m_enableViz) + return m_delegatedFrameHost->GetCurrentSurfaceId(); return viz::SurfaceId(); } const viz::FrameSinkId &RenderWidgetHostViewQt::GetFrameSinkId() const { + if (m_enableViz) + return m_delegatedFrameHost->frame_sink_id(); return m_frameSinkId; } const viz::LocalSurfaceIdAllocation &RenderWidgetHostViewQt::GetLocalSurfaceIdAllocation() const { - return m_localSurfaceIdAllocator.GetCurrentLocalSurfaceIdAllocation(); + return m_dfhLocalSurfaceIdAllocator.GetCurrentLocalSurfaceIdAllocation(); } void RenderWidgetHostViewQt::TakeFallbackContentFrom(content::RenderWidgetHostView *view) { DCHECK(!static_cast<RenderWidgetHostViewBase*>(view)->IsRenderWidgetHostViewChildFrame()); DCHECK(!static_cast<RenderWidgetHostViewBase*>(view)->IsRenderWidgetHostViewGuest()); - base::Optional<SkColor> color = view->GetBackgroundColor(); + RenderWidgetHostViewQt *viewQt = static_cast<RenderWidgetHostViewQt *>(view); + base::Optional<SkColor> color = viewQt->GetBackgroundColor(); if (color) SetBackgroundColor(*color); + if (m_enableViz) { + m_delegatedFrameHost->TakeFallbackContentFrom(viewQt->m_delegatedFrameHost.get()); + host()->GetContentRenderingTimeoutFrom(viewQt->host()); + } } void RenderWidgetHostViewQt::EnsureSurfaceSynchronizedForWebTest() @@ -1729,14 +1836,38 @@ void RenderWidgetHostViewQt::OnRenderFrameMetadataChangedAfterActivation() m_selectionEnd = metadata.selection.end; m_touchSelectionControllerClient->UpdateClientSelectionBounds(m_selectionStart, m_selectionEnd); } + + gfx::Vector2dF scrollOffset = metadata.root_scroll_offset.value_or(gfx::Vector2dF()); + gfx::SizeF contentsSize = metadata.root_layer_size; + std::swap(m_lastScrollOffset, scrollOffset); + std::swap(m_lastContentsSize, contentsSize); + if (scrollOffset != m_lastScrollOffset) + m_adapterClient->updateScrollPosition(toQt(m_lastScrollOffset)); + if (contentsSize != m_lastContentsSize) + m_adapterClient->updateContentsSize(toQt(m_lastContentsSize)); } void RenderWidgetHostViewQt::synchronizeVisualProperties(const base::Optional<viz::LocalSurfaceIdAllocation> &childSurfaceId) { if (childSurfaceId) - m_localSurfaceIdAllocator.UpdateFromChild(*childSurfaceId); + m_dfhLocalSurfaceIdAllocator.UpdateFromChild(*childSurfaceId); else - m_localSurfaceIdAllocator.GenerateId(); + m_dfhLocalSurfaceIdAllocator.GenerateId(); + + if (m_enableViz) { + gfx::Size viewSizeInDips = GetRequestedRendererSize(); + gfx::Size viewSizeInPixels = GetCompositorViewportPixelSize(); + m_rootLayer->SetBounds(gfx::Rect(gfx::Point(), viewSizeInPixels)); + m_uiCompositorLocalSurfaceIdAllocator.GenerateId(); + m_uiCompositor->SetScaleAndSize( + m_screenInfo.device_scale_factor, + viewSizeInPixels, + m_uiCompositorLocalSurfaceIdAllocator.GetCurrentLocalSurfaceIdAllocation()); + m_delegatedFrameHost->EmbedSurface( + m_dfhLocalSurfaceIdAllocator.GetCurrentLocalSurfaceIdAllocation().local_surface_id(), + viewSizeInDips, + cc::DeadlinePolicy::UseDefaultDeadline()); + } host()->SynchronizeVisualProperties(); } diff --git a/src/core/render_widget_host_view_qt.h b/src/core/render_widget_host_view_qt.h index 91d5b8610..5935f477a 100644 --- a/src/core/render_widget_host_view_qt.h +++ b/src/core/render_widget_host_view_qt.h @@ -40,10 +40,11 @@ #ifndef RENDER_WIDGET_HOST_VIEW_QT_H #define RENDER_WIDGET_HOST_VIEW_QT_H +#include "compositor/display_frame_sink.h" +#include "delegated_frame_host_client_qt.h" #include "render_widget_host_view_qt_delegate.h" #include "base/memory/weak_ptr.h" -#include "components/viz/common/frame_sinks/begin_frame_source.h" #include "components/viz/common/resources/transferable_resource.h" #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h" #include "components/viz/host/host_frame_sink_client.h" @@ -53,7 +54,7 @@ #include "content/browser/renderer_host/text_input_manager.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> @@ -104,6 +105,7 @@ class RenderWidgetHostViewQt , public RenderWidgetHostViewQtDelegateClient , public base::SupportsWeakPtr<RenderWidgetHostViewQt> , public content::TextInputManager::Observer + , public DisplayConsumer { public: enum LoadVisuallyCommittedState { @@ -215,12 +217,16 @@ public: // Overridden from content::BrowserAccessibilityDelegate content::BrowserAccessibilityManager* CreateBrowserAccessibilityManager(content::BrowserAccessibilityDelegate* delegate, bool for_root_frame) override; - LoadVisuallyCommittedState getLoadVisuallyCommittedState() const { return m_loadVisuallyCommittedState; } - void setLoadVisuallyCommittedState(LoadVisuallyCommittedState state) { m_loadVisuallyCommittedState = state; } + + // Called from WebContentsDelegateQt + void OnDidFirstVisuallyNonEmptyPaint(); // Overridden from content::RenderFrameMetadataProvider::Observer void OnRenderFrameMetadataChangedAfterActivation() override; + // Overridden from DisplayConsumer + void scheduleUpdate() override; + gfx::SizeF lastContentsSize() const { return m_lastContentsSize; } gfx::Vector2dF lastScrollOffset() const { return m_lastScrollOffset; } @@ -230,6 +236,8 @@ public: ui::TextInputType getTextInputType() const; private: + friend class DelegatedFrameHostClientQt; + void processMotionEvent(const ui::MotionEvent &motionEvent); void clearPreviousTouchMotionState(); QList<QTouchEvent::TouchPoint> mapTouchPointIds(const QList<QTouchEvent::TouchPoint> &inputPoints); @@ -241,12 +249,16 @@ private: void synchronizeVisualProperties(const base::Optional<viz::LocalSurfaceIdAllocation> &childSurfaceId); + void callUpdate(); + // Geometry of the view in screen DIPs. gfx::Rect m_viewRectInDips; // Geometry of the window, including frame, in screen DIPs. gfx::Rect m_windowRectInDips; content::ScreenInfo m_screenInfo; + scoped_refptr<base::SingleThreadTaskRunner> m_taskRunner; + ui::FilteredGestureProvider m_gestureProvider; base::TimeDelta m_eventsToNowDelta; bool m_sendMotionActionDown; @@ -255,6 +267,13 @@ private: QList<QTouchEvent::TouchPoint> m_previousTouchPoints; std::unique_ptr<RenderWidgetHostViewQtDelegate> m_delegate; + const bool m_enableViz; + bool m_visible; + DelegatedFrameHostClientQt m_delegatedFrameHostClient{this}; + std::unique_ptr<content::DelegatedFrameHost> m_delegatedFrameHost; + std::unique_ptr<ui::Layer> m_rootLayer; + std::unique_ptr<ui::Compositor> m_uiCompositor; + scoped_refptr<DisplayFrameSink> m_displayFrameSink; std::unique_ptr<Compositor> m_compositor; LoadVisuallyCommittedState m_loadVisuallyCommittedState; @@ -268,7 +287,8 @@ private: gfx::Vector2dF m_lastScrollOffset; gfx::SizeF m_lastContentsSize; - viz::ParentLocalSurfaceIdAllocator m_localSurfaceIdAllocator; + viz::ParentLocalSurfaceIdAllocator m_dfhLocalSurfaceIdAllocator; + viz::ParentLocalSurfaceIdAllocator m_uiCompositorLocalSurfaceIdAllocator; uint m_imState; int m_anchorPositionWithinSelection; @@ -290,6 +310,8 @@ private: std::unique_ptr<ui::TouchSelectionController> m_touchSelectionController; gfx::SelectionBound m_selectionStart; gfx::SelectionBound m_selectionEnd; + + base::WeakPtrFactory<RenderWidgetHostViewQt> m_weakPtrFactory{this}; }; } // namespace QtWebEngineCore diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp index 2831ec159..85e63c5a4 100644 --- a/src/core/web_contents_adapter.cpp +++ b/src/core/web_contents_adapter.cpp @@ -86,7 +86,6 @@ #include "content/public/common/web_preferences.h" #include "content/public/common/webrtc_ip_handling_policy.h" #include "extensions/buildflags/buildflags.h" -#include "third_party/blink/public/mojom/frame/find_in_page.mojom.h" #include "third_party/blink/public/web/web_media_player_action.h" #include "printing/buildflags/buildflags.h" #include "ui/base/clipboard/clipboard.h" @@ -416,7 +415,6 @@ WebContentsAdapter::WebContentsAdapter() #endif , m_adapterClient(nullptr) , m_nextRequestId(CallbackDirectory::ReservedCallbackIdsEnd) - , m_lastFindRequestId(0) , m_currentDropAction(blink::kWebDragOperationNone) , m_devToolsFrontend(nullptr) { @@ -433,7 +431,6 @@ WebContentsAdapter::WebContentsAdapter(std::unique_ptr<content::WebContents> web #endif , m_adapterClient(nullptr) , m_nextRequestId(CallbackDirectory::ReservedCallbackIdsEnd) - , m_lastFindRequestId(0) , m_currentDropAction(blink::kWebDragOperationNone) , m_devToolsFrontend(nullptr) { @@ -1046,41 +1043,6 @@ quint64 WebContentsAdapter::fetchDocumentInnerText() return m_nextRequestId++; } -quint64 WebContentsAdapter::findText(const QString &subString, bool caseSensitively, bool findBackward) -{ - CHECK_INITIALIZED(0); - if (m_lastFindRequestId > m_webContentsDelegate->lastReceivedFindReply()) { - // There are cases where the render process will overwrite a previous request - // with the new search and we'll have a dangling callback, leaving the application - // waiting for it forever. - // Assume that any unfinished find has been unsuccessful when a new one is started - // to cover that case. - m_webContentsDelegate->setLastReceivedFindReply(m_lastFindRequestId); - m_adapterClient->didFindText(m_lastFindRequestId, 0); - } - - 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), std::move(options)); - m_lastFindRequestId = shrunkRequestId; - return shrunkRequestId; -} - -void WebContentsAdapter::stopFinding() -{ - CHECK_INITIALIZED(); - m_webContentsDelegate->setLastReceivedFindReply(m_lastFindRequestId); - m_webContentsDelegate->setLastSearchedString(QString()); - m_webContents->StopFinding(content::STOP_FIND_ACTION_KEEP_SELECTION); -} - void WebContentsAdapter::updateWebPreferences(const content::WebPreferences & webPreferences) { CHECK_INITIALIZED(); @@ -1696,12 +1658,6 @@ void WebContentsAdapter::focusIfNecessary() m_webContents->Focus(); } -bool WebContentsAdapter::isFindTextInProgress() const -{ - CHECK_INITIALIZED(false); - return m_lastFindRequestId != m_webContentsDelegate->lastReceivedFindReply(); -} - bool WebContentsAdapter::hasFocusedFrame() const { CHECK_INITIALIZED(false); @@ -1745,6 +1701,12 @@ FaviconManager *WebContentsAdapter::faviconManager() return m_webContentsDelegate->faviconManager(); } +FindTextHelper *WebContentsAdapter::findTextHelper() +{ + CHECK_INITIALIZED(nullptr); + return m_webContentsDelegate->findTextHelper(); +} + void WebContentsAdapter::viewSource() { CHECK_INITIALIZED(); diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h index baf9d241c..11f8f9cb1 100644 --- a/src/core/web_contents_adapter.h +++ b/src/core/web_contents_adapter.h @@ -85,6 +85,7 @@ namespace QtWebEngineCore { class DevToolsFrontendQt; class FaviconManager; +class FindTextHelper; class MessagePassingInterface; class ProfileQt; class RenderViewObserverHostQt; @@ -158,8 +159,6 @@ public: quint64 runJavaScriptCallbackResult(const QString &javaScript, quint32 worldId); quint64 fetchDocumentMarkup(); quint64 fetchDocumentInnerText(); - quint64 findText(const QString &subString, bool caseSensitively, bool findBackward); - void stopFinding(); void updateWebPreferences(const content::WebPreferences &webPreferences); void download(const QUrl &url, const QString &suggestedFileName, const QUrl &referrerUrl = QUrl(), @@ -205,6 +204,7 @@ public: void setWebChannel(QWebChannel *, uint worldId); #endif FaviconManager *faviconManager(); + FindTextHelper *findTextHelper(); QPointF lastScrollOffset() const; QSizeF lastContentsSize() const; @@ -263,7 +263,6 @@ private: #endif WebContentsAdapterClient *m_adapterClient; quint64 m_nextRequestId; - int m_lastFindRequestId; std::unique_ptr<content::DropData> m_currentDropData; uint m_currentDropAction; bool m_updateDragActionCalled; diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h index d53568215..4bdb55b4c 100644 --- a/src/core/web_contents_adapter_client.h +++ b/src/core/web_contents_adapter_client.h @@ -64,6 +64,7 @@ QT_FORWARD_DECLARE_CLASS(CertificateErrorController) QT_FORWARD_DECLARE_CLASS(ClientCertSelectController) QT_FORWARD_DECLARE_CLASS(QKeyEvent) QT_FORWARD_DECLARE_CLASS(QVariant) +QT_FORWARD_DECLARE_CLASS(QWebEngineFindTextResult) QT_FORWARD_DECLARE_CLASS(QWebEngineQuotaRequest) QT_FORWARD_DECLARE_CLASS(QWebEngineRegisterProtocolHandlerRequest) QT_FORWARD_DECLARE_CLASS(QWebEngineUrlRequestInfo) @@ -461,7 +462,6 @@ public: virtual void didRunJavaScript(quint64 requestId, const QVariant& result) = 0; 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, QSharedPointer<QByteArray>) = 0; virtual void didPrintPageToPdf(const QString &filePath, bool success) = 0; virtual bool passOnFocus(bool reverse) = 0; @@ -499,6 +499,7 @@ public: 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 void findTextFinished(const QWebEngineFindTextResult &result) = 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 f18617f8e..9855e3859 100644 --- a/src/core/web_contents_delegate_qt.cpp +++ b/src/core/web_contents_delegate_qt.cpp @@ -102,8 +102,8 @@ static WebContentsAdapterClient::JavaScriptConsoleMessageLevel mapToJavascriptCo WebContentsDelegateQt::WebContentsDelegateQt(content::WebContents *webContents, WebContentsAdapterClient *adapterClient) : m_viewClient(adapterClient) - , m_lastReceivedFindReply(0) , m_faviconManager(new FaviconManager(webContents, adapterClient)) + , m_findTextHelper(new FindTextHelper(webContents, adapterClient)) , m_lastLoadProgress(-1) , m_loadingState(determineLoadingState(webContents)) , m_didStartLoadingSeen(m_loadingState == LoadingState::Loading) @@ -350,9 +350,7 @@ void WebContentsDelegateQt::EmitLoadFinished(bool success, const QUrl &url, bool void WebContentsDelegateQt::EmitLoadCommitted() { - // Make sure that we don't set the findNext WebFindOptions on a new frame. - m_lastSearchedString = QString(); - + m_findTextHelper->handleLoadCommitted(); m_viewClient->loadCommitted(); m_viewClient->updateNavigationActions(); } @@ -577,13 +575,7 @@ bool WebContentsDelegateQt::DidAddMessageToConsole(content::WebContents *source, void WebContentsDelegateQt::FindReply(content::WebContents *source, int request_id, int number_of_matches, const gfx::Rect& selection_rect, int active_match_ordinal, bool final_update) { - Q_UNUSED(source) - Q_UNUSED(selection_rect) - Q_UNUSED(active_match_ordinal) - if (final_update && request_id > m_lastReceivedFindReply) { - m_lastReceivedFindReply = request_id; - m_viewClient->didFindText(request_id, number_of_matches); - } + m_findTextHelper->handleFindReply(source, request_id, number_of_matches, selection_rect, active_match_ordinal, final_update); } void WebContentsDelegateQt::RequestMediaAccessPermission(content::WebContents *web_contents, const content::MediaStreamRequest &request, content::MediaResponseCallback callback) @@ -623,13 +615,7 @@ void WebContentsDelegateQt::DidFirstVisuallyNonEmptyPaint() if (!rwhv) return; - RenderWidgetHostViewQt::LoadVisuallyCommittedState loadVisuallyCommittedState = rwhv->getLoadVisuallyCommittedState(); - if (loadVisuallyCommittedState == RenderWidgetHostViewQt::NotCommitted) { - rwhv->setLoadVisuallyCommittedState(RenderWidgetHostViewQt::DidFirstVisuallyNonEmptyPaint); - } else if (loadVisuallyCommittedState == RenderWidgetHostViewQt::DidFirstCompositorFrameSwap) { - m_viewClient->loadVisuallyCommitted(); - rwhv->setLoadVisuallyCommittedState(RenderWidgetHostViewQt::NotCommitted); - } + rwhv->OnDidFirstVisuallyNonEmptyPaint(); } void WebContentsDelegateQt::ActivateContents(content::WebContents* contents) @@ -798,6 +784,11 @@ FaviconManager *WebContentsDelegateQt::faviconManager() return m_faviconManager.data(); } +FindTextHelper *WebContentsDelegateQt::findTextHelper() +{ + return m_findTextHelper.data(); +} + WebEngineSettings *WebContentsDelegateQt::webEngineSettings() const { return m_viewClient->webEngineSettings(); } diff --git a/src/core/web_contents_delegate_qt.h b/src/core/web_contents_delegate_qt.h index 00b715c30..f1d5ed76c 100644 --- a/src/core/web_contents_delegate_qt.h +++ b/src/core/web_contents_delegate_qt.h @@ -50,6 +50,7 @@ #include "color_chooser_controller.h" #include "favicon_manager.h" +#include "find_text_helper.h" #include "javascript_dialog_manager_qt.h" #include <QtCore/qvector.h> @@ -112,10 +113,6 @@ class WebContentsDelegateQt : public content::WebContentsDelegate public: WebContentsDelegateQt(content::WebContents*, WebContentsAdapterClient *adapterClient); ~WebContentsDelegateQt(); - QString lastSearchedString() const { return m_lastSearchedString; } - void setLastSearchedString(const QString &s) { m_lastSearchedString = s; } - int lastReceivedFindReply() const { return m_lastReceivedFindReply; } - void setLastReceivedFindReply(int id) { m_lastReceivedFindReply = id; } QUrl url() const { return m_url; } QString title() const { return m_title; } @@ -178,6 +175,7 @@ public: void requestUserNotificationPermission(const QUrl &requestingOrigin); void launchExternalURL(const QUrl &url, ui::PageTransition page_transition, bool is_main_frame, bool has_user_gesture); FaviconManager *faviconManager(); + FindTextHelper *findTextHelper(); void setSavePageInfo(const SavePageInfo &spi) { m_savePageInfo = spi; } const SavePageInfo &savePageInfo() { return m_savePageInfo; } @@ -213,10 +211,9 @@ private: int &streamCount(blink::MediaStreamType type); WebContentsAdapterClient *m_viewClient; - QString m_lastSearchedString; - int m_lastReceivedFindReply; QVector<int64_t> m_loadingErrorFrameList; QScopedPointer<FaviconManager> m_faviconManager; + QScopedPointer<FindTextHelper> m_findTextHelper; SavePageInfo m_savePageInfo; QSharedPointer<FilePickerController> m_filePickerController; QUrl m_initialTargetUrl; diff --git a/src/core/web_engine_context.cpp b/src/core/web_engine_context.cpp index a988040ee..a42565c2b 100644 --- a/src/core/web_engine_context.cpp +++ b/src/core/web_engine_context.cpp @@ -488,6 +488,16 @@ WebEngineContext::WebEngineContext() parsedCommandLine->AppendSwitch(switches::kDisableES3GLContext); #endif + bool threadedGpu = true; +#ifndef QT_NO_OPENGL + threadedGpu = QOpenGLContext::supportsThreadedOpenGL(); +#endif + + bool enableViz = ((threadedGpu && !parsedCommandLine->HasSwitch("disable-viz-display-compositor")) + || parsedCommandLine->HasSwitch("enable-viz-display-compositor")); + parsedCommandLine->RemoveSwitch("disable-viz-display-compositor"); + parsedCommandLine->RemoveSwitch("enable-viz-display-compositor"); + std::string disableFeatures; std::string enableFeatures; // Needed to allow navigations within pages that were set using setHtml(). One example is @@ -495,10 +505,6 @@ WebEngineContext::WebEngineContext() // This is deprecated behavior, and will be removed in a future Chromium version, as per // upstream Chromium commit ba52f56207a4b9d70b34880fbff2352e71a06422. appendToFeatureList(enableFeatures, features::kAllowContentInitiatedDataUrlNavigations.name); - // Surface synchronization breaks our current graphics integration (since 65) - appendToFeatureList(disableFeatures, features::kEnableSurfaceSynchronization.name); - // Viz Display Compositor is enabled by default since 73. Doesn't work for us (also implies SurfaceSynchronization) - appendToFeatureList(disableFeatures, features::kVizDisplayCompositor.name); // The video-capture service is not functioning at this moment (since 69) appendToFeatureList(disableFeatures, features::kMojoVideoCapture.name); // Breaks WebEngineNewViewRequest.userInitiated API (since 73) @@ -506,8 +512,6 @@ WebEngineContext::WebEngineContext() // We do not yet support the network-service, but it is enabled by default since 75. appendToFeatureList(disableFeatures, network::features::kNetworkService.name); - // VideoSurfaceLayer is enabled by default since 75. We don't support it. - appendToFeatureList(disableFeatures, media::kUseSurfaceLayerForVideo.name); // BlinkGenPropertyTrees is enabled by default in 75, but causes regressions. appendToFeatureList(disableFeatures, blink::features::kBlinkGenPropertyTrees.name); @@ -531,6 +535,15 @@ WebEngineContext::WebEngineContext() parsedCommandLine->AppendSwitch(cc::switches::kDisableCompositedAntialiasing); } + if (!enableViz) { + // Surface synchronization breaks our current graphics integration (since 65) + appendToFeatureList(disableFeatures, features::kEnableSurfaceSynchronization.name); + // Viz Display Compositor is enabled by default since 73. Doesn't work for us (also implies SurfaceSynchronization) + appendToFeatureList(disableFeatures, features::kVizDisplayCompositor.name); + // VideoSurfaceLayer is enabled by default since 75. We don't support it. + appendToFeatureList(disableFeatures, media::kUseSurfaceLayerForVideo.name); + } + appendToFeatureSwitch(parsedCommandLine, switches::kDisableFeatures, disableFeatures); appendToFeatureSwitch(parsedCommandLine, switches::kEnableFeatures, enableFeatures); base::FeatureList::InitializeInstance( @@ -619,10 +632,6 @@ WebEngineContext::WebEngineContext() parsedCommandLine->AppendSwitch(switches::kDisableGpu); } - bool threadedGpu = true; -#ifndef QT_NO_OPENGL - threadedGpu = QOpenGLContext::supportsThreadedOpenGL(); -#endif registerMainThreadFactories(threadedGpu); SetContentClient(new ContentClientQt); diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp index 30283dc03..58d950cd9 100644 --- a/src/webengine/api/qquickwebengineview.cpp +++ b/src/webengine/api/qquickwebengineview.cpp @@ -43,6 +43,7 @@ #include "profile_adapter.h" #include "certificate_error_controller.h" #include "file_picker_controller.h" +#include "find_text_helper.h" #include "javascript_dialog_controller.h" #include "touch_selection_menu_controller.h" @@ -61,6 +62,7 @@ #include "qquickwebenginesettings_p.h" #include "qquickwebenginescript_p.h" #include "qquickwebenginetouchhandleprovider_p_p.h" +#include "qwebenginefindtextresult.h" #include "qwebenginequotarequest.h" #include "qwebengineregisterprotocolhandlerrequest.h" @@ -164,7 +166,6 @@ QQuickWebEngineViewPrivate::~QQuickWebEngineViewPrivate() { Q_ASSERT(m_profileInitialized); m_profile->d_ptr->removeWebContentsAdapterClient(this); - adapter->stopFinding(); if (faviconProvider) faviconProvider->detach(q_ptr); // q_ptr->d_ptr might be null due to destroy() @@ -273,8 +274,8 @@ void QQuickWebEngineViewPrivate::navigationRequested(int navigationType, const Q Q_EMIT q->navigationRequested(&navigationRequest); navigationRequestAction = navigationRequest.action(); - if ((navigationRequestAction == WebContentsAdapterClient::AcceptRequest) && adapter->isFindTextInProgress()) - adapter->stopFinding(); + if ((navigationRequestAction == WebContentsAdapterClient::AcceptRequest) && adapter->findTextHelper()->isFindTextInProgress()) + adapter->findTextHelper()->stopFinding(); } void QQuickWebEngineViewPrivate::javascriptDialog(QSharedPointer<JavaScriptDialogController> dialog) @@ -699,6 +700,12 @@ void QQuickWebEngineViewPrivate::widgetChanged(RenderWidgetHostViewQtDelegate *n bindViewAndWidget(q, static_cast<RenderWidgetHostViewQtDelegateQuick *>(newWidgetBase)); } +void QQuickWebEngineViewPrivate::findTextFinished(const QWebEngineFindTextResult &result) +{ + Q_Q(QQuickWebEngineView); + Q_EMIT q->findTextFinished(result); +} + WebEngineSettings *QQuickWebEngineViewPrivate::webEngineSettings() const { return m_settings->d_ptr.data(); @@ -1185,14 +1192,6 @@ void QQuickWebEngineViewPrivate::didRunJavaScript(quint64 requestId, const QVari callback.call(args); } -void QQuickWebEngineViewPrivate::didFindText(quint64 requestId, int matchCount) -{ - QJSValue callback = m_callbacks.take(requestId); - QJSValueList args; - args.append(QJSValue(matchCount)); - callback.call(args); -} - void QQuickWebEngineViewPrivate::didPrintPage(quint64 requestId, QSharedPointer<QByteArray> result) { Q_Q(QQuickWebEngineView); @@ -1464,18 +1463,8 @@ void QQuickWebEngineView::findText(const QString &subString, FindFlags options, Q_D(QQuickWebEngineView); if (!d->adapter->isInitialized()) return; - if (subString.isEmpty()) { - d->adapter->stopFinding(); - if (!callback.isUndefined()) { - QJSValueList args; - args.append(QJSValue(0)); - const_cast<QJSValue&>(callback).call(args); - } - } else { - quint64 requestId = d->adapter->findText(subString, options & FindCaseSensitively, options & FindBackward); - if (!callback.isUndefined()) - d->m_callbacks.insert(requestId, callback); - } + + d->adapter->findTextHelper()->startFinding(subString, options & FindCaseSensitively, options & FindBackward, callback); } QQuickWebEngineHistory *QQuickWebEngineView::navigationHistory() const diff --git a/src/webengine/api/qquickwebengineview_p.h b/src/webengine/api/qquickwebengineview_p.h index 3c8e1d9ec..4a88e3c28 100644 --- a/src/webengine/api/qquickwebengineview_p.h +++ b/src/webengine/api/qquickwebengineview_p.h @@ -79,6 +79,7 @@ class QQuickWebEngineSettings; class QQuickWebEngineTooltipRequest; class QQuickWebEngineFormValidationMessageRequest; class QQuickWebEngineViewPrivate; +class QWebEngineFindTextResult; class QWebEngineQuotaRequest; class QWebEngineRegisterProtocolHandlerRequest; @@ -574,6 +575,7 @@ Q_SIGNALS: Q_REVISION(10) void tooltipRequested(QQuickWebEngineTooltipRequest *request); Q_REVISION(11) void lifecycleStateChanged(LifecycleState state); Q_REVISION(11) void recommendedStateChanged(LifecycleState state); + Q_REVISION(11) void findTextFinished(const QWebEngineFindTextResult &result); #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 e0f2595ec..df6843ac3 100644 --- a/src/webengine/api/qquickwebengineview_p_p.h +++ b/src/webengine/api/qquickwebengineview_p_p.h @@ -80,6 +80,7 @@ class QQuickWebEngineSettings; class QQuickWebEngineFaviconProvider; class QQuickWebEngineProfilePrivate; class QQuickWebEngineTouchHandleProvider; +class QWebEngineFindTextResult; QQuickWebEngineView::WebAction editorActionForKeyEvent(QKeyEvent* event); @@ -133,7 +134,6 @@ public: void didRunJavaScript(quint64, const QVariant&) override; void didFetchDocumentMarkup(quint64, const QString&) override { } void didFetchDocumentInnerText(quint64, const QString&) override { } - void didFindText(quint64, int) override; void didPrintPage(quint64 requestId, QSharedPointer<QByteArray>) override; void didPrintPageToPdf(const QString &filePath, bool success) override; bool passOnFocus(bool reverse) override; @@ -170,6 +170,7 @@ public: QtWebEngineCore::WebContentsAdapter *webContentsAdapter() override; void printRequested() override; void widgetChanged(QtWebEngineCore::RenderWidgetHostViewQtDelegate *newWidgetBase) override; + void findTextFinished(const QWebEngineFindTextResult &result) override; void updateAction(QQuickWebEngineView::WebAction) const; void adoptWebContents(QtWebEngineCore::WebContentsAdapter *webContents); diff --git a/src/webengine/doc/src/webengineview_lgpl.qdoc b/src/webengine/doc/src/webengineview_lgpl.qdoc index 1c4328d01..8f03774c8 100644 --- a/src/webengine/doc/src/webengineview_lgpl.qdoc +++ b/src/webengine/doc/src/webengineview_lgpl.qdoc @@ -414,26 +414,33 @@ \qmlmethod void WebEngineView::findText(string subString) \since QtWebEngine 1.1 Finds the specified string, \a subString, in the page. + The findTextFinished() signal is emitted when a string search is completed. To clear the search highlight, just pass an empty string. + + \sa findTextFinished() */ /*! \qmlmethod void WebEngineView::findText(string subString, FindFlags options) \since QtWebEngine 1.1 Finds the specified string, \a subString, in the page, using the given \a options. + The findTextFinished() signal is emitted when a string search is completed. To clear the search highlight, just pass an empty string. \code findText("Qt", WebEngineView.FindBackward | WebEngineView.FindCaseSensitively); \endcode + + \sa findTextFinished() */ /*! \qmlmethod void WebEngineView::findText(string subString, FindFlags options, variant resultCallback) \since QtWebEngine 1.1 Finds the specified string, \a subString, in the page, using the given \a options. + The findTextFinished() signal is emitted when a string search is completed. To clear the search highlight, just pass an empty string. @@ -447,6 +454,8 @@ console.log("Qt was found!"); }); \endcode + + \sa findTextFinished() */ /*! @@ -1569,5 +1578,39 @@ resource state is however completely safe. \sa lifecycleState, {WebEngine Lifecycle Example} +*/ + +/*! + \qmltype FindTextResult + \instantiates QWebEngineFindTextResult + \inqmlmodule QtWebEngine + \since QtWebEngine 1.11 + + \brief A utility type for encapsulating the result of a string search on a page. + + \sa WebEngineView::findTextFinished() +*/ + +/*! + \qmlproperty int FindTextResult::numberOfMatches + \readonly + + \brief The number of matches found. +*/ + +/*! + \qmlproperty int FindTextResult::activeMatchOrdinal + \readonly + + \brief The index of the currently highlighted match. +*/ + +/*! + \qmlsignal WebEngineView::findTextFinished(FindTextResult result) + \since QtWebEngine 1.11 + + This signal is emitted when a string search on a page is completed. \a result is + the result of the string search. + \sa findText(), FindTextResult */ diff --git a/src/webengine/plugin/plugin.cpp b/src/webengine/plugin/plugin.cpp index ad49d6543..e47a46a95 100644 --- a/src/webengine/plugin/plugin.cpp +++ b/src/webengine/plugin/plugin.cpp @@ -55,6 +55,7 @@ #include <QtWebEngine/private/qquickwebenginetouchhandleprovider_p_p.h> #include <QtWebEngine/private/qquickwebengineview_p.h> #include <QtWebEngine/private/qquickwebengineaction_p.h> +#include <QtWebEngineCore/qwebenginefindtextresult.h> #include <QtWebEngineCore/qwebenginenotification.h> #include <QtWebEngineCore/qwebenginequotarequest.h> #include <QtWebEngineCore/qwebengineregisterprotocolhandlerrequest.h> @@ -170,6 +171,8 @@ public: qmlRegisterUncreatableType<QWebEngineNotification>(uri, 1, 9, "WebEngineNotification", msgUncreatableType("WebEngineNotification")); qmlRegisterUncreatableType<QQuickWebEngineTooltipRequest>(uri, 1, 10, "TooltipRequest", msgUncreatableType("TooltipRequest")); + qRegisterMetaType<QWebEngineFindTextResult>(); + qmlRegisterUncreatableType<QWebEngineFindTextResult>(uri, 1, 11, "FindTextResult", msgUncreatableType("FindTextResult")); } private: diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp index e990170eb..c9e9177b7 100644 --- a/src/webenginewidgets/api/qwebenginepage.cpp +++ b/src/webenginewidgets/api/qwebenginepage.cpp @@ -45,12 +45,14 @@ #include "certificate_error_controller.h" #include "color_chooser_controller.h" #include "favicon_manager.h" +#include "find_text_helper.h" #include "file_picker_controller.h" #include "javascript_dialog_controller.h" #if QT_CONFIG(webengine_printing_and_pdf) #include "printer_worker.h" #endif #include "qwebenginecertificateerror.h" +#include "qwebenginefindtextresult.h" #include "qwebenginefullscreenrequest.h" #include "qwebenginehistory.h" #include "qwebenginehistory_p.h" @@ -170,6 +172,7 @@ QWebEnginePagePrivate::QWebEnginePagePrivate(QWebEngineProfile *_profile) qRegisterMetaType<QWebEngineQuotaRequest>(); qRegisterMetaType<QWebEngineRegisterProtocolHandlerRequest>(); + qRegisterMetaType<QWebEngineFindTextResult>(); // See setVisible(). wasShownTimer.setSingleShot(true); @@ -420,11 +423,6 @@ void QWebEnginePagePrivate::didFetchDocumentInnerText(quint64 requestId, const Q m_callbacks.invoke(requestId, result); } -void QWebEnginePagePrivate::didFindText(quint64 requestId, int matchCount) -{ - m_callbacks.invoke(requestId, matchCount > 0); -} - void QWebEnginePagePrivate::didPrintPage(quint64 requestId, QSharedPointer<QByteArray> result) { #if QT_CONFIG(webengine_printing_and_pdf) @@ -702,6 +700,12 @@ void QWebEnginePagePrivate::widgetChanged(RenderWidgetHostViewQtDelegate *newWid bindPageAndWidget(q, static_cast<RenderWidgetHostViewQtDelegateWidget *>(newWidgetBase)); } +void QWebEnginePagePrivate::findTextFinished(const QWebEngineFindTextResult &result) +{ + Q_Q(QWebEnginePage); + Q_EMIT q->findTextFinished(result); +} + void QWebEnginePagePrivate::ensureInitialized() const { if (!adapter->isInitialized()) @@ -802,6 +806,16 @@ QWebEnginePage::QWebEnginePage(QObject* parent) } /*! + \fn void QWebEnginePage::findTextFinished(const QWebEngineFindTextResult &result) + \since 5.14 + + This signal is emitted when a search string search on a page is completed. \a result is + the result of the string search. + + \sa findText() +*/ + +/*! \fn void QWebEnginePage::printRequested() \since 5.12 @@ -963,7 +977,6 @@ QWebEnginePage::~QWebEnginePage() if (d_ptr) { // d_ptr might be exceptionally null if profile adapter got deleted first setDevToolsPage(nullptr); - d_ptr->adapter->stopFinding(); QWebEnginePagePrivate::bindPageAndView(this, nullptr); QWebEnginePagePrivate::bindPageAndWidget(this, nullptr); } @@ -1592,16 +1605,11 @@ void QWebEnginePage::findText(const QString &subString, FindFlags options, const { Q_D(QWebEnginePage); if (!d->adapter->isInitialized()) { - d->m_callbacks.invokeEmpty(resultCallback); + QtWebEngineCore::CallbackDirectory().invokeEmpty(resultCallback); return; } - if (subString.isEmpty()) { - d->adapter->stopFinding(); - d->m_callbacks.invokeEmpty(resultCallback); - } else { - quint64 requestId = d->adapter->findText(subString, options & FindCaseSensitively, options & FindBackward); - d->m_callbacks.registerCallback(requestId, resultCallback); - } + + d->adapter->findTextHelper()->startFinding(subString, options & FindCaseSensitively, options & FindBackward, resultCallback); } /*! @@ -1652,8 +1660,8 @@ void QWebEnginePagePrivate::navigationRequested(int navigationType, const QUrl & { Q_Q(QWebEnginePage); bool accepted = q->acceptNavigationRequest(url, static_cast<QWebEnginePage::NavigationType>(navigationType), isMainFrame); - if (accepted && adapter->isFindTextInProgress()) - adapter->stopFinding(); + if (accepted && adapter->findTextHelper()->isFindTextInProgress()) + adapter->findTextHelper()->stopFinding(); navigationRequestAction = accepted ? WebContentsAdapterClient::AcceptRequest : WebContentsAdapterClient::IgnoreRequest; } diff --git a/src/webenginewidgets/api/qwebenginepage.h b/src/webenginewidgets/api/qwebenginepage.h index 736d7ed69..cd012ea70 100644 --- a/src/webenginewidgets/api/qwebenginepage.h +++ b/src/webenginewidgets/api/qwebenginepage.h @@ -62,6 +62,7 @@ class QWebChannel; class QWebEngineCertificateError; class QWebEngineClientCertificateSelection; class QWebEngineContextMenuData; +class QWebEngineFindTextResult; class QWebEngineFullScreenRequest; class QWebEngineHistory; class QWebEnginePage; @@ -369,6 +370,8 @@ Q_SIGNALS: void lifecycleStateChanged(LifecycleState state); void recommendedStateChanged(LifecycleState state); + void findTextFinished(const QWebEngineFindTextResult &result); + protected: virtual QWebEnginePage *createWindow(WebWindowType type); virtual QStringList chooseFiles(FileSelectionMode mode, const QStringList &oldFiles, const QStringList &acceptedMimeTypes); diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h index acf95a265..fae97b9fa 100644 --- a/src/webenginewidgets/api/qwebenginepage_p.h +++ b/src/webenginewidgets/api/qwebenginepage_p.h @@ -72,6 +72,7 @@ class WebContentsAdapter; } QT_BEGIN_NAMESPACE +class QWebEngineFindTextResult; class QWebEngineHistory; class QWebEnginePage; class QWebEngineProfile; @@ -127,7 +128,6 @@ public: void didRunJavaScript(quint64 requestId, const QVariant& result) override; 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, QSharedPointer<QByteArray> result) override; void didPrintPageToPdf(const QString &filePath, bool success) override; bool passOnFocus(bool reverse) override; @@ -163,6 +163,7 @@ public: ClientType clientType() override { return QtWebEngineCore::WebContentsAdapterClient::WidgetsClient; } void interceptRequest(QWebEngineUrlRequestInfo &) override; void widgetChanged(QtWebEngineCore::RenderWidgetHostViewQtDelegate *newWidget) override; + void findTextFinished(const QWebEngineFindTextResult &result) override; QtWebEngineCore::ProfileAdapter *profileAdapter() override; QtWebEngineCore::WebContentsAdapter *webContentsAdapter() override; diff --git a/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc b/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc index 99bbc5162..64fe4c9cd 100644 --- a/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc +++ b/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc @@ -489,6 +489,7 @@ /*! \fn void QWebEnginePage::findText(const QString &subString, QWebEnginePage::FindFlags options = FindFlags(), const QWebEngineCallback<bool> &resultCallback = QWebEngineCallback<bool>()) Finds the specified string, \a subString, in the page, using the given \a options. + The findTextFinished() signal is emitted when a string search is completed. To clear the search highlight, just pass an empty string. @@ -501,6 +502,8 @@ For example: \snippet qtwebengine_qwebenginepage_snippet.cpp 0 + + \sa findTextFinished() */ /*! diff --git a/tests/auto/quick/publicapi/tst_publicapi.cpp b/tests/auto/quick/publicapi/tst_publicapi.cpp index f464d58d0..9f7dfa8ad 100644 --- a/tests/auto/quick/publicapi/tst_publicapi.cpp +++ b/tests/auto/quick/publicapi/tst_publicapi.cpp @@ -35,6 +35,7 @@ #include <QtTest/QtTest> #include <QtWebEngine/QQuickWebEngineProfile> #include <QtWebEngine/QQuickWebEngineScript> +#include <QtWebEngineCore/QWebEngineFindTextResult> #include <QtWebEngineCore/QWebEngineNotification> #include <QtWebEngineCore/QWebEngineQuotaRequest> #include <QtWebEngineCore/QWebEngineRegisterProtocolHandlerRequest> @@ -85,6 +86,7 @@ static const QList<const QMetaObject *> typesToCheck = QList<const QMetaObject * << &QWebEngineQuotaRequest::staticMetaObject << &QWebEngineRegisterProtocolHandlerRequest::staticMetaObject << &QWebEngineNotification::staticMetaObject + << &QWebEngineFindTextResult::staticMetaObject ; static QList<const char *> knownEnumNames = QList<const char *>(); @@ -276,6 +278,8 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineFileDialogRequest.dialogAccept(QStringList) --> void" << "QQuickWebEngineFileDialogRequest.dialogReject() --> void" << "QQuickWebEngineFileDialogRequest.mode --> FileMode" + << "QWebEngineFindTextResult.numberOfMatches --> int" + << "QWebEngineFindTextResult.activeMatchOrdinal --> int" << "QQuickWebEngineFormValidationMessageRequest.Hide --> RequestType" << "QQuickWebEngineFormValidationMessageRequest.Move --> RequestType" << "QQuickWebEngineFormValidationMessageRequest.Show --> RequestType" @@ -694,6 +698,7 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.findText(QString) --> void" << "QQuickWebEngineView.findText(QString,FindFlags) --> void" << "QQuickWebEngineView.findText(QString,FindFlags,QJSValue) --> void" + << "QQuickWebEngineView.findTextFinished(QWebEngineFindTextResult) --> void" << "QQuickWebEngineView.formValidationMessageRequested(QQuickWebEngineFormValidationMessageRequest*) --> void" << "QQuickWebEngineView.fullScreenCancelled() --> void" << "QQuickWebEngineView.fullScreenRequested(QQuickWebEngineFullScreenRequest) --> void" diff --git a/tests/auto/quick/qmltests/data/tst_findText.qml b/tests/auto/quick/qmltests/data/tst_findText.qml index 14053a675..040d324e6 100644 --- a/tests/auto/quick/qmltests/data/tst_findText.qml +++ b/tests/auto/quick/qmltests/data/tst_findText.qml @@ -38,9 +38,16 @@ TestWebEngineView { property int matchCount: 0 property bool findFailed: false + SignalSpy { + id: findTextSpy + target: webEngineView + signalName: "findTextFinished" + } + function clear() { findFailed = false matchCount = -1 + findTextSpy.clear() } function findCallbackCalled() { return matchCount != -1 } @@ -104,6 +111,9 @@ TestWebEngineView { webEngineView.findText("Hello", findFlags, webEngineView.findTextCallback) tryCompare(webEngineView, "matchCount", 1) verify(!findFailed) + tryCompare(findTextSpy, "count", 1) + compare(findTextSpy.signalArguments[0][0].numberOfMatches, 1) + compare(findTextSpy.signalArguments[0][0].activeMatchOrdinal, 1) } function test_findTextCaseInsensitive() { @@ -115,6 +125,9 @@ TestWebEngineView { webEngineView.findText("heLLo", findFlags, webEngineView.findTextCallback) tryCompare(webEngineView, "matchCount", 1) verify(!findFailed) + tryCompare(findTextSpy, "count", 1) + compare(findTextSpy.signalArguments[0][0].numberOfMatches, 1) + compare(findTextSpy.signalArguments[0][0].activeMatchOrdinal, 1) } function test_findTextManyMatches() { @@ -126,6 +139,9 @@ TestWebEngineView { webEngineView.findText("bla", findFlags, webEngineView.findTextCallback) tryCompare(webEngineView, "matchCount", 100, 20000) verify(!findFailed) + tryCompare(findTextSpy, "count", 1) + compare(findTextSpy.signalArguments[0][0].numberOfMatches, 100) + compare(findTextSpy.signalArguments[0][0].activeMatchOrdinal, 1) } @@ -138,6 +154,9 @@ TestWebEngineView { webEngineView.findText("heLLo", findFlags, webEngineView.findTextCallback) tryCompare(webEngineView, "matchCount", 0) verify(findFailed) + tryCompare(findTextSpy, "count", 1) + compare(findTextSpy.signalArguments[0][0].numberOfMatches, 0) + compare(findTextSpy.signalArguments[0][0].activeMatchOrdinal, 0) } function test_findTextNotFound() { @@ -149,6 +168,9 @@ TestWebEngineView { webEngineView.findText("string-that-is-not-threre", findFlags, webEngineView.findTextCallback) tryCompare(webEngineView, "matchCount", 0) verify(findFailed) + tryCompare(findTextSpy, "count", 1) + compare(findTextSpy.signalArguments[0][0].numberOfMatches, 0) + compare(findTextSpy.signalArguments[0][0].activeMatchOrdinal, 0) } function test_findTextAfterNotFound() { @@ -160,6 +182,9 @@ TestWebEngineView { webEngineView.findText("hello", findFlags, webEngineView.findTextCallback) tryCompare(webEngineView, "matchCount", 0) verify(findFailed) + tryCompare(findTextSpy, "count", 1) + compare(findTextSpy.signalArguments[0][0].numberOfMatches, 0) + compare(findTextSpy.signalArguments[0][0].activeMatchOrdinal, 0) webEngineView.url = Qt.resolvedUrl("test1.html") verify(webEngineView.waitForLoadSucceeded()) @@ -168,6 +193,9 @@ TestWebEngineView { webEngineView.findText("hello", findFlags, webEngineView.findTextCallback) tryCompare(webEngineView, "matchCount", 1) verify(!findFailed) + tryCompare(findTextSpy, "count", 1) + compare(findTextSpy.signalArguments[0][0].numberOfMatches, 1) + compare(findTextSpy.signalArguments[0][0].activeMatchOrdinal, 1) } function test_findTextInModifiedDOMAfterNotFound() { @@ -182,6 +210,9 @@ TestWebEngineView { webEngineView.findText("hello", findFlags, webEngineView.findTextCallback) tryCompare(webEngineView, "matchCount", 0, 20000) verify(findFailed) + tryCompare(findTextSpy, "count", 1) + compare(findTextSpy.signalArguments[0][0].numberOfMatches, 0) + compare(findTextSpy.signalArguments[0][0].activeMatchOrdinal, 0) runJavaScript("document.body.innerHTML = 'blahellobla'"); tryVerify(function() { return getBodyInnerHTML() == "blahellobla"; }, 20000); @@ -190,6 +221,9 @@ TestWebEngineView { webEngineView.findText("hello", findFlags, webEngineView.findTextCallback) tryCompare(webEngineView, "matchCount", 1) verify(!findFailed) + tryCompare(findTextSpy, "count", 1) + compare(findTextSpy.signalArguments[0][0].numberOfMatches, 1) + compare(findTextSpy.signalArguments[0][0].activeMatchOrdinal, 1) } function test_findTextInterruptedByLoad() { @@ -227,5 +261,55 @@ TestWebEngineView { webEngineView.findText('New page', findFlags, webEngineView.findTextCallback) tryCompare(webEngineView, 'matchCount', 1) } + + function test_findTextActiveMatchOrdinal() { + webEngineView.loadHtml( + "<html><body>" + + "foo bar foo bar foo" + + "</body></html>"); + verify(webEngineView.waitForLoadSucceeded()); + + // Iterate over all "foo" matches. + webEngineView.clear(); + for (var i = 1; i <= 3; ++i) { + webEngineView.findText("foo"); + findTextSpy.wait(); + compare(findTextSpy.count, i); + compare(findTextSpy.signalArguments[i-1][0].numberOfMatches, 3); + compare(findTextSpy.signalArguments[i-1][0].activeMatchOrdinal, i); + } + + // The last match is followed by the fist one. + webEngineView.clear(); + webEngineView.findText("foo"); + findTextSpy.wait(); + compare(findTextSpy.count, 1); + compare(findTextSpy.signalArguments[0][0].numberOfMatches, 3); + compare(findTextSpy.signalArguments[0][0].activeMatchOrdinal, 1); + + // The first match is preceded by the last one. + webEngineView.clear(); + webEngineView.findText("foo", WebEngineView.FindBackward); + findTextSpy.wait(); + compare(findTextSpy.count, 1); + compare(findTextSpy.signalArguments[0][0].numberOfMatches, 3); + compare(findTextSpy.signalArguments[0][0].activeMatchOrdinal, 3); + + // Finding another word resets the activeMatchOrdinal. + webEngineView.clear(); + webEngineView.findText("bar"); + findTextSpy.wait(); + compare(findTextSpy.count, 1); + compare(findTextSpy.signalArguments[0][0].numberOfMatches, 2); + compare(findTextSpy.signalArguments[0][0].activeMatchOrdinal, 1); + + // If no match activeMatchOrdinal is 0. + webEngineView.clear(); + webEngineView.findText("bla"); + findTextSpy.wait(); + compare(findTextSpy.count, 1); + compare(findTextSpy.signalArguments[0][0].numberOfMatches, 0); + compare(findTextSpy.signalArguments[0][0].activeMatchOrdinal, 0); + } } } diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index 713feca6d..dbb15ba10 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -47,6 +47,7 @@ #include <qnetworkreply.h> #include <qnetworkrequest.h> #include <qwebenginedownloaditem.h> +#include <qwebenginefindtextresult.h> #include <qwebenginefullscreenrequest.h> #include <qwebenginehistory.h> #include <qwebenginenotification.h> @@ -127,6 +128,8 @@ private Q_SLOTS: void findText(); void findTextResult(); void findTextSuccessiveShouldCallAllCallbacks(); + void findTextCalledOnMatch(); + void findTextActiveMatchOrdinal(); void deleteQWebEngineViewTwice(); void loadSignalsOrder_data(); void loadSignalsOrder(); @@ -941,18 +944,24 @@ void tst_QWebEnginePage::findText() // Invoking a stopFinding operation will not change or clear the currently selected text, // if nothing was found beforehand. { - CallbackSpy<bool> spy; - m_view->findText("", 0, spy.ref()); - QVERIFY(spy.wasCalled()); + CallbackSpy<bool> callbackSpy; + QSignalSpy signalSpy(m_view->page(), &QWebEnginePage::findTextFinished); + m_view->findText("", 0, callbackSpy.ref()); + QVERIFY(callbackSpy.wasCalled()); + QCOMPARE(signalSpy.count(), 1); QTRY_COMPARE(m_view->selectedText(), QString("foo bar")); } // Invoking a startFinding operation with text that won't be found, will clear the current // selection. { - CallbackSpy<bool> spy; - m_view->findText("Will not be found", 0, spy.ref()); - QCOMPARE(spy.waitForResult(), false); + CallbackSpy<bool> callbackSpy; + QSignalSpy signalSpy(m_view->page(), &QWebEnginePage::findTextFinished); + m_view->findText("Will not be found", 0, callbackSpy.ref()); + QCOMPARE(callbackSpy.waitForResult(), false); + QTRY_COMPARE(signalSpy.count(), 1); + auto result = signalSpy.takeFirst().value(0).value<QWebEngineFindTextResult>(); + QCOMPARE(result.numberOfMatches(), 0); QTRY_VERIFY(m_view->selectedText().isEmpty()); } @@ -963,24 +972,36 @@ void tst_QWebEnginePage::findText() // Invoking a startFinding operation with text that will be found, will clear the current // selection as well. { - CallbackSpy<bool> spy; - m_view->findText("foo", 0, spy.ref()); - QVERIFY(spy.waitForResult()); + CallbackSpy<bool> callbackSpy; + QSignalSpy signalSpy(m_view->page(), &QWebEnginePage::findTextFinished); + m_view->findText("foo", 0, callbackSpy.ref()); + QVERIFY(callbackSpy.waitForResult()); + QTRY_COMPARE(signalSpy.count(), 1); QTRY_VERIFY(m_view->selectedText().isEmpty()); } // Invoking a stopFinding operation after text was found, will set the selected text to the // found text. { - CallbackSpy<bool> spy; - m_view->findText("", 0, spy.ref()); - QTRY_VERIFY(spy.wasCalled()); + CallbackSpy<bool> callbackSpy; + QSignalSpy signalSpy(m_view->page(), &QWebEnginePage::findTextFinished); + m_view->findText("", 0, callbackSpy.ref()); + QTRY_VERIFY(callbackSpy.wasCalled()); + QTRY_COMPARE(signalSpy.count(), 1); QTRY_COMPARE(m_view->selectedText(), QString("foo")); } } void tst_QWebEnginePage::findTextResult() { + QSignalSpy findTextSpy(m_view->page(), &QWebEnginePage::findTextFinished); + auto signalResult = [&findTextSpy]() -> QVector<int> { + if (findTextSpy.count() != 1) + return QVector<int>({-1, -1}); + auto r = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>(); + return QVector<int>({ r.numberOfMatches(), r.activeMatchOrdinal() }); + }; + // findText will abort in blink if the view has an empty size. m_view->resize(800, 600); m_view->show(); @@ -990,15 +1011,21 @@ void tst_QWebEnginePage::findTextResult() QTRY_COMPARE(loadSpy.count(), 1); QCOMPARE(findTextSync(m_page, ""), false); + QCOMPARE(signalResult(), QVector<int>({0, 0})); const QStringList words = { "foo", "bar" }; for (const QString &subString : words) { QCOMPARE(findTextSync(m_page, subString), true); + QCOMPARE(signalResult(), QVector<int>({1, 1})); + QCOMPARE(findTextSync(m_page, ""), false); + QCOMPARE(signalResult(), QVector<int>({0, 0})); } QCOMPARE(findTextSync(m_page, "blahhh"), false); + QCOMPARE(signalResult(), QVector<int>({0, 0})); QCOMPARE(findTextSync(m_page, ""), false); + QCOMPARE(signalResult(), QVector<int>({0, 0})); } void tst_QWebEnginePage::findTextSuccessiveShouldCallAllCallbacks() @@ -1024,6 +1051,91 @@ void tst_QWebEnginePage::findTextSuccessiveShouldCallAllCallbacks() QVERIFY(spy5.wasCalled()); } +void tst_QWebEnginePage::findTextCalledOnMatch() +{ + QSignalSpy loadSpy(m_view->page(), &QWebEnginePage::loadFinished); + + // findText will abort in blink if the view has an empty size. + m_view->resize(800, 600); + m_view->show(); + m_view->setHtml(QString("<html><head></head><body><div>foo bar</div></body></html>")); + QTRY_COMPARE(loadSpy.count(), 1); + + // CALLBACK + bool callbackCalled = false; + m_view->page()->findText("foo", 0, [this, &callbackCalled](bool found) { + QVERIFY(found); + + m_view->page()->findText("bar", 0, [&callbackCalled](bool found) { + QVERIFY(found); + callbackCalled = true; + }); + }); + QTRY_VERIFY(callbackCalled); + + // SIGNAL + int findTextFinishedCount = 0; + connect(m_view->page(), &QWebEnginePage::findTextFinished, [this, &findTextFinishedCount](QWebEngineFindTextResult result) { + QCOMPARE(result.numberOfMatches(), 1); + if (findTextFinishedCount == 0) + m_view->page()->findText("bar"); + findTextFinishedCount++; + }); + + m_view->page()->findText("foo"); + QTRY_COMPARE(findTextFinishedCount, 2); +} + +void tst_QWebEnginePage::findTextActiveMatchOrdinal() +{ + QSignalSpy loadSpy(m_view->page(), &QWebEnginePage::loadFinished); + QSignalSpy findTextSpy(m_view->page(), &QWebEnginePage::findTextFinished); + QWebEngineFindTextResult result; + + // findText will abort in blink if the view has an empty size. + m_view->resize(800, 600); + m_view->show(); + m_view->setHtml(QString("<html><head></head><body><div>foo bar foo bar foo</div></body></html>")); + QTRY_COMPARE(loadSpy.count(), 1); + + // Iterate over all "foo" matches. + for (int i = 1; i <= 3; ++i) { + m_view->page()->findText("foo", 0); + QTRY_COMPARE(findTextSpy.count(), 1); + result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>(); + QCOMPARE(result.numberOfMatches(), 3); + QCOMPARE(result.activeMatchOrdinal(), i); + } + + // The last match is followed by the fist one. + m_view->page()->findText("foo", 0); + QTRY_COMPARE(findTextSpy.count(), 1); + result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>(); + QCOMPARE(result.numberOfMatches(), 3); + QCOMPARE(result.activeMatchOrdinal(), 1); + + // The first match is preceded by the last one. + m_view->page()->findText("foo", QWebEnginePage::FindBackward); + QTRY_COMPARE(findTextSpy.count(), 1); + result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>(); + QCOMPARE(result.numberOfMatches(), 3); + QCOMPARE(result.activeMatchOrdinal(), 3); + + // Finding another word resets the activeMatchOrdinal. + m_view->page()->findText("bar", 0); + QTRY_COMPARE(findTextSpy.count(), 1); + result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>(); + QCOMPARE(result.numberOfMatches(), 2); + QCOMPARE(result.activeMatchOrdinal(), 1); + + // If no match activeMatchOrdinal is 0. + m_view->page()->findText("bla", 0); + QTRY_COMPARE(findTextSpy.count(), 1); + result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>(); + QCOMPARE(result.numberOfMatches(), 0); + QCOMPARE(result.activeMatchOrdinal(), 0); +} + static QWindow *findNewTopLevelWindow(const QWindowList &oldTopLevelWindows) { const auto tlws = QGuiApplication::topLevelWindows(); |